utim-cli 2.3.14__tar.gz → 2.3.16__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.14 → utim_cli-2.3.16}/CHANGELOG.md +10 -0
- {utim_cli-2.3.14/utim_cli.egg-info → utim_cli-2.3.16}/PKG-INFO +1 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/pyproject.toml +2 -2
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim/__init__.py +1 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/__init__.py +1 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/_version.py +1 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/agent.py +7 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/bootstrap.py +47 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/brain.py +1 -1
- utim_cli-2.3.16/utim_cli/builtin/skills/utim-guide/SKILL.md +37 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/client_utils.py +164 -6
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/config.py +33 -27
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/context_pruner.py +101 -27
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/crawler/markdown_distiller.py +258 -251
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/orchestrator.py +2 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/orchestrator_helpers.py +3 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/orchestrator_llm_transport.py +59 -24
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/orchestrator_tool_exec.py +24 -28
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/orchestrator_turn_history.py +40 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/manager.py +135 -43
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/reflection.py +2 -7
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/auth.py +51 -5
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/cli_auth.py +6 -1
- utim_cli-2.3.16/utim_cli/server/email_utils.py +917 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/history.py +6 -2
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/key_rotator.py +1055 -710
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/models.py +396 -95
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/pricing_updater.py +3 -3
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/router.py +7 -2
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/__init__.py +21 -20
- utim_cli-2.3.16/utim_cli/server/routes/admin_bonus_routes.py +184 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/completion_routes.py +101 -37
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/referral_routes.py +308 -308
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/security_routes.py +7 -4
- utim_cli-2.3.16/utim_cli/server/utils.py +283 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tools.py +95 -43
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/feedback_dialog.py +87 -14
- utim_cli-2.3.16/utim_cli/tui/plugins_dialog.py +756 -0
- utim_cli-2.3.16/utim_cli/update_checker.py +545 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/utim.py +156 -34
- {utim_cli-2.3.14 → utim_cli-2.3.16/utim_cli.egg-info}/PKG-INFO +1 -1
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli.egg-info/SOURCES.txt +4 -0
- utim_cli-2.3.14/utim_cli/server/email_utils.py +0 -691
- utim_cli-2.3.14/utim_cli/tui/plugins_dialog.py +0 -351
- {utim_cli-2.3.14 → utim_cli-2.3.16}/LICENSE +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/MANIFEST.in +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/README.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/setup.cfg +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/setup.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/_verify_repro.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/_verify_syntax.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/ask_helper.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/auth.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/backup.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/billing.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/blender_agent.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/constants.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/crawler/__init__.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/crawler/engine.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/dependency_manager.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/doctor.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/evidence_registry.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/harbor.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/local_db.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/logger.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/mcp_clean_wrapper.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/models.txt +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/personalities.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/platform_installer.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/__init__.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/hooks.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/registry.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/schema.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/plugins/tool_loader.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/report.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/__init__.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/config.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/context.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/events.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/handlers.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/session.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/sdk/sidecar.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/admin_auth.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/batch_processor.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/captcha.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/concurrency.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/db.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/local_llm.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/micro_batcher.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/model_agent.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/rewards_engine.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/admin_db_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/auth_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/billing_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/credit_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/marketplace_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/quota_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/rewards_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/routes/share_routes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/server.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/server/storage_nodes.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/share.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/share_tui.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/situational_scoring.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/state.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/subagent_manager.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/task_dispatcher.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/task_logger.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tools_grep.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/marketplace_app_state.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/marketplace_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/marketplace_layout_engine.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/mcp_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/miniagents_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/model_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/personality_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/publish_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/quota_redeem_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/quota_share_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/resume_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/rewards_tui.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/skills_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/subagents_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/tasks_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/thinking_display.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/tools_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/tui/update_dialog.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/utilities.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/utimmodel.txt +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/vector_memory.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/wheel.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli/workspace.py +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli.egg-info/entry_points.txt +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli.egg-info/requires.txt +0 -0
- {utim_cli-2.3.14 → utim_cli-2.3.16}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.3.16] - 2026-08-29
|
|
4
|
+
|
|
5
|
+
### 🛠️ Minor Enhancements and Bug Fixes
|
|
6
|
+
- Minor Enhancements and Bug Fixes
|
|
7
|
+
|
|
8
|
+
## [2.3.15] - 2026-08-29
|
|
9
|
+
|
|
10
|
+
### 🛠️ Minor Enhancements and Tool Call Bug Fixes
|
|
11
|
+
- Minor enhancements and fixed tool call bugs
|
|
12
|
+
|
|
3
13
|
## [2.3.14] - 2026-08-28
|
|
4
14
|
|
|
5
15
|
### ⚡ Request Queue & Scaled Free Capacity
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.16
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "utim-cli"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.16"
|
|
8
8
|
|
|
9
9
|
description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
|
|
10
10
|
readme = "README.md"
|
|
@@ -97,7 +97,7 @@ include = ["utim*", "utim_cli*"]
|
|
|
97
97
|
exclude = ["tests*", "landing*", "scripts*"]
|
|
98
98
|
|
|
99
99
|
[tool.setuptools.package-data]
|
|
100
|
-
utim_cli = ["mcp_registry.json", "_version.py", "utimmodel.txt", "models.txt", "server/docs_md/*.md"]
|
|
100
|
+
utim_cli = ["mcp_registry.json", "_version.py", "utimmodel.txt", "models.txt", "server/docs_md/*.md", "builtin/**/*", "builtin/skills/**/*"]
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
[tool.pytest.ini_options]
|
|
@@ -14,7 +14,7 @@ Quickstart:
|
|
|
14
14
|
try:
|
|
15
15
|
from utim_cli._version import VERSION as __version__
|
|
16
16
|
except ImportError:
|
|
17
|
-
__version__ = "2.3.
|
|
17
|
+
__version__ = "2.3.16"
|
|
18
18
|
|
|
19
19
|
# Developer-friendly core classes (similar to `import genai` / `import openai`)
|
|
20
20
|
from utim_cli.sdk.session import UtimSession as Session
|
|
@@ -168,6 +168,13 @@ class ReActAgent:
|
|
|
168
168
|
arguments[key] = val
|
|
169
169
|
tool_name = actual_name
|
|
170
170
|
|
|
171
|
+
# Collapse duplicated tool names (e.g. 'read_fileread_file', 'read_file read_file',
|
|
172
|
+
# 'run_commandrun_command') produced by buggy model/proxy tool-call serialization.
|
|
173
|
+
import re as _re_dup
|
|
174
|
+
_dup_match = _re_dup.match(r'^([a-zA-Z_]\w*)[\s_]*\1$', tool_name)
|
|
175
|
+
if _dup_match:
|
|
176
|
+
tool_name = _dup_match.group(1)
|
|
177
|
+
|
|
171
178
|
# Map common alias tool names to actual UTIM CLI tool names
|
|
172
179
|
_TOOL_NAME_ALIASES = {
|
|
173
180
|
"shell": "run_command",
|
|
@@ -74,10 +74,54 @@ def _sync_global_experience(api_key: str, current_folder: str):
|
|
|
74
74
|
pass
|
|
75
75
|
|
|
76
76
|
def _write_skills_and_agents():
|
|
77
|
-
"""Create the
|
|
77
|
+
"""Create the skills/ directory inside ~/.utim/ and auto-seed built-in guides."""
|
|
78
78
|
try:
|
|
79
79
|
skills_dir = UTIM_DIR / 'skills'
|
|
80
80
|
skills_dir.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
|
|
82
|
+
guide_dir = skills_dir / 'utim-guide'
|
|
83
|
+
guide_dir.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
guide_md = guide_dir / 'SKILL.md'
|
|
85
|
+
if not guide_md.exists():
|
|
86
|
+
guide_content = """---
|
|
87
|
+
name: utim-guide
|
|
88
|
+
description: Comprehensive reference for UTIM CLI features, keybindings, commands (/model, /rewind, /tools, /tasks, /mcp, /doctor), model switching, and session controls.
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
# UTIM CLI User Guide & Command Reference
|
|
92
|
+
|
|
93
|
+
When users ask how to use, configure, or control UTIM CLI, or ask you to switch models or perform CLI operations, explain and reference these native commands:
|
|
94
|
+
|
|
95
|
+
## 1. Model Switching & Configuration
|
|
96
|
+
- **Switch Model**: Type `/model` or press `Ctrl+M` in the terminal to open the interactive model selector (browse catalog, switch to free/paid models, import BYOK OpenRouter/Anthropic/OpenAI keys, or connect custom OpenAI-compatible endpoints).
|
|
97
|
+
- **Model Settings**: Type `/modelsettings` to customize max token limits, temperature, or context envelopes per model.
|
|
98
|
+
- **API Keys / BYOK**: Type `/auth` or configure OpenRouter / custom provider keys via `/model`.
|
|
99
|
+
|
|
100
|
+
## 2. Session & History Navigation
|
|
101
|
+
- **Rollback & Rewind**: Type `/rewind` to open the interactive checkpoint rollback menu (rewinds conversation history and reverts filesystem changes).
|
|
102
|
+
- **Undo / Redo**: Type `/undo` or `/redo` to quickly revert or reapply recent file modifications.
|
|
103
|
+
- **Resume Sessions**: Type `/resume` to search and load past saved sessions.
|
|
104
|
+
- **Clear / Fresh Session**: Type `/clear` or `/new` to start a clean new session.
|
|
105
|
+
- **Context Compaction**: Type `/compact` to manually compact session context.
|
|
106
|
+
|
|
107
|
+
## 3. Tool & Terminal Controls
|
|
108
|
+
- **Tool Toggle Menu**: Type `/tools` to interactively enable or disable specific tools (e.g. `run_command`, `write_file`, `web_search`).
|
|
109
|
+
- **Expand / Collapse Output**: Press `Ctrl+O` in the terminal to toggle expanding or collapsing tool execution details and `<think>` reasoning blocks.
|
|
110
|
+
- **Abort / Cancel**: Press `Ctrl+C` to immediately cancel active model generation or tool execution.
|
|
111
|
+
- **Background Tasks**: Type `/tasks` or `/bg` to list running background processes and timers. Use `/kill <id>` or `/stop <id>` to terminate them.
|
|
112
|
+
|
|
113
|
+
## 4. MCP, Skills & Subagents
|
|
114
|
+
- **Model Context Protocol (MCP)**: Type `/mcp` to manage MCP server connections, view active tools, or install new MCP servers.
|
|
115
|
+
- **Skills & Subagents**: Type `/skills` to browse active skill playbooks, or `/subagents` / `/miniagents` to invoke specialized subagent workflows.
|
|
116
|
+
- **Marketplace**: Type `/marketplace` to explore community tools and extensions.
|
|
117
|
+
|
|
118
|
+
## 5. Account & Diagnostics
|
|
119
|
+
- **Login / Logout**: Type `/login` or `/logout` to manage your UTIM Community account.
|
|
120
|
+
- **Quota & Balance**: Type `/quota` to check remaining credits, `/usage` for model token breakdown, `/quotashare` to share quota, or `/redeem` to redeem shared access keys.
|
|
121
|
+
- **Diagnostics**: Type `/doctor` to test environment health, API connectivity, and database integrity.
|
|
122
|
+
- **Support Report**: Type `/report` to create a diagnostic support bundle.
|
|
123
|
+
"""
|
|
124
|
+
guide_md.write_text(guide_content, encoding='utf-8')
|
|
81
125
|
except Exception as e:
|
|
82
126
|
from utim_cli.logger import log_error
|
|
83
127
|
log_error("bootstrap", "Failed to create skills directory in ~/.utim/", e)
|
|
@@ -157,7 +201,9 @@ def scan_available_skills(force_reload: bool = False):
|
|
|
157
201
|
disabled = []
|
|
158
202
|
|
|
159
203
|
skills = {}
|
|
204
|
+
pkg_builtin_skills = Path(__file__).resolve().parent / "builtin" / "skills"
|
|
160
205
|
paths_to_check = [
|
|
206
|
+
pkg_builtin_skills,
|
|
161
207
|
Path('.agents/skills'),
|
|
162
208
|
Path('.agentskills'),
|
|
163
209
|
Path('.agents'),
|
|
@@ -990,7 +990,7 @@ def get_brain_context_prompt(user_prompt: str = "") -> str:
|
|
|
990
990
|
f"\n\n### BRAIN MEMORY DIRECTORY ###\n"
|
|
991
991
|
f"Project brain directory: [brain](file:///{proj_url})\n"
|
|
992
992
|
f"Global brain directory: [global_brain](file:///{root_url})\n"
|
|
993
|
-
f"
|
|
993
|
+
f"Note: The paths above are directories. To explore historical project memories, architecture snapshots, or conventions, call `list_directory('{proj_url}')` and `read_file` on the memory files inside it.\n"
|
|
994
994
|
)
|
|
995
995
|
except Exception:
|
|
996
996
|
return ""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: utim-guide
|
|
3
|
+
description: Comprehensive reference for UTIM CLI features, keybindings, commands (/model, /rewind, /tools, /tasks, /mcp, /doctor), model switching, and session controls.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# UTIM CLI User Guide & Command Reference
|
|
7
|
+
|
|
8
|
+
When users ask how to use, configure, or control UTIM CLI, or ask you to switch models or perform CLI operations, explain and reference these native commands:
|
|
9
|
+
|
|
10
|
+
## 1. Model Switching & Configuration
|
|
11
|
+
- **Switch Model**: Type `/model` or press `Ctrl+M` in the terminal to open the interactive model selector (browse catalog, switch between free/paid models, import BYOK OpenRouter/Anthropic/OpenAI keys, or connect custom OpenAI-compatible endpoints).
|
|
12
|
+
- **Model Settings**: Type `/modelsettings` to customize max token limits, temperature, or context envelopes per model.
|
|
13
|
+
- **API Keys / BYOK**: Type `/auth` or configure OpenRouter / custom provider keys via `/model`.
|
|
14
|
+
|
|
15
|
+
## 2. Session & History Navigation
|
|
16
|
+
- **Rollback & Rewind**: Type `/rewind` to open the interactive checkpoint rollback menu (rewinds conversation history and reverts filesystem changes).
|
|
17
|
+
- **Undo / Redo**: Type `/undo` or `/redo` to quickly revert or reapply recent file modifications.
|
|
18
|
+
- **Resume Sessions**: Type `/resume` to search and load past saved sessions.
|
|
19
|
+
- **Clear / Fresh Session**: Type `/clear` or `/new` to start a clean new session.
|
|
20
|
+
- **Context Compaction**: Type `/compact` to manually compact session context.
|
|
21
|
+
|
|
22
|
+
## 3. Tool & Terminal Controls
|
|
23
|
+
- **Tool Toggle Menu**: Type `/tools` to interactively enable or disable specific tools (e.g. `run_command`, `write_file`, `web_search`).
|
|
24
|
+
- **Expand / Collapse Output**: Press `Ctrl+O` in the terminal to toggle expanding or collapsing tool execution details and `<think>` reasoning blocks.
|
|
25
|
+
- **Abort / Cancel**: Press `Ctrl+C` to immediately cancel active model generation or tool execution.
|
|
26
|
+
- **Background Tasks**: Type `/tasks` or `/bg` to list running background processes and timers. Use `/kill <id>` or `/stop <id>` to terminate them.
|
|
27
|
+
|
|
28
|
+
## 4. MCP, Skills & Subagents
|
|
29
|
+
- **Model Context Protocol (MCP)**: Type `/mcp` to manage MCP server connections, view active tools, or install new MCP servers.
|
|
30
|
+
- **Skills & Subagents**: Type `/skills` to browse active skill playbooks, or `/subagents` / `/miniagents` to invoke specialized subagent workflows.
|
|
31
|
+
- **Marketplace**: Type `/marketplace` to explore community tools and extensions.
|
|
32
|
+
|
|
33
|
+
## 5. Account & Diagnostics
|
|
34
|
+
- **Login / Logout**: Type `/login` or `/logout` to manage your UTIM Community account.
|
|
35
|
+
- **Quota & Balance**: Type `/quota` to check remaining credits, `/usage` for model token breakdown, `/quotashare` to share quota, or `/redeem` to redeem shared access keys.
|
|
36
|
+
- **Diagnostics**: Type `/doctor` to test environment health, API connectivity, and database integrity.
|
|
37
|
+
- **Support Report**: Type `/report` to create a diagnostic support bundle.
|
|
@@ -678,7 +678,8 @@ def sanitize_messages_for_api(messages, model_id: Optional[str] = None):
|
|
|
678
678
|
fn["arguments"] = "{}"
|
|
679
679
|
elif not isinstance(args, str):
|
|
680
680
|
try:
|
|
681
|
-
|
|
681
|
+
clean_args = _sanitize_json(args)
|
|
682
|
+
fn["arguments"] = json.dumps(clean_args, allow_nan=False)
|
|
682
683
|
except (TypeError, ValueError):
|
|
683
684
|
fn["arguments"] = "{}"
|
|
684
685
|
c["function"] = fn
|
|
@@ -856,14 +857,171 @@ def parse_xml_tool_calls(content: str) -> tuple[str | None, list[dict]]:
|
|
|
856
857
|
}
|
|
857
858
|
})
|
|
858
859
|
|
|
859
|
-
parts.append(content[last_idx:])
|
|
860
|
-
cleaned_content = "".join(parts).strip()
|
|
861
|
-
if not cleaned_content:
|
|
862
|
-
cleaned_content = None
|
|
863
|
-
|
|
864
860
|
return cleaned_content, tool_calls
|
|
865
861
|
|
|
866
862
|
|
|
863
|
+
def merge_tool_call_delta(entry: dict, tc_delta: Any) -> None:
|
|
864
|
+
"""
|
|
865
|
+
Safely accumulates an incoming streaming tool call delta chunk into the target entry.
|
|
866
|
+
Prevents duplicate tool names (e.g. 'read_fileread_file') and duplicate cumulative
|
|
867
|
+
argument buffers (which produce invalid concatenated JSON).
|
|
868
|
+
"""
|
|
869
|
+
if not entry or not isinstance(entry, dict):
|
|
870
|
+
return
|
|
871
|
+
|
|
872
|
+
if "function" not in entry or not isinstance(entry["function"], dict):
|
|
873
|
+
entry["function"] = {"name": "", "arguments": ""}
|
|
874
|
+
|
|
875
|
+
# 1. Tool Call ID
|
|
876
|
+
tc_id = tc_delta.get("id") if isinstance(tc_delta, dict) else getattr(tc_delta, "id", None)
|
|
877
|
+
if tc_id and not entry.get("id"):
|
|
878
|
+
entry["id"] = tc_id
|
|
879
|
+
|
|
880
|
+
# 2. Function object
|
|
881
|
+
fn = tc_delta.get("function") if isinstance(tc_delta, dict) else getattr(tc_delta, "function", None)
|
|
882
|
+
if not fn:
|
|
883
|
+
return
|
|
884
|
+
|
|
885
|
+
incoming_name = fn.get("name") if isinstance(fn, dict) else getattr(fn, "name", None)
|
|
886
|
+
incoming_args = fn.get("arguments") if isinstance(fn, dict) else getattr(fn, "arguments", None)
|
|
887
|
+
|
|
888
|
+
# Function Name:
|
|
889
|
+
if incoming_name:
|
|
890
|
+
curr_name = entry["function"].get("name", "")
|
|
891
|
+
if not curr_name:
|
|
892
|
+
entry["function"]["name"] = str(incoming_name)
|
|
893
|
+
elif curr_name == incoming_name:
|
|
894
|
+
pass # Provider resent the exact same complete tool name; do not duplicate
|
|
895
|
+
elif incoming_name.startswith(curr_name):
|
|
896
|
+
entry["function"]["name"] = str(incoming_name) # Provider sent full/longer name
|
|
897
|
+
elif curr_name.startswith(incoming_name):
|
|
898
|
+
pass # Existing name is already more complete
|
|
899
|
+
else:
|
|
900
|
+
# Genuine chunk continuation
|
|
901
|
+
entry["function"]["name"] += str(incoming_name)
|
|
902
|
+
|
|
903
|
+
# Function Arguments:
|
|
904
|
+
if incoming_args is not None:
|
|
905
|
+
incoming_args_str = str(incoming_args)
|
|
906
|
+
curr_args = entry["function"].get("arguments", "")
|
|
907
|
+
if not curr_args:
|
|
908
|
+
entry["function"]["arguments"] = incoming_args_str
|
|
909
|
+
elif curr_args == incoming_args_str:
|
|
910
|
+
pass # Provider resent the same arguments buffer
|
|
911
|
+
elif incoming_args_str.startswith(curr_args):
|
|
912
|
+
# Provider sent cumulative buffer instead of delta
|
|
913
|
+
entry["function"]["arguments"] = incoming_args_str
|
|
914
|
+
else:
|
|
915
|
+
# Standard delta chunk
|
|
916
|
+
entry["function"]["arguments"] += incoming_args_str
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
def repair_and_normalize_tool_call(tc: dict) -> dict:
|
|
920
|
+
"""
|
|
921
|
+
Sanitizes, de-duplicates and repairs tool call names and JSON arguments.
|
|
922
|
+
Fixes doubled tool names ('read_fileread_file' -> 'read_file'), alias names,
|
|
923
|
+
concatenated JSON objects, unclosed braces, and attribute-style strings.
|
|
924
|
+
"""
|
|
925
|
+
if not tc or not isinstance(tc, dict):
|
|
926
|
+
return tc
|
|
927
|
+
|
|
928
|
+
import re, json
|
|
929
|
+
|
|
930
|
+
KNOWN_TOOLS = {
|
|
931
|
+
"read_file", "write_file", "edit_file", "delete_file", "move_file",
|
|
932
|
+
"run_command", "list_directory", "get_background_output", "send_background_input",
|
|
933
|
+
"stop_background_process", "list_background_processes", "web_search",
|
|
934
|
+
"plan_project", "manage_todos", "generate_image", "grep_search", "view_file", "search"
|
|
935
|
+
}
|
|
936
|
+
ALIASES = {
|
|
937
|
+
"view_file": "read_file",
|
|
938
|
+
"search": "grep_search",
|
|
939
|
+
"search_codebase": "grep_search",
|
|
940
|
+
"bash": "run_command",
|
|
941
|
+
"cmd": "run_command",
|
|
942
|
+
"shell": "run_command",
|
|
943
|
+
"execute_command": "run_command",
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
fn = tc.get("function")
|
|
947
|
+
if not fn or not isinstance(fn, dict):
|
|
948
|
+
return tc
|
|
949
|
+
|
|
950
|
+
raw_name = str(fn.get("name", "")).strip()
|
|
951
|
+
raw_args = fn.get("arguments", "{}")
|
|
952
|
+
|
|
953
|
+
# 1. Clean and normalize tool name
|
|
954
|
+
for known in KNOWN_TOOLS:
|
|
955
|
+
if raw_name.startswith(known):
|
|
956
|
+
raw_name = known
|
|
957
|
+
break
|
|
958
|
+
if raw_name == known + known:
|
|
959
|
+
raw_name = known
|
|
960
|
+
break
|
|
961
|
+
|
|
962
|
+
if raw_name in ALIASES:
|
|
963
|
+
raw_name = ALIASES[raw_name]
|
|
964
|
+
|
|
965
|
+
fn["name"] = raw_name
|
|
966
|
+
|
|
967
|
+
# 2. Repair and validate arguments JSON
|
|
968
|
+
if isinstance(raw_args, dict):
|
|
969
|
+
fn["arguments"] = json.dumps(raw_args)
|
|
970
|
+
return tc
|
|
971
|
+
|
|
972
|
+
raw_args_str = str(raw_args).strip()
|
|
973
|
+
if not raw_args_str or raw_args_str in ("{}", "null", "None"):
|
|
974
|
+
fn["arguments"] = "{}"
|
|
975
|
+
return tc
|
|
976
|
+
|
|
977
|
+
# Try standard JSON parsing
|
|
978
|
+
try:
|
|
979
|
+
parsed = json.loads(raw_args_str)
|
|
980
|
+
if isinstance(parsed, dict):
|
|
981
|
+
fn["arguments"] = json.dumps(parsed)
|
|
982
|
+
return tc
|
|
983
|
+
except json.JSONDecodeError:
|
|
984
|
+
pass
|
|
985
|
+
|
|
986
|
+
# Recovery 1: Check for concatenated JSON objects (e.g. {"filepath": "foo.py"}{"filepath": "foo.py"})
|
|
987
|
+
match = re.search(r'(\{[\s\S]*?\})(?:\{|$)', raw_args_str)
|
|
988
|
+
if match:
|
|
989
|
+
try:
|
|
990
|
+
parsed = json.loads(match.group(1))
|
|
991
|
+
if isinstance(parsed, dict):
|
|
992
|
+
fn["arguments"] = json.dumps(parsed)
|
|
993
|
+
return tc
|
|
994
|
+
except json.JSONDecodeError:
|
|
995
|
+
pass
|
|
996
|
+
|
|
997
|
+
# Recovery 2: XML / key="value" attribute string fallback
|
|
998
|
+
attrs = re.findall(
|
|
999
|
+
r'(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|\'((?:[^\'\\]|\\.)*)\'|(\S+))',
|
|
1000
|
+
raw_args_str,
|
|
1001
|
+
)
|
|
1002
|
+
if attrs:
|
|
1003
|
+
recovered = {}
|
|
1004
|
+
for key, v1, v2, v3 in attrs:
|
|
1005
|
+
val = v1 or v2 or v3 or ""
|
|
1006
|
+
val = val.replace('\\"', '"').replace("\\'", "'").replace("\\n", "\n").replace("\\t", "\t")
|
|
1007
|
+
recovered[key] = val
|
|
1008
|
+
if recovered:
|
|
1009
|
+
fn["arguments"] = json.dumps(recovered)
|
|
1010
|
+
return tc
|
|
1011
|
+
|
|
1012
|
+
# Recovery 3: Missing trailing closing brace (e.g. {"filepath": "foo.py" )
|
|
1013
|
+
if raw_args_str.startswith("{") and not raw_args_str.endswith("}"):
|
|
1014
|
+
try:
|
|
1015
|
+
parsed = json.loads(raw_args_str + "}")
|
|
1016
|
+
if isinstance(parsed, dict):
|
|
1017
|
+
fn["arguments"] = json.dumps(parsed)
|
|
1018
|
+
return tc
|
|
1019
|
+
except json.JSONDecodeError:
|
|
1020
|
+
pass
|
|
1021
|
+
|
|
1022
|
+
return tc
|
|
1023
|
+
|
|
1024
|
+
|
|
867
1025
|
# ── Process-level session-usage runtime sentinel ────────────────────────────
|
|
868
1026
|
_RUNTIME_SESSION_KEY: str = ""
|
|
869
1027
|
|
|
@@ -230,15 +230,7 @@ def get_all_utim_dirs() -> List[pathlib.Path]:
|
|
|
230
230
|
if "pytest" in sys.argv[0] or "py.test" in sys.argv[0] or "PYTEST_CURRENT_TEST" in os.environ:
|
|
231
231
|
_add(pathlib.Path('.utim'))
|
|
232
232
|
|
|
233
|
-
# 3.
|
|
234
|
-
try:
|
|
235
|
-
cwd = pathlib.Path.cwd()
|
|
236
|
-
_add(cwd / ".utim")
|
|
237
|
-
_add(cwd / ".agents")
|
|
238
|
-
except Exception:
|
|
239
|
-
pass
|
|
240
|
-
|
|
241
|
-
# 4. Standard User Home variants (handles Windows username differences, Git Bash, MSYS, Cygwin)
|
|
233
|
+
# 3. Standard User Home variants (handles Windows username differences, Linux /home/<user>, macOS /Users/<user>, Git Bash, MSYS, Cygwin)
|
|
242
234
|
user_home_paths = []
|
|
243
235
|
if os.environ.get("USERPROFILE"):
|
|
244
236
|
user_home_paths.append(pathlib.Path(os.environ["USERPROFILE"]))
|
|
@@ -263,20 +255,28 @@ def get_all_utim_dirs() -> List[pathlib.Path]:
|
|
|
263
255
|
_add(h / ".utim")
|
|
264
256
|
_add(h / ".agents")
|
|
265
257
|
|
|
258
|
+
# 4. Current workspace / project directories
|
|
259
|
+
try:
|
|
260
|
+
cwd = pathlib.Path.cwd()
|
|
261
|
+
_add(cwd / ".utim")
|
|
262
|
+
_add(cwd / ".agents")
|
|
263
|
+
except Exception:
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
266
|
return candidates
|
|
267
267
|
|
|
268
268
|
|
|
269
269
|
def get_utim_dir() -> pathlib.Path:
|
|
270
|
-
"""Return the centralized .utim directory path.
|
|
271
|
-
|
|
272
|
-
and
|
|
270
|
+
"""Return the centralized, authoritative .utim directory path for the current user.
|
|
271
|
+
Dynamically resolves across all OS environments (Windows, macOS, Linux, Termux)
|
|
272
|
+
and user profiles.
|
|
273
273
|
"""
|
|
274
274
|
import sys
|
|
275
275
|
if "pytest" in sys.argv[0] or "py.test" in sys.argv[0] or "PYTEST_CURRENT_TEST" in os.environ:
|
|
276
|
-
return pathlib.Path('.utim')
|
|
276
|
+
return pathlib.Path('.utim').resolve()
|
|
277
277
|
|
|
278
|
-
# Check explicit
|
|
279
|
-
for env_var in ("UTIM_HOME", "UTIM_DIR", "UTIM_PATH"):
|
|
278
|
+
# 1. Check explicit environment variables
|
|
279
|
+
for env_var in ("UTIM_HOME", "UTIM_DIR", "UTIM_PATH", "AGY_HOME", "ANTIGRAVITY_HOME"):
|
|
280
280
|
val = os.environ.get(env_var)
|
|
281
281
|
if val:
|
|
282
282
|
try:
|
|
@@ -286,26 +286,32 @@ def get_utim_dir() -> pathlib.Path:
|
|
|
286
286
|
except Exception:
|
|
287
287
|
pass
|
|
288
288
|
|
|
289
|
-
#
|
|
290
|
-
all_dirs = get_all_utim_dirs()
|
|
291
|
-
for d in all_dirs:
|
|
292
|
-
if d.exists() and d.is_dir() and d.name == ".utim":
|
|
293
|
-
return d
|
|
294
|
-
|
|
295
|
-
# Default fallback to primary user home
|
|
289
|
+
# 2. Dynamic Primary User Home directory (~/.utim)
|
|
296
290
|
try:
|
|
297
291
|
if os.environ.get("USERPROFILE"):
|
|
298
|
-
|
|
292
|
+
home_utim = pathlib.Path(os.environ["USERPROFILE"]) / ".utim"
|
|
293
|
+
elif os.environ.get("HOME"):
|
|
294
|
+
home_utim = pathlib.Path(os.environ["HOME"]) / ".utim"
|
|
295
|
+
elif os.environ.get("HOMEDRIVE") and os.environ.get("HOMEPATH"):
|
|
296
|
+
home_utim = pathlib.Path(os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"]) / ".utim"
|
|
299
297
|
else:
|
|
300
|
-
|
|
298
|
+
home_utim = pathlib.Path.home() / ".utim"
|
|
301
299
|
except Exception:
|
|
302
|
-
|
|
300
|
+
home_utim = pathlib.Path(os.path.expanduser("~")) / ".utim"
|
|
303
301
|
|
|
304
302
|
try:
|
|
305
|
-
|
|
303
|
+
home_utim.mkdir(parents=True, exist_ok=True)
|
|
304
|
+
return home_utim
|
|
306
305
|
except Exception:
|
|
307
306
|
pass
|
|
308
|
-
|
|
307
|
+
|
|
308
|
+
# 3. Candidate fallback if user home is read-only or inaccessible
|
|
309
|
+
all_dirs = get_all_utim_dirs()
|
|
310
|
+
for d in all_dirs:
|
|
311
|
+
if d.exists() and d.is_dir() and d.name == ".utim":
|
|
312
|
+
return d
|
|
313
|
+
|
|
314
|
+
return home_utim
|
|
309
315
|
|
|
310
316
|
|
|
311
317
|
class Config:
|