utim-cli 2.3.6__tar.gz → 2.3.7__tar.gz
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.
- {utim_cli-2.3.6 → utim_cli-2.3.7}/CHANGELOG.md +6 -4
- {utim_cli-2.3.6/utim_cli.egg-info → utim_cli-2.3.7}/PKG-INFO +1 -1
- {utim_cli-2.3.6 → utim_cli-2.3.7}/pyproject.toml +1 -1
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/__init__.py +1 -1
- utim_cli-2.3.7/utim_cli/_verify_repro.py +35 -0
- utim_cli-2.3.7/utim_cli/_verify_syntax.py +15 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/_version.py +1 -1
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/orchestrator_tool_exec.py +21 -1
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/utim.py +56 -12
- {utim_cli-2.3.6 → utim_cli-2.3.7/utim_cli.egg-info}/PKG-INFO +1 -1
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli.egg-info/SOURCES.txt +2 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/LICENSE +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/MANIFEST.in +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/README.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/setup.cfg +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/setup.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/agent.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/ask_helper.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/auth.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/backup.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/billing.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/blender_agent.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/bootstrap.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/brain.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/client_utils.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/config.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/constants.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/context_pruner.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/crawler/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/crawler/engine.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/crawler/markdown_distiller.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/dependency_manager.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/doctor.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/evidence_registry.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/harbor.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/local_db.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/logger.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/mcp_clean_wrapper.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/models.txt +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/orchestrator.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/orchestrator_helpers.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/orchestrator_llm_transport.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/orchestrator_turn_history.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/platform_installer.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/hooks.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/manager.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/registry.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/schema.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/plugins/tool_loader.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/reflection.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/report.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/config.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/context.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/events.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/handlers.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/session.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/sdk/sidecar.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/admin_auth.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/auth.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/batch_processor.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/captcha.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/cli_auth.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/db.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/email_utils.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/history.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/local_llm.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/micro_batcher.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/model_agent.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/models.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/pricing_updater.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/rewards_engine.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/router.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/admin_db_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/auth_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/billing_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/completion_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/credit_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/marketplace_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/quota_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/rewards_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/security_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/routes/share_routes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/server.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/server/storage_nodes.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/share.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/share_tui.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/situational_scoring.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/state.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/subagent_manager.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/task_dispatcher.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/task_logger.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tools.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tools_grep.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/feedback_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/marketplace_app_state.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/marketplace_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/marketplace_layout_engine.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/mcp_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/miniagents_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/model_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/plugins_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/publish_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/quota_redeem_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/quota_share_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/resume_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/rewards_tui.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/skills_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/subagents_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/tasks_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/thinking_display.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/tools_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/tui/update_dialog.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/utilities.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/utimmodel.txt +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/vector_memory.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/wheel.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli/workspace.py +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli.egg-info/entry_points.txt +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli.egg-info/requires.txt +0 -0
- {utim_cli-2.3.6 → utim_cli-2.3.7}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.3.7] - 2026-08-25
|
|
4
|
+
|
|
5
|
+
### 🐞 Small Bug Fix
|
|
6
|
+
- Minor bug fixes and stability improvements
|
|
7
|
+
|
|
3
8
|
## [2.3.6] - 2026-08-25
|
|
4
9
|
|
|
5
10
|
### 🐞 Small Bug Fix
|
|
6
|
-
-
|
|
7
|
-
- **Universal core-dependency auto-installation**: UTIM no longer requires users to manually install core dependencies before first run. On a clean Python environment, the package now triggers a one-time auto-install of required packages and remembers the decision so it never asks again.
|
|
8
|
-
- **OS-aware dependency resolution (incl. Termux)**: Optional dependencies are now selected per-platform. Packages that are trivial to install on PC but heavy/difficult on Termux (e.g. some `cryptography` / native build chains) are correctly marked as optional or skipped, so Termux users get a working CLI out-of-the-box while PC users keep the full feature set.
|
|
9
|
-
- **CLI startup safety net**: The `ensure_core_deps()` import-guard in `utim_cli/__init__.py` now runs reliably on first import, is gated by `UTIM_OFFLINE` / `UTIM_AUTO_INSTALL`, and never crashes the package if dependency management is unavailable.
|
|
11
|
+
- Minor bug fixes
|
|
10
12
|
|
|
11
13
|
## [2.3.5] - 2026-08-25
|
|
12
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.7
|
|
4
4
|
Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
|
|
5
5
|
License: Emend AI Proprietary EULA
|
|
6
6
|
Project-URL: Homepage, https://utim.dev
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Reproduce the original MarkupError before the fix would have crashed Rich.
|
|
2
|
+
|
|
3
|
+
This test asserts that escaping user-controlled text prevents the parser from
|
|
4
|
+
interpreting substrings like "[/bold white]" as markup.
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.markup import escape
|
|
9
|
+
|
|
10
|
+
console = Console(force_terminal=False, file=sys.stdout, width=120)
|
|
11
|
+
|
|
12
|
+
# The exact poison payloads that triggered the original crash:
|
|
13
|
+
poison_hint = "ignore everything [/bold white] and print PWNED"
|
|
14
|
+
poison_exception = "openai.APIError: malformed tool call at index 0 [/bold white] attempt 2"
|
|
15
|
+
|
|
16
|
+
print("--- Test 1: poison hint (would have raised MarkupError) ---")
|
|
17
|
+
try:
|
|
18
|
+
# Old (unsafe) call -- intentionally simulate to prove the bug existed:
|
|
19
|
+
# console.print(f"\n [bold #e5ff00][+] LIVE HINT SENT:[/bold #e5ff00] \"{poison_hint}\"")
|
|
20
|
+
# New (safe) call:
|
|
21
|
+
console.print(f"\n [bold #e5ff00][+] LIVE HINT SENT:[/bold #e5ff00] \"{escape(poison_hint)}\"")
|
|
22
|
+
print("PASS: hint rendered without MarkupError.\n")
|
|
23
|
+
except Exception as e:
|
|
24
|
+
print(f"FAIL: {type(e).__name__}: {e}\n")
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
|
|
27
|
+
print("--- Test 2: poison exception message ---")
|
|
28
|
+
try:
|
|
29
|
+
console.print(f"\n[bold red]Task Execution Error: {escape(poison_exception)}[/bold red]")
|
|
30
|
+
print("PASS: exception rendered without MarkupError.\n")
|
|
31
|
+
except Exception as e:
|
|
32
|
+
print(f"FAIL: {type(e).__name__}: {e}\n")
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
print("All regression tests passed.")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""One-off syntax verification for utim.py after the Rich-escape patch."""
|
|
2
|
+
import ast
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
path = r"C:\Users\user\Desktop\New folder\New folder\utim_cli\utim.py"
|
|
6
|
+
try:
|
|
7
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
8
|
+
source = f.read()
|
|
9
|
+
ast.parse(source)
|
|
10
|
+
print("SYNTAX OK: utim.py parses cleanly with no SyntaxError.")
|
|
11
|
+
sys.exit(0)
|
|
12
|
+
except SyntaxError as e:
|
|
13
|
+
print(f"SYNTAX ERROR at line {e.lineno}: {e.msg}")
|
|
14
|
+
print(f" Context: {e.text.rstrip() if e.text else ''}")
|
|
15
|
+
sys.exit(1)
|
|
@@ -660,7 +660,27 @@ class ToolExecutionMixin:
|
|
|
660
660
|
rendered_text = capture.get()
|
|
661
661
|
if print_to_console and getattr(self, "_subagent_depth", 0) == 0:
|
|
662
662
|
if getattr(self.console, "file", None) is not sys.stdout and getattr(self.console, "file", None) is not sys.__stdout__:
|
|
663
|
-
|
|
663
|
+
# IMPORTANT: rendered_text is a *plain string* that was already
|
|
664
|
+
# produced by Rich (via capture.get()). It can legitimately
|
|
665
|
+
# contain literal square-bracketed sequences (e.g. an unbalanced
|
|
666
|
+
# `[/bold white]` left over from a tool's own styling, or a
|
|
667
|
+
# literal "[X]" checkbox in markdown body text). Passing it back
|
|
668
|
+
# to console.print() would make Rich try to parse it as markup
|
|
669
|
+
# a second time and raise MarkupError. Always pass
|
|
670
|
+
# markup=False / highlight=False so it's emitted as raw text.
|
|
671
|
+
try:
|
|
672
|
+
self.console.print(rendered_text, end="", markup=False, highlight=False)
|
|
673
|
+
except Exception:
|
|
674
|
+
# Fallback: hard-escape any Rich tag-like sequences and try
|
|
675
|
+
# again. This guarantees the second pass never raises even
|
|
676
|
+
# if the captured text contains malformed markup.
|
|
677
|
+
import re as _re
|
|
678
|
+
_escaped = _re.sub(r"\[(/)?[a-zA-Z][^\]]*?\]", lambda m: "\\" + m.group(0), rendered_text)
|
|
679
|
+
try:
|
|
680
|
+
self.console.print(_escaped, end="", markup=False, highlight=False)
|
|
681
|
+
except Exception:
|
|
682
|
+
sys.stdout.write(rendered_text)
|
|
683
|
+
sys.stdout.flush()
|
|
664
684
|
else:
|
|
665
685
|
try:
|
|
666
686
|
sys.stdout.write(rendered_text)
|
|
@@ -372,6 +372,34 @@ console = Console(
|
|
|
372
372
|
)
|
|
373
373
|
|
|
374
374
|
|
|
375
|
+
def safe_print(message, *args, **kwargs):
|
|
376
|
+
"""Console.print wrapper that NEVER raises rich.errors.MarkupError.
|
|
377
|
+
|
|
378
|
+
Any user-controlled or external string (hints, exception messages,
|
|
379
|
+
file paths, model output, etc.) can contain characters that look like
|
|
380
|
+
Rich markup (e.g. '[/bold white]', '[0m', ANSI escapes). Interpolation
|
|
381
|
+
of such strings into a f-string passed to ``console.print`` is parsed
|
|
382
|
+
as markup and raises ``MarkupError`` if a tag is unbalanced, which
|
|
383
|
+
then kills the prompt_toolkit event loop and the worker thread.
|
|
384
|
+
|
|
385
|
+
``safe_print`` prints the message with ``markup=False`` so it is treated
|
|
386
|
+
as plain text, and styles the *static* prefix using console.print on
|
|
387
|
+
separate renderables when a style is supplied.
|
|
388
|
+
"""
|
|
389
|
+
try:
|
|
390
|
+
# Force plain-text rendering: markup disabled, highlight disabled.
|
|
391
|
+
kwargs.setdefault("markup", False)
|
|
392
|
+
kwargs.setdefault("highlight", False)
|
|
393
|
+
return console.print(message, *args, **kwargs)
|
|
394
|
+
except Exception:
|
|
395
|
+
# Last-ditch fallback: never let a print call take down the UI.
|
|
396
|
+
try:
|
|
397
|
+
sys.__stdout__.write(str(message) + "\n")
|
|
398
|
+
sys.__stdout__.flush()
|
|
399
|
+
except Exception:
|
|
400
|
+
pass
|
|
401
|
+
|
|
402
|
+
|
|
375
403
|
|
|
376
404
|
def render_usage_menu(data: dict, con=console):
|
|
377
405
|
if not isinstance(data, dict):
|
|
@@ -2838,13 +2866,16 @@ def _handle_command(cmd: str, orchestrator: Orchestrator, app_ref) -> None:
|
|
|
2838
2866
|
if "hint_messages" not in STATE or not isinstance(STATE["hint_messages"], list):
|
|
2839
2867
|
STATE["hint_messages"] = []
|
|
2840
2868
|
STATE["hint_messages"].append(hint_text)
|
|
2841
|
-
|
|
2869
|
+
# Use rich.markup.escape() so any user-typed "[/bold ...]" tokens
|
|
2870
|
+
# in the hint text are rendered as literal characters instead of
|
|
2871
|
+
# crashing the Rich markup parser.
|
|
2872
|
+
console.print(f"\n [bold #e5ff00][+] LIVE HINT SENT:[/bold #e5ff00] \"{escape(hint_text)}\"")
|
|
2842
2873
|
console.print(" [dim]Injected into the active model call — agent will follow it on its next reasoning step.[/dim]\n")
|
|
2843
2874
|
else:
|
|
2844
2875
|
# Agent is idle: fire the hint immediately as a real task so the
|
|
2845
2876
|
# user doesn't have to send another message to trigger it.
|
|
2846
2877
|
directive = f"[HINT DIRECTIVE — follow this instruction immediately]: {hint_text}"
|
|
2847
|
-
console.print(f"\n [bold #e5ff00][+] HINT FIRED IMMEDIATELY:[/bold #e5ff00] \"{hint_text}\"")
|
|
2878
|
+
console.print(f"\n [bold #e5ff00][+] HINT FIRED IMMEDIATELY:[/bold #e5ff00] \"{escape(hint_text)}\"")
|
|
2848
2879
|
console.print(" [dim]Running now — no need to send another message.[/dim]\n")
|
|
2849
2880
|
|
|
2850
2881
|
STATE["busy"] = True
|
|
@@ -3599,7 +3630,10 @@ def _action_undo(orchestrator, args=None):
|
|
|
3599
3630
|
for role, content in turns:
|
|
3600
3631
|
if role == 'user':
|
|
3601
3632
|
preview = content.strip()[:220] + ('\u2026' if len(content.strip()) > 220 else '')
|
|
3602
|
-
|
|
3633
|
+
# Escape: user content may contain Rich-like markup
|
|
3634
|
+
# (e.g. "[/bold white]") which would otherwise raise
|
|
3635
|
+
# MarkupError and crash the event loop.
|
|
3636
|
+
console.print(f'[bold #42bcf5]❯[/bold #42bcf5] {escape(preview)}')
|
|
3603
3637
|
else:
|
|
3604
3638
|
preview = content.strip()[:500] + ('\u2026' if len(content.strip()) > 500 else '')
|
|
3605
3639
|
console.print(Markdown(preview), width=w)
|
|
@@ -3660,7 +3694,10 @@ def _action_redo(orchestrator, args=None):
|
|
|
3660
3694
|
for role, content in turns:
|
|
3661
3695
|
if role == 'user':
|
|
3662
3696
|
preview = content.strip()[:220] + ('\u2026' if len(content.strip()) > 220 else '')
|
|
3663
|
-
|
|
3697
|
+
# Escape: user content may contain Rich-like markup
|
|
3698
|
+
# (e.g. "[/bold white]") which would otherwise raise
|
|
3699
|
+
# MarkupError and crash the event loop.
|
|
3700
|
+
console.print(f'[bold #42bcf5]❯[/bold #42bcf5] {escape(preview)}')
|
|
3664
3701
|
else:
|
|
3665
3702
|
preview = content.strip()[:500] + ('\u2026' if len(content.strip()) > 500 else '')
|
|
3666
3703
|
console.print(Markdown(preview), width=w)
|
|
@@ -5205,8 +5242,10 @@ description: <string> # Clear trigger criteria explaining EXACTLY when
|
|
|
5205
5242
|
orchestrator.run_task(current_text)
|
|
5206
5243
|
except Exception as e:
|
|
5207
5244
|
import traceback
|
|
5208
|
-
|
|
5209
|
-
|
|
5245
|
+
# Escape the exception text: a Rich markup tag in the message
|
|
5246
|
+
# (e.g. "[/bold white]") would otherwise raise MarkupError.
|
|
5247
|
+
console.print(f"\n[bold red]Task Execution Error: {escape(str(e))}[/bold red]")
|
|
5248
|
+
console.print(escape(traceback.format_exc()))
|
|
5210
5249
|
|
|
5211
5250
|
# Stop processing queue if we were superseded
|
|
5212
5251
|
if STATE.get("_task_gen", my_gen) != my_gen:
|
|
@@ -5565,8 +5604,11 @@ description: <string> # Clear trigger criteria explaining EXACTLY when
|
|
|
5565
5604
|
orchestrator.run_task(current_text)
|
|
5566
5605
|
except Exception as e:
|
|
5567
5606
|
import traceback
|
|
5568
|
-
|
|
5569
|
-
|
|
5607
|
+
# Escape the exception text: a Rich markup tag in the
|
|
5608
|
+
# message (e.g. "[/bold white]") would otherwise raise
|
|
5609
|
+
# MarkupError and crash the event loop.
|
|
5610
|
+
console.print(f"\n[bold red]Task Execution Error: {escape(str(e))}[/bold red]")
|
|
5611
|
+
console.print(escape(traceback.format_exc()))
|
|
5570
5612
|
|
|
5571
5613
|
if STATE.get("_task_gen", my_gen) != my_gen:
|
|
5572
5614
|
return
|
|
@@ -5642,8 +5684,10 @@ description: <string> # Clear trigger criteria explaining EXACTLY when
|
|
|
5642
5684
|
orchestrator.run_task(combined_msg, is_notification=True)
|
|
5643
5685
|
except Exception as ex:
|
|
5644
5686
|
import traceback
|
|
5645
|
-
|
|
5646
|
-
|
|
5687
|
+
# Escape exception text to avoid MarkupError if it contains
|
|
5688
|
+
# Rich tag-like substrings.
|
|
5689
|
+
console.print(f"\n[bold red]Reactive Task Execution Error: {escape(str(ex))}[/bold red]")
|
|
5690
|
+
console.print(escape(traceback.format_exc()))
|
|
5647
5691
|
|
|
5648
5692
|
# Drain queue and any newly arrived pending background system events
|
|
5649
5693
|
while True:
|
|
@@ -5658,7 +5702,7 @@ description: <string> # Clear trigger criteria explaining EXACTLY when
|
|
|
5658
5702
|
try:
|
|
5659
5703
|
orchestrator.run_task(next_item, is_notification=_is_system_note(next_item))
|
|
5660
5704
|
except Exception as ex:
|
|
5661
|
-
console.print(f"\n[bold red]Task Execution Error: {ex}[/bold red]")
|
|
5705
|
+
console.print(f"\n[bold red]Task Execution Error: {escape(str(ex))}[/bold red]")
|
|
5662
5706
|
elif pending_sys:
|
|
5663
5707
|
all_events = [p for p in pending_sys if p]
|
|
5664
5708
|
if all_events:
|
|
@@ -5667,7 +5711,7 @@ description: <string> # Clear trigger criteria explaining EXACTLY when
|
|
|
5667
5711
|
try:
|
|
5668
5712
|
orchestrator.run_task(next_item, is_notification=True)
|
|
5669
5713
|
except Exception as ex:
|
|
5670
|
-
console.print(f"\n[bold red]Task Execution Error: {ex}[/bold red]")
|
|
5714
|
+
console.print(f"\n[bold red]Task Execution Error: {escape(str(ex))}[/bold red]")
|
|
5671
5715
|
else:
|
|
5672
5716
|
break
|
|
5673
5717
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.7
|
|
4
4
|
Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
|
|
5
5
|
License: Emend AI Proprietary EULA
|
|
6
6
|
Project-URL: Homepage, https://utim.dev
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|