utim-cli 2.1.1__tar.gz → 2.1.3__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.1.1 → utim_cli-2.1.3}/CHANGELOG.md +14 -0
- utim_cli-2.1.3/LICENSE +26 -0
- {utim_cli-2.1.1/utim_cli.egg-info → utim_cli-2.1.3}/PKG-INFO +2 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/pyproject.toml +4 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/__init__.py +1 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_version.py +1 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/agent.py +95 -69
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/auth.py +15 -2
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/blender_agent.py +2 -1
- utim_cli-2.1.3/utim_cli/brain.py +744 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/client_utils.py +20 -3
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/config.py +4 -2
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/constants.py +2 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/context_pruner.py +7 -3
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/mcp_clean_wrapper.py +28 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/orchestrator.py +553 -343
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/reflection.py +225 -3
- utim_cli-2.1.3/utim_cli/server/batch_processor.py +119 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/captcha.py +1 -2
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/cli_auth.py +5 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/db.py +108 -3
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/email_utils.py +509 -509
- utim_cli-2.1.3/utim_cli/server/local_llm.py +236 -0
- utim_cli-2.1.3/utim_cli/server/micro_batcher.py +95 -0
- utim_cli-2.1.3/utim_cli/server/model_agent.py +218 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/models.py +242 -29
- utim_cli-2.1.3/utim_cli/server/rewards_engine.py +403 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/router.py +29 -3
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/__init__.py +2 -2
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/auth_routes.py +8 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/completion_routes.py +135 -40
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/credit_routes.py +8 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/marketplace_routes.py +2 -2
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/quota_routes.py +33 -0
- utim_cli-2.1.3/utim_cli/server/routes/rewards_routes.py +558 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/security_routes.py +1 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/share_routes.py +146 -51
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/server.py +60 -56
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/storage_nodes.py +134 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/share.py +116 -47
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/share_tui.py +12 -12
- utim_cli-2.1.3/utim_cli/situational_scoring.py +359 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tools.py +186 -60
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/feedback_dialog.py +433 -419
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/marketplace_app_state.py +63 -63
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/marketplace_dialog.py +132 -132
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/marketplace_layout_engine.py +182 -182
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/mcp_dialog.py +627 -627
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/miniagents_dialog.py +369 -369
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/model_dialog.py +1701 -1902
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/publish_dialog.py +30 -30
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/quota_redeem_dialog.py +181 -181
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/quota_share_dialog.py +491 -491
- utim_cli-2.1.3/utim_cli/tui/rewards_tui.py +224 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/skills_dialog.py +320 -320
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/subagents_dialog.py +314 -314
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/tools_dialog.py +248 -248
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/update_dialog.py +1 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/utim.py +165 -114
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/vector_memory.py +10 -1
- utim_cli-2.1.3/utim_cli/wheel.py +26 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3/utim_cli.egg-info}/PKG-INFO +2 -1
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli.egg-info/SOURCES.txt +9 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli.egg-info/entry_points.txt +1 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli.egg-info/requires.txt +1 -0
- utim_cli-2.1.1/LICENSE +0 -21
- utim_cli-2.1.1/utim_cli/situational_scoring.py +0 -278
- {utim_cli-2.1.1 → utim_cli-2.1.3}/MANIFEST.in +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/README.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/setup.cfg +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/setup.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_check_obj.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_find_mcp.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_find_plan.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_find_tool.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_patch_multimodal.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/_test_regex.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/backup.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/billing.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/bootstrap.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/doctor.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/harbor.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/local_db.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/logger.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/report.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/auth.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/history.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/pricing_updater.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/state.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/tui/resume_dialog.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/utilities.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli/workspace.py +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.1.1 → utim_cli-2.1.3}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.1.3] - 2026-08-12
|
|
4
|
+
|
|
5
|
+
### 🛠️ Fixes
|
|
6
|
+
- Minor modifications and Bug fixes
|
|
7
|
+
|
|
8
|
+
## [2.1.2] - 2026-08-10
|
|
9
|
+
|
|
10
|
+
### 🛠️ Fixes
|
|
11
|
+
- **Fixed `grep_search` auto-regex promotion bug**: Queries containing regex metacharacters (`|`, `\`, `*`, etc.) were silently auto-promoted to regex mode even when `is_regex=False`, causing literal searches to fail with "No matches found." Now `is_regex=False` always means literal search, matching the documented "Literal-first semantics."
|
|
12
|
+
- **Made ripgrep an automatic dependency**: Added `ripgrep>=0.13.0` to core dependencies in `pyproject.toml` and `requirements.txt`. The `_grep_find_ripgrep()` function now falls back to the `ripgrep` PyPI package (which bundles a prebuilt binary) when ripgrep isn't found on the system PATH, ensuring fast searches work out of the box.
|
|
13
|
+
- **Increased context compression interval from 25 to 35 iterations**: Reduced compression frequency to conserve context resources and improve performance during long agent sessions.
|
|
14
|
+
- **Reduced batch poll interval from 5s to 0.3s**: Improved responsiveness of batch status checks in `BatchAPIProcessor.poll_and_retrieve_results()`.
|
|
15
|
+
- **Cleaned up network error messages in feedback dialog**: Stripped verbose `HTTPSConnectionPool` technical details from error messages, now showing clean messages like "Connection timed out. Please check your network and try again."
|
|
16
|
+
|
|
3
17
|
## [2.1.1] - 2026-08-06
|
|
4
18
|
|
|
5
19
|
### 🛠️ Fixes
|
utim_cli-2.1.3/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
UTIM PROPRIETARY SOFTWARE END USER LICENSE AGREEMENT (EULA)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Emend AI. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
IMPORTANT: READ THIS LICENSE AGREEMENT CAREFULLY BEFORE DOWNLOADING, INSTALLING, OR USING THE UTIM CLI SOFTWARE.
|
|
6
|
+
|
|
7
|
+
1. PROPRIETARY SOFTWARE
|
|
8
|
+
UTIM CLI ("Software") is proprietary commercial software developed and owned exclusively by Emend AI ("Licensor"). The Software is licensed, not sold, to you ("Licensee") under the terms and conditions of this Agreement.
|
|
9
|
+
|
|
10
|
+
2. LICENSE GRANT
|
|
11
|
+
Subject to compliance with this Agreement and payment of applicable subscription or usage fees:
|
|
12
|
+
- Free Tier License: Licensor grants Licensee a limited, non-exclusive, non-transferable, revocable license to install and use the Software for personal, open-source learning, and non-commercial evaluation purposes.
|
|
13
|
+
- Commercial Tier License: Paid subscribers (Hobby, Pro, Max, Ultimate Tiers) or users with active Bring Your Own Key (BYOK) configurations are granted a non-exclusive, non-transferable license to use the Software for commercial software engineering, corporate repositories, and professional client work.
|
|
14
|
+
|
|
15
|
+
3. RESTRICTIONS
|
|
16
|
+
Licensee shall NOT:
|
|
17
|
+
- Reverse engineer, decompile, disassemble, modify, or attempt to discover the source code or underlying client orchestrator mechanisms of compiled Software binaries.
|
|
18
|
+
- Circumvent rate limits, credit accounting controls, or 5-hour quota refill mechanisms.
|
|
19
|
+
- Sublicense, resell, rent, lease, or distribute the Software to third parties without prior written authorization.
|
|
20
|
+
- Create duplicate or automated accounts to harvest free promotional credit allocations.
|
|
21
|
+
|
|
22
|
+
4. INTELLECTUAL PROPERTY
|
|
23
|
+
All title, ownership, brand trademarks, domain adaptation engines, AST parsing logic, and intellectual property rights in and to the Software remain the exclusive property of Emend AI. Licensee retains 100% full ownership of all user prompts submitted and code files generated in their local workspaces.
|
|
24
|
+
|
|
25
|
+
5. NO WARRANTY & LIMITATION OF LIABILITY
|
|
26
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL EMEND AI OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES (INCLUDING DATA LOSS, REPOSITORY CORRUPTION, OR API BILLING COSTS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
4
4
|
Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://utim.dev
|
|
@@ -31,6 +31,7 @@ Requires-Dist: rich>=13.0.0
|
|
|
31
31
|
Requires-Dist: prompt_toolkit>=3.0.0
|
|
32
32
|
Requires-Dist: nest-asyncio>=1.5.0
|
|
33
33
|
Requires-Dist: sqlalchemy>=2.0.0
|
|
34
|
+
Requires-Dist: ripgrep>=0.13.0
|
|
34
35
|
Provides-Extra: search
|
|
35
36
|
Requires-Dist: scrapy>=2.11.0; extra == "search"
|
|
36
37
|
Requires-Dist: scrapy-playwright>=0.0.34; extra == "search"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "utim-cli"
|
|
7
|
-
version = "2.1.
|
|
7
|
+
version = "2.1.3"
|
|
8
8
|
description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"prompt_toolkit>=3.0.0",
|
|
34
34
|
"nest-asyncio>=1.5.0",
|
|
35
35
|
"sqlalchemy>=2.0.0",
|
|
36
|
+
"ripgrep>=0.13.0",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.urls]
|
|
@@ -88,6 +89,8 @@ memory = [
|
|
|
88
89
|
|
|
89
90
|
[project.scripts]
|
|
90
91
|
utim = "utim_cli.utim:main_cli_entry"
|
|
92
|
+
utimlite = "utim_cli.utim:main_cli_entry"
|
|
93
|
+
|
|
91
94
|
|
|
92
95
|
[tool.setuptools.packages.find]
|
|
93
96
|
include = ["utim_cli*"]
|
|
@@ -7,7 +7,6 @@ from rich.console import Console
|
|
|
7
7
|
from rich.live import Live
|
|
8
8
|
from rich.spinner import Spinner
|
|
9
9
|
from rich.markdown import Markdown
|
|
10
|
-
from rich.panel import Panel
|
|
11
10
|
from rich.text import Text
|
|
12
11
|
from rich.rule import Rule
|
|
13
12
|
from rich.syntax import Syntax
|
|
@@ -19,23 +18,16 @@ BLUE = "#42bcf5"
|
|
|
19
18
|
YELLOW = "#f9e2af"
|
|
20
19
|
|
|
21
20
|
TOOL_META = {
|
|
22
|
-
"read_file": {"icon": "
|
|
23
|
-
"write_file": {"icon": "
|
|
24
|
-
"edit_file": {"icon": "
|
|
25
|
-
"move_file": {"icon": "
|
|
26
|
-
"delete_file": {"icon": "
|
|
27
|
-
"run_command": {"icon": "
|
|
28
|
-
"list_directory": {"icon": "
|
|
21
|
+
"read_file": {"icon": "", "verb": "Read", "color": BLUE},
|
|
22
|
+
"write_file": {"icon": "", "verb": "Write", "color": YELLOW},
|
|
23
|
+
"edit_file": {"icon": "", "verb": "Edit", "color": YELLOW},
|
|
24
|
+
"move_file": {"icon": "", "verb": "Move", "color": BLUE},
|
|
25
|
+
"delete_file": {"icon": "", "verb": "Delete", "color": PURPLE},
|
|
26
|
+
"run_command": {"icon": "", "verb": "Bash", "color": YELLOW},
|
|
27
|
+
"list_directory": {"icon": "", "verb": "ListDir", "color": PURPLE},
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
TIPS = [
|
|
32
|
-
"Tip: Use /help to see all available commands and the help panel",
|
|
33
|
-
"Tip: Save your current conversation with /resume save <tag>",
|
|
34
|
-
"Tip: Clear conversation history with /clear",
|
|
35
|
-
"Tip: Check your quota limits and credit usage with /usage",
|
|
36
|
-
"Tip: Enable or disable individual tools with /tools",
|
|
37
|
-
"Tip: Switch models with /model",
|
|
38
|
-
]
|
|
30
|
+
TIPS = []
|
|
39
31
|
|
|
40
32
|
class ReActAgent:
|
|
41
33
|
"""A standalone agent that can reason and execute tools with streaming."""
|
|
@@ -48,7 +40,7 @@ class ReActAgent:
|
|
|
48
40
|
|
|
49
41
|
api_key = os.getenv("OPENROUTER_API_KEY") or os.getenv("OPENRouter_API_KEY")
|
|
50
42
|
if not api_key:
|
|
51
|
-
self.console.print("[dim]
|
|
43
|
+
self.console.print("[dim]Warning: OPENROUTER_API_KEY is not set.[/dim]")
|
|
52
44
|
|
|
53
45
|
try:
|
|
54
46
|
from openai import OpenAI
|
|
@@ -80,23 +72,23 @@ class ReActAgent:
|
|
|
80
72
|
return f"{elapsed // 60}m {elapsed % 60}s" if elapsed >= 60 else f"{elapsed}s"
|
|
81
73
|
|
|
82
74
|
def _next_tip(self):
|
|
75
|
+
if not TIPS:
|
|
76
|
+
return ""
|
|
83
77
|
tip = TIPS[self._tip_index % len(TIPS)]
|
|
84
78
|
self._tip_index += 1
|
|
85
79
|
return tip
|
|
86
80
|
|
|
81
|
+
|
|
87
82
|
def _render_tool_panel(self, func_name: str, args: dict, result: str):
|
|
88
|
-
"""Render a
|
|
89
|
-
meta = TOOL_META.get(func_name, {"icon": "●", "verb": func_name, "color": "white"})
|
|
90
|
-
icon = meta["icon"]
|
|
91
|
-
verb = meta["verb"]
|
|
92
|
-
color = meta["color"]
|
|
83
|
+
"""Render a completed tool call in Antigravity inline style — no box, no border.
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
Line 1: \u25cf Verb(path/arg)
|
|
86
|
+
Line 2: \u2514 summary
|
|
87
|
+
"""
|
|
88
|
+
meta = TOOL_META.get(func_name, {"icon": "\u25cf", "verb": func_name, "color": "white"})
|
|
89
|
+
verb = meta["verb"]
|
|
90
|
+
color = meta["color"]
|
|
98
91
|
|
|
99
|
-
# Show the primary argument (filepath or command)
|
|
100
92
|
primary_arg = (
|
|
101
93
|
args.get("filepath")
|
|
102
94
|
or args.get("command")
|
|
@@ -104,51 +96,37 @@ class ReActAgent:
|
|
|
104
96
|
or args.get("src")
|
|
105
97
|
or ""
|
|
106
98
|
)
|
|
107
|
-
if primary_arg:
|
|
108
|
-
header.append(f" {primary_arg}", style="white")
|
|
109
99
|
|
|
110
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
100
|
+
# Line 1: \u25cf Verb(arg)
|
|
101
|
+
header = Text()
|
|
102
|
+
header.append(verb, style="bold white")
|
|
103
|
+
if primary_arg:
|
|
104
|
+
header.append(f"({primary_arg})", style="dim white")
|
|
105
|
+
header.append(" (ctrl+o to expand)", style="dim #585b70")
|
|
115
106
|
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
# Line 2: \u2514 summary
|
|
108
|
+
summary = Text()
|
|
109
|
+
summary.append(" \u2514 ", style="dim #585b70")
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
if func_name == "read_file":
|
|
120
112
|
lines = result.split("\n")
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
summary.append(f"{len(lines)} lines", style="dim white")
|
|
114
|
+
elif func_name in ("write_file", "edit_file"):
|
|
115
|
+
first = result.strip().splitlines()[0][:80] if result.strip() else "done"
|
|
116
|
+
summary.append(first, style="dim white")
|
|
123
117
|
elif func_name == "run_command":
|
|
124
118
|
out = str(result).strip()
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
body.append(out, style="dim")
|
|
128
|
-
|
|
119
|
+
first = out.splitlines()[0][:80] if out else "(no output)"
|
|
120
|
+
summary.append(first, style="dim white")
|
|
129
121
|
elif func_name == "list_directory":
|
|
130
122
|
items = [l for l in str(result).split("\n") if l.strip()]
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
elif func_name == "move_file":
|
|
134
|
-
body.append(result, style="dim")
|
|
135
|
-
|
|
136
|
-
elif func_name == "delete_file":
|
|
137
|
-
body.append(result, style="dim")
|
|
138
|
-
|
|
123
|
+
summary.append(f"{max(0, len(items)-1)} items", style="dim white")
|
|
139
124
|
else:
|
|
140
125
|
out = str(result).strip()
|
|
141
|
-
if
|
|
142
|
-
out = out[:300] + "\n[…]"
|
|
143
|
-
body.append(out, style="dim")
|
|
126
|
+
summary.append((out.splitlines()[0][:80] if out else "done"), style="dim white")
|
|
144
127
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
self.console.print(Panel(
|
|
148
|
-
content,
|
|
149
|
-
border_style=color,
|
|
150
|
-
expand=False,
|
|
151
|
-
))
|
|
128
|
+
self.console.print(header)
|
|
129
|
+
self.console.print(summary)
|
|
152
130
|
|
|
153
131
|
def _execute_tool_call(self, tool_call):
|
|
154
132
|
"""Execute a single tool call by name."""
|
|
@@ -196,23 +174,53 @@ class ReActAgent:
|
|
|
196
174
|
|
|
197
175
|
arguments = json.loads(tool_call["function"]["arguments"])
|
|
198
176
|
|
|
199
|
-
# Show the tool is running
|
|
177
|
+
# Show the tool is running - Antigravity style: ● Verb(arg) (ctrl+o to expand)
|
|
200
178
|
meta = TOOL_META.get(tool_name, {"icon": "●", "verb": tool_name, "color": "white"})
|
|
201
|
-
|
|
179
|
+
_pre = Text()
|
|
180
|
+
_pre.append(meta['verb'], style="bold white")
|
|
181
|
+
primary_arg = (
|
|
182
|
+
arguments.get("filepath")
|
|
183
|
+
or arguments.get("command")
|
|
184
|
+
or arguments.get("path")
|
|
185
|
+
or ""
|
|
186
|
+
)
|
|
187
|
+
if primary_arg:
|
|
188
|
+
_pre.append(f"({primary_arg})", style="dim white")
|
|
189
|
+
_pre.append(" (ctrl+o to expand)", style="dim #585b70")
|
|
190
|
+
self.console.print(_pre)
|
|
191
|
+
pre_plain = f"{meta['verb']}({primary_arg}) (ctrl+o to expand)" if primary_arg else f"{meta['verb']} (ctrl+o to expand)"
|
|
192
|
+
self._last_running_indicator_len = len(pre_plain)
|
|
202
193
|
|
|
203
194
|
if "__" in tool_name:
|
|
204
195
|
server_name, actual_tool_name = tool_name.split("__", 1)
|
|
205
196
|
try:
|
|
206
197
|
from utim_cli.mcp_client import mcp_manager
|
|
207
198
|
if server_name in mcp_manager.sessions:
|
|
208
|
-
self.console.print(f"
|
|
199
|
+
self.console.print(f" Calling MCP tool {server_name} ➔ {actual_tool_name}...", style="dim #cba6f7")
|
|
209
200
|
result = mcp_manager.call_tool(server_name, actual_tool_name, arguments)
|
|
210
201
|
|
|
211
202
|
# Temporarily register metadata for render
|
|
212
|
-
TOOL_META[tool_name] = {"icon": "
|
|
203
|
+
TOOL_META[tool_name] = {"icon": "", "verb": f"Calling {server_name} ➔ {actual_tool_name}", "color": "#cba6f7"}
|
|
204
|
+
# Erase the running line first
|
|
205
|
+
try:
|
|
206
|
+
width = self.console.size.width
|
|
207
|
+
except Exception:
|
|
208
|
+
width = 80
|
|
209
|
+
import math
|
|
210
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
211
|
+
for _ in range(num_lines):
|
|
212
|
+
self.console.print("\033[F\033[K", end="")
|
|
213
213
|
self._render_tool_panel(tool_name, arguments, result)
|
|
214
214
|
return str(result)
|
|
215
215
|
except Exception as e:
|
|
216
|
+
try:
|
|
217
|
+
width = self.console.size.width
|
|
218
|
+
except Exception:
|
|
219
|
+
width = 80
|
|
220
|
+
import math
|
|
221
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
222
|
+
for _ in range(num_lines):
|
|
223
|
+
self.console.print("\033[F\033[K", end="")
|
|
216
224
|
return f"Error executing MCP tool {tool_name}: {str(e)}"
|
|
217
225
|
|
|
218
226
|
utim_tools, tool_functions = get_tools()
|
|
@@ -224,11 +232,29 @@ class ReActAgent:
|
|
|
224
232
|
|
|
225
233
|
try:
|
|
226
234
|
result = tool_functions[tool_name](**arguments)
|
|
235
|
+
# Erase the running line first
|
|
236
|
+
try:
|
|
237
|
+
width = self.console.size.width
|
|
238
|
+
except Exception:
|
|
239
|
+
width = 80
|
|
240
|
+
import math
|
|
241
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
242
|
+
for _ in range(num_lines):
|
|
243
|
+
self.console.print("\033[F\033[K", end="")
|
|
227
244
|
self._render_tool_panel(tool_name, arguments, result)
|
|
228
245
|
return str(result)
|
|
229
246
|
except Exception as e:
|
|
247
|
+
try:
|
|
248
|
+
width = self.console.size.width
|
|
249
|
+
except Exception:
|
|
250
|
+
width = 80
|
|
251
|
+
import math
|
|
252
|
+
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
253
|
+
for _ in range(num_lines):
|
|
254
|
+
self.console.print("\033[F\033[K", end="")
|
|
230
255
|
return f"Error executing {tool_name}: {str(e)}"
|
|
231
256
|
|
|
257
|
+
|
|
232
258
|
def run(self, max_iterations: int = 500, show_tools: bool = True):
|
|
233
259
|
"""Run the agent loop with streaming."""
|
|
234
260
|
self.start_time = time.time()
|
|
@@ -323,17 +349,17 @@ class ReActAgent:
|
|
|
323
349
|
"content": result,
|
|
324
350
|
})
|
|
325
351
|
else:
|
|
326
|
-
self.console.print(f"\n[bold yellow]
|
|
352
|
+
self.console.print(f"\n[bold yellow]Agent paused after reaching maximum iterations ({max_iterations}).[/bold yellow]")
|
|
327
353
|
self.console.print("[dim]You can type 'continue' to resume the task.[/dim]\n")
|
|
328
354
|
|
|
329
355
|
elapsed = self.get_elapsed_time()
|
|
330
356
|
tip = self._next_tip()
|
|
331
|
-
self.console.print(Rule(f"[dim]
|
|
357
|
+
self.console.print(Rule(f"[dim]{elapsed}[/dim]", style="dim"))
|
|
332
358
|
|
|
333
359
|
def list_tools(self):
|
|
334
360
|
"""Display all available tools in a formatted table."""
|
|
335
361
|
self.console.print()
|
|
336
|
-
self.console.print(Rule("[bold accent]
|
|
362
|
+
self.console.print(Rule("[bold accent]Available Tools[/bold accent]"))
|
|
337
363
|
self.console.print()
|
|
338
364
|
|
|
339
365
|
headers = ["Tool", "Description"]
|
|
@@ -374,7 +400,7 @@ class ReActAgent:
|
|
|
374
400
|
desc_str = f"{status_tag} [dim]{desc}[/dim]" if is_disabled else f"[dim]{desc}[/dim]"
|
|
375
401
|
|
|
376
402
|
if t_type == "mcp":
|
|
377
|
-
tool_str = f"
|
|
403
|
+
tool_str = f" {name.ljust(col_widths[0] - 4)} {desc_str}"
|
|
378
404
|
self.console.print(f"[#cba6f7]{tool_str}[/#cba6f7]")
|
|
379
405
|
else:
|
|
380
406
|
meta = TOOL_META.get(name, {"icon": "●", "color": "white"})
|
|
@@ -38,6 +38,19 @@ YELLOW = "#f9e2af"
|
|
|
38
38
|
SERVER_URL = os.environ.get("UTIM_SERVER_URL", "https://api.utim.dev")
|
|
39
39
|
WEB_URL = os.environ.get("UTIM_WEB_URL", "https://utim.dev")
|
|
40
40
|
|
|
41
|
+
def get_api_url() -> str:
|
|
42
|
+
"""Get base server URL from environment or fallback default."""
|
|
43
|
+
return os.environ.get("UTIM_SERVER_URL", SERVER_URL)
|
|
44
|
+
|
|
45
|
+
def get_auth_headers() -> dict[str, str]:
|
|
46
|
+
"""Get request headers containing current user API key."""
|
|
47
|
+
api_key = config.get("api_key") or ""
|
|
48
|
+
headers = {"Content-Type": "application/json"}
|
|
49
|
+
if api_key:
|
|
50
|
+
headers["X-API-Key"] = api_key
|
|
51
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
52
|
+
return headers
|
|
53
|
+
|
|
41
54
|
# Firebase project config (public — safe to embed in CLI)
|
|
42
55
|
FIREBASE_PROJECT_ID = "u-t-i-m-39c26"
|
|
43
56
|
FIREBASE_API_KEY = "AIzaSyAV-L3jY6dS3wXMMNGnYnPTX3IuqBFqK4E"
|
|
@@ -201,7 +214,7 @@ def login(restart: bool = True) -> None:
|
|
|
201
214
|
pass
|
|
202
215
|
|
|
203
216
|
# Print compact Termux/Android friendly instructions
|
|
204
|
-
raw_print(f"\n {BOLD}
|
|
217
|
+
raw_print(f"\n {BOLD}Device Sign-In Required:{RESET}")
|
|
205
218
|
raw_print(f" 1. Open link: {BLUE}{verify_url}{RESET}")
|
|
206
219
|
if clipboard_notice:
|
|
207
220
|
raw_print(f" {clipboard_notice.strip()}")
|
|
@@ -276,7 +289,7 @@ def login(restart: bool = True) -> None:
|
|
|
276
289
|
restart_process()
|
|
277
290
|
return
|
|
278
291
|
else:
|
|
279
|
-
raw_print(f" {YELLOW}
|
|
292
|
+
raw_print(f" {YELLOW}Authorization pending. Please complete it at: {BLUE}{verify_url}{RESET}")
|
|
280
293
|
else:
|
|
281
294
|
raw_print(f"\n \033[1;31m✗ Verification failed (HTTP {pr.status_code}). Please try again.\033[0m\n")
|
|
282
295
|
except Exception as e:
|
|
@@ -63,12 +63,13 @@ _VISION_MODELS: List[str] = [
|
|
|
63
63
|
]
|
|
64
64
|
|
|
65
65
|
_CODE_MODELS: List[str] = [
|
|
66
|
-
"poolside/laguna-m.1:free",
|
|
67
66
|
DEFAULT_MODEL,
|
|
67
|
+
"cohere/north-mini-code:free",
|
|
68
68
|
"openrouter/free"
|
|
69
69
|
]
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
|
|
72
73
|
def _get_vision_models() -> List[str]:
|
|
73
74
|
"""Return vision model list, with user override prepended if configured."""
|
|
74
75
|
try:
|