utim-cli 2.4.0__tar.gz → 2.4.2__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.4.0 → utim_cli-2.4.2}/CHANGELOG.md +20 -0
- {utim_cli-2.4.0/utim_cli.egg-info → utim_cli-2.4.2}/PKG-INFO +2 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/pyproject.toml +2 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim/__init__.py +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/__init__.py +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/_version.py +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/agent.py +59 -1
- utim_cli-2.4.2/utim_cli/architecture_indexer.py +269 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/ask_helper.py +5 -4
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/bootstrap.py +73 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/brain.py +178 -170
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/client_utils.py +172 -164
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/config.py +6 -1
- utim_cli-2.4.2/utim_cli/constants.py +118 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/context_pruner.py +45 -46
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/crawler/engine.py +187 -14
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/harbor.py +212 -201
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/mcp_client.py +359 -359
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/orchestrator.py +245 -106
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/orchestrator_helpers.py +174 -177
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/orchestrator_llm_transport.py +176 -67
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/orchestrator_tool_exec.py +160 -8
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/orchestrator_turn_history.py +191 -50
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/schema.py +257 -257
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/reflection.py +43 -4
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/session.py +340 -340
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/admin_auth.py +6 -0
- utim_cli-2.4.2/utim_cli/server/api_models_db.py +1287 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/auth.py +142 -22
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/captcha.py +74 -7
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/concurrency.py +40 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/db.py +369 -18
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/about.md +6 -6
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/docs.md +761 -761
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/pricing.md +24 -24
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/terms.md +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/email_utils.py +80 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/key_rotator.py +1806 -98
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/logging_config.py +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/micro_batcher.py +343 -343
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/models.py +629 -234
- utim_cli-2.4.2/utim_cli/server/platform_billing.py +217 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/pricing_updater.py +211 -198
- utim_cli-2.4.2/utim_cli/server/provider_balance.py +472 -0
- utim_cli-2.4.2/utim_cli/server/provider_discovery.py +546 -0
- utim_cli-2.4.2/utim_cli/server/provider_router.py +847 -0
- utim_cli-2.4.2/utim_cli/server/rate_limit.py +112 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/router.py +434 -237
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/__init__.py +10 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/admin_db_routes.py +95 -0
- utim_cli-2.4.2/utim_cli/server/routes/admin_providers_routes.py +579 -0
- utim_cli-2.4.2/utim_cli/server/routes/api_gateway_extensions_routes.py +544 -0
- utim_cli-2.4.2/utim_cli/server/routes/api_key_routes.py +117 -0
- utim_cli-2.4.2/utim_cli/server/routes/api_models_admin_routes.py +967 -0
- utim_cli-2.4.2/utim_cli/server/routes/api_models_public_routes.py +2879 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/auth_routes.py +100 -17
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/billing_routes.py +276 -276
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/completion_routes.py +656 -509
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/credit_routes.py +2 -2
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/feedback_routes.py +106 -88
- utim_cli-2.4.2/utim_cli/server/routes/provider_compat_routes.py +575 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/quota_routes.py +165 -98
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/quota_share_routes.py +768 -768
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/rewards_routes.py +95 -62
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/session_routes.py +201 -201
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/share_routes.py +369 -369
- utim_cli-2.4.2/utim_cli/server/routes/wallet_routes.py +193 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/webhook_routes.py +58 -7
- utim_cli-2.4.2/utim_cli/server/signup_guard.py +237 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/utils.py +354 -3
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/state.py +1 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/subagent_manager.py +1621 -1548
- utim_cli-2.4.2/utim_cli/syntax_fix.py +715 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tools.py +1104 -97
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/model_dialog.py +403 -93
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/resume_dialog.py +1 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/tools_dialog.py +9 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/utim.py +355 -63
- utim_cli-2.4.2/utim_cli/voice.py +554 -0
- utim_cli-2.4.2/utim_cli/voice_coding.py +288 -0
- utim_cli-2.4.2/utim_cli/voice_events.py +409 -0
- utim_cli-2.4.2/utim_cli/voice_handler.py +695 -0
- utim_cli-2.4.2/utim_cli/voice_instant.py +233 -0
- utim_cli-2.4.2/utim_cli/voice_orchestrator.py +392 -0
- utim_cli-2.4.2/utim_cli/voice_session.py +964 -0
- utim_cli-2.4.2/utim_cli/voice_stt.py +502 -0
- utim_cli-2.4.2/utim_cli/voice_tts.py +955 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2/utim_cli.egg-info}/PKG-INFO +2 -1
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli.egg-info/SOURCES.txt +24 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli.egg-info/requires.txt +1 -0
- utim_cli-2.4.0/utim_cli/constants.py +0 -50
- utim_cli-2.4.0/utim_cli/server/rate_limit.py +0 -38
- {utim_cli-2.4.0 → utim_cli-2.4.2}/LICENSE +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/MANIFEST.in +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/README.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/setup.cfg +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/setup.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/_chip_test.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/_verify_repro.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/_verify_syntax.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/_wizard_smoke_test.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/auth.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/backup.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/billing.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/blender_agent.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/builtin/skills/utim-guide/SKILL.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/context_inspector.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/crawler/__init__.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/crawler/markdown_distiller.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/dependency_manager.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/doctor.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/evidence_registry.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/local_db.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/logger.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/mcp_clean_wrapper.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/models.txt +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/personalities.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/platform_installer.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/__init__.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/hooks.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/loader.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/manager.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/registry.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/runtime.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/plugins/tool_loader.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/report.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/__init__.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/config.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/context.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/events.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/handlers.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/sdk/sidecar.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/batch_processor.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/cli_auth.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/history.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/local_llm.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/model_agent.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/plan_integrity.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/rewards_engine.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/admin_bonus_routes.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/marketplace_routes.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/routes/security_routes.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/security_alerts.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/server.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/server/storage_nodes.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/share.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/share_tui.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/situational_scoring.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/task_dispatcher.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/task_logger.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tools_grep.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/compression_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/feedback_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/marketplace_app_state.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/marketplace_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/marketplace_layout_engine.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/mcp_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/miniagents_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/personality_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/plugins_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/publish_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/quota_redeem_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/quota_share_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/rewards_tui.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/skills_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/subagents_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/tasks_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/thinking_display.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/tui/update_dialog.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/update_checker.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/utilities.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/utimmodel.txt +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/vector_memory.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/wheel.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli/workspace.py +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli.egg-info/entry_points.txt +0 -0
- {utim_cli-2.4.0 → utim_cli-2.4.2}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.4.2] - 2026-10-13
|
|
4
|
+
|
|
5
|
+
### 🤖 Sub-Agent Swarm Model Configuration & Hardcoded Lock
|
|
6
|
+
- **Swarm Model Settings in `/model`**: Added a dedicated configuration section in the `/model` dialog allowing users to choose the model used across all swarm subagents.
|
|
7
|
+
- **Strict User Lock**: When a specific model is selected, it is hardcoded—the main agent cannot override or change it, even if custom `model_id` parameters are passed to `invoke_subagents`.
|
|
8
|
+
- **Dynamic Auto Mode**: Added a "Let the agent decide (Auto)" option allowing the main agent to dynamically assign models per task or fall back to its active model.
|
|
9
|
+
|
|
10
|
+
### ⚡ Non-Interactive Command Execution (`--hard-skip-perms`)
|
|
11
|
+
- **Hard Skip Permissions**: Added the `--hard-skip-perms` CLI flag, `UTIM_HARD_SKIP_PERMS=1` environment variable, and `/hard-skip-perms` slash command.
|
|
12
|
+
- **Unrestricted Command Execution**: Automatically approves terminal commands without prompting for user confirmation, even for unsafe or destructive commands.
|
|
13
|
+
|
|
14
|
+
### 🐛 Bug Fixes
|
|
15
|
+
- **Premature Halting Fix**: Fixed an issue where the model abruptly stopped if it completed inspection tools and emitted an action promise without executing further mutation tools, now triggering an automatic continuation nudge.
|
|
16
|
+
|
|
17
|
+
## [2.4.1] - 2026-09-18
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
- Fixed the `UnboundLocalError: rotator` crash when resolving failover providers.
|
|
21
|
+
- Fixed an Admin UI bug that zeroed out free model upstream prices in the Model Pricing & Provisioning interface.
|
|
22
|
+
|
|
3
23
|
## [2.4.0] - 2026-09-14
|
|
4
24
|
|
|
5
25
|
### ✨ New Features & Enhancements
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
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
|
|
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
|
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: requests==2.31.0
|
|
24
24
|
Requires-Dist: aiohttp>=3.9.5
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
25
26
|
Requires-Dist: python-dotenv==1.0.1
|
|
26
27
|
Requires-Dist: urllib3==1.26.18
|
|
27
28
|
Requires-Dist: charset-normalizer==3.3.2
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "utim-cli"
|
|
7
|
-
version = "2.4.
|
|
7
|
+
version = "2.4.2"
|
|
8
8
|
|
|
9
9
|
description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
|
|
10
10
|
readme = "README.md"
|
|
@@ -25,6 +25,7 @@ classifiers = [
|
|
|
25
25
|
dependencies = [
|
|
26
26
|
"requests==2.31.0",
|
|
27
27
|
"aiohttp>=3.9.5",
|
|
28
|
+
"httpx>=0.25.0",
|
|
28
29
|
"python-dotenv==1.0.1",
|
|
29
30
|
"urllib3==1.26.18",
|
|
30
31
|
"charset-normalizer==3.3.2",
|
|
@@ -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.4.
|
|
17
|
+
__version__ = "2.4.2"
|
|
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
|
|
@@ -3,6 +3,7 @@ import json
|
|
|
3
3
|
import sys
|
|
4
4
|
import time
|
|
5
5
|
from .tools import get_tools
|
|
6
|
+
from .syntax_fix import attempt_syntax_repair
|
|
6
7
|
from rich.console import Console
|
|
7
8
|
from rich.live import Live
|
|
8
9
|
from rich.spinner import Spinner
|
|
@@ -293,7 +294,20 @@ class ReActAgent:
|
|
|
293
294
|
num_lines = math.ceil(getattr(self, "_last_running_indicator_len", 40) / max(1, width))
|
|
294
295
|
for _ in range(num_lines):
|
|
295
296
|
self.console.print("\033[F\033[K", end="")
|
|
296
|
-
|
|
297
|
+
|
|
298
|
+
# Auto-repair: if the tool failed due to a syntax error in the
|
|
299
|
+
# generated code, attempt an "on the go" syntax fix before
|
|
300
|
+
# reporting the error.
|
|
301
|
+
error_msg = str(e)
|
|
302
|
+
repaired = None
|
|
303
|
+
try:
|
|
304
|
+
repaired = attempt_syntax_repair(error_msg, self.console)
|
|
305
|
+
except Exception:
|
|
306
|
+
repaired = None
|
|
307
|
+
if repaired:
|
|
308
|
+
return repaired
|
|
309
|
+
|
|
310
|
+
return f"Error executing {tool_name}: {error_msg}"
|
|
297
311
|
|
|
298
312
|
|
|
299
313
|
def run(self, max_iterations: int = 500, show_tools: bool = True):
|
|
@@ -440,6 +454,15 @@ class ReActAgent:
|
|
|
440
454
|
"tool_calls": tool_calls,
|
|
441
455
|
})
|
|
442
456
|
|
|
457
|
+
# Clear any stale vision images and videos from a previous iteration
|
|
458
|
+
try:
|
|
459
|
+
import utim_cli.tools as _tools_module
|
|
460
|
+
_tools_module._pending_vision_images.clear()
|
|
461
|
+
_tools_module._pending_vision_videos.clear()
|
|
462
|
+
_tools_module._active_model_id = self.model_id
|
|
463
|
+
except Exception:
|
|
464
|
+
pass
|
|
465
|
+
|
|
443
466
|
# Execute each tool call
|
|
444
467
|
for i, tc in enumerate(tool_calls):
|
|
445
468
|
result = self._execute_tool_call(tc)
|
|
@@ -451,6 +474,41 @@ class ReActAgent:
|
|
|
451
474
|
"tool_call_id": tc_id,
|
|
452
475
|
"content": result,
|
|
453
476
|
})
|
|
477
|
+
|
|
478
|
+
# Drain and inject vision images & native videos into conversation
|
|
479
|
+
try:
|
|
480
|
+
import utim_cli.tools as _tools_module
|
|
481
|
+
_vision_queue = getattr(_tools_module, "_pending_vision_images", [])
|
|
482
|
+
for _vimg in list(_vision_queue or []):
|
|
483
|
+
self.messages.append({
|
|
484
|
+
"role": "user",
|
|
485
|
+
"content": [
|
|
486
|
+
{"type": "text", "text": _vimg.get("group_header") or f"[{_vimg.get('meta', 'Image')}]"},
|
|
487
|
+
{
|
|
488
|
+
"type": "image_url",
|
|
489
|
+
"image_url": {"url": f"data:{_vimg['mime']};base64,{_vimg['b64']}"},
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
})
|
|
493
|
+
if _vision_queue:
|
|
494
|
+
_vision_queue.clear()
|
|
495
|
+
|
|
496
|
+
_video_queue = getattr(_tools_module, "_pending_vision_videos", [])
|
|
497
|
+
for _vent in list(_video_queue or []):
|
|
498
|
+
self.messages.append({
|
|
499
|
+
"role": "user",
|
|
500
|
+
"content": [
|
|
501
|
+
{"type": "text", "text": _vent.get("header") or f"[Video: {_vent.get('filepath', '')}]"},
|
|
502
|
+
{
|
|
503
|
+
"type": "video_url",
|
|
504
|
+
"video_url": {"url": f"data:{_vent['mime']};base64,{_vent['b64']}"},
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
})
|
|
508
|
+
if _video_queue:
|
|
509
|
+
_video_queue.clear()
|
|
510
|
+
except Exception:
|
|
511
|
+
pass
|
|
454
512
|
else:
|
|
455
513
|
self.console.print(f"\n[bold yellow]Agent paused after reaching maximum iterations ({max_iterations}).[/bold yellow]")
|
|
456
514
|
self.console.print("[dim]You can type 'continue' to resume the task.[/dim]\n")
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
import hashlib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import ast
|
|
7
|
+
|
|
8
|
+
def get_project_hash(project_path: str) -> str:
|
|
9
|
+
return hashlib.sha256(os.path.abspath(project_path).encode('utf-8')).hexdigest()[:12]
|
|
10
|
+
|
|
11
|
+
def _ignore_dir(dname: str) -> bool:
|
|
12
|
+
ignored = {'.git', 'node_modules', 'venv', '.venv', '__pycache__', 'dist', 'build', '.next', '.utim'}
|
|
13
|
+
return dname in ignored
|
|
14
|
+
|
|
15
|
+
def index_python_file(filepath: Path) -> dict:
|
|
16
|
+
try:
|
|
17
|
+
content = filepath.read_text(encoding='utf-8', errors='ignore')
|
|
18
|
+
tree = ast.parse(content)
|
|
19
|
+
imports = []
|
|
20
|
+
symbols = []
|
|
21
|
+
lines = content.split('\n')
|
|
22
|
+
|
|
23
|
+
for node in ast.walk(tree):
|
|
24
|
+
if isinstance(node, ast.Import):
|
|
25
|
+
for alias in node.names: imports.append(alias.name)
|
|
26
|
+
elif isinstance(node, ast.ImportFrom):
|
|
27
|
+
if node.module: imports.append(node.module)
|
|
28
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
29
|
+
docstring = ast.get_docstring(node)
|
|
30
|
+
args = [a.arg for a in node.args.args]
|
|
31
|
+
sig = f"({', '.join(args)})"
|
|
32
|
+
start = max(0, node.lineno - 1)
|
|
33
|
+
end = min(len(lines), start + 6)
|
|
34
|
+
snippet = "\n".join(lines[start:end])
|
|
35
|
+
if len(lines) > end: snippet += "\n # ..."
|
|
36
|
+
symbols.append({"type": "function", "name": node.name, "line": node.lineno, "docstring": docstring, "signature": sig, "snippet": snippet})
|
|
37
|
+
elif isinstance(node, ast.ClassDef):
|
|
38
|
+
docstring = ast.get_docstring(node)
|
|
39
|
+
start = max(0, node.lineno - 1)
|
|
40
|
+
end = min(len(lines), start + 5)
|
|
41
|
+
snippet = "\n".join(lines[start:end])
|
|
42
|
+
if len(lines) > end: snippet += "\n # ..."
|
|
43
|
+
symbols.append({"type": "class", "name": node.name, "line": node.lineno, "docstring": docstring, "snippet": snippet})
|
|
44
|
+
return {"imports": list(set(imports)), "symbols": symbols}
|
|
45
|
+
except Exception:
|
|
46
|
+
return {"imports": [], "symbols": []}
|
|
47
|
+
|
|
48
|
+
def index_js_ts_file(filepath: Path) -> dict:
|
|
49
|
+
try:
|
|
50
|
+
content = filepath.read_text(encoding='utf-8', errors='ignore')
|
|
51
|
+
imports = re.findall(r'import\s+.*?from\s+[\'"](.*?)[\'"]', content)
|
|
52
|
+
symbols = []
|
|
53
|
+
lines = content.split('\n')
|
|
54
|
+
for i, line in enumerate(lines):
|
|
55
|
+
match = re.search(r'(?:export\s+)?(?:const|let|var|function|class)\s+([a-zA-Z0-9_]+)', line)
|
|
56
|
+
if match:
|
|
57
|
+
name = match.group(1)
|
|
58
|
+
snippet = "\n".join(lines[i:min(len(lines), i+6)])
|
|
59
|
+
if len(lines) > i+6: snippet += "\n // ..."
|
|
60
|
+
symbols.append({"type": "export", "name": name, "line": i+1, "snippet": snippet, "signature": ""})
|
|
61
|
+
return {"imports": list(set(imports)), "symbols": symbols}
|
|
62
|
+
except Exception:
|
|
63
|
+
return {"imports": [], "symbols": []}
|
|
64
|
+
|
|
65
|
+
def index_generic_file(filepath: Path) -> dict:
|
|
66
|
+
try:
|
|
67
|
+
content = filepath.read_text(encoding='utf-8', errors='ignore')
|
|
68
|
+
symbols = []
|
|
69
|
+
lines = content.split('\n')
|
|
70
|
+
for i, line in enumerate(lines):
|
|
71
|
+
s_line = line.strip()
|
|
72
|
+
name = None
|
|
73
|
+
|
|
74
|
+
# 1. Standard OOP / Procedural / DevOps / Smart Contracts
|
|
75
|
+
m1 = re.search(r'^(?:public |private |protected |export |func |fn |class |struct |interface |def |sub |function |contract |resource |module )\s*([a-zA-Z0-9_]+)\s*(?:\{|\(|:|"|\')', s_line)
|
|
76
|
+
# 2. SQL Data Definition
|
|
77
|
+
m2 = re.search(r'^CREATE\s+(?:TABLE|PROCEDURE|VIEW|FUNCTION|TRIGGER)\s+([a-zA-Z0-9_]+)', s_line, re.IGNORECASE)
|
|
78
|
+
# 3. Bash / Shell scripts
|
|
79
|
+
m3 = re.search(r'^([a-zA-Z0-9_]+)\s*\(\)\s*\{', s_line)
|
|
80
|
+
# 4. Lisp / Clojure / Scheme
|
|
81
|
+
m4 = re.search(r'^\(\s*def[a-zA-Z0-9_]*\s+([a-zA-Z0-9_\-]+)', s_line)
|
|
82
|
+
|
|
83
|
+
if m1: name = m1.group(1)
|
|
84
|
+
elif m2: name = m2.group(1)
|
|
85
|
+
elif m3: name = m3.group(1)
|
|
86
|
+
elif m4: name = m4.group(1)
|
|
87
|
+
|
|
88
|
+
if name and len(name) > 2 and name not in ("if", "for", "while", "switch", "catch", "return", "else", "elif"):
|
|
89
|
+
snippet = "\n".join(lines[i:min(len(lines), i+6)])
|
|
90
|
+
if len(lines) > i+6: snippet += "\n // ..."
|
|
91
|
+
symbols.append({"type": "symbol", "name": name, "line": i+1, "snippet": snippet, "signature": ""})
|
|
92
|
+
return {"imports": [], "symbols": symbols}
|
|
93
|
+
except Exception:
|
|
94
|
+
return {"imports": [], "symbols": []}
|
|
95
|
+
|
|
96
|
+
def architecture_lock(project_path: str = ".") -> str:
|
|
97
|
+
import uuid
|
|
98
|
+
from utim_cli.brain import get_current_resolved_project
|
|
99
|
+
abs_path = os.path.abspath(project_path)
|
|
100
|
+
if not os.path.isdir(abs_path):
|
|
101
|
+
return f"Error: Project path '{abs_path}' does not exist or is not a directory."
|
|
102
|
+
|
|
103
|
+
proj_id, proj_dir = get_current_resolved_project(abs_path)
|
|
104
|
+
arch_hash = uuid.uuid4().hex[:8]
|
|
105
|
+
arch_dir = proj_dir / f"arch_{arch_hash}"
|
|
106
|
+
arch_dir.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
brain_file = arch_dir / "00_architecture_map.json"
|
|
108
|
+
|
|
109
|
+
inventory = []
|
|
110
|
+
project_type = []
|
|
111
|
+
|
|
112
|
+
# Generic stack identification heuristics
|
|
113
|
+
if (Path(abs_path) / "package.json").exists(): project_type.append("Node.js")
|
|
114
|
+
if (Path(abs_path) / "requirements.txt").exists() or (Path(abs_path) / "pyproject.toml").exists(): project_type.append("Python")
|
|
115
|
+
if (Path(abs_path) / "go.mod").exists(): project_type.append("Go")
|
|
116
|
+
if (Path(abs_path) / "Cargo.toml").exists(): project_type.append("Rust")
|
|
117
|
+
if (Path(abs_path) / "composer.json").exists(): project_type.append("PHP")
|
|
118
|
+
if (Path(abs_path) / "Gemfile").exists(): project_type.append("Ruby")
|
|
119
|
+
|
|
120
|
+
# Known binaries to skip indexing completely
|
|
121
|
+
BINARY_EXTS = {
|
|
122
|
+
".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".mp4", ".mp3", ".wav",
|
|
123
|
+
".zip", ".tar", ".gz", ".rar", ".7z", ".exe", ".dll", ".so", ".dylib",
|
|
124
|
+
".bin", ".dat", ".db", ".sqlite", ".pyc", ".class", ".o", ".a", ".lib",
|
|
125
|
+
".pdf", ".doc", ".docx", ".xls", ".xlsx"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for root, dirs, files in os.walk(abs_path):
|
|
129
|
+
dirs[:] = [d for d in dirs if not _ignore_dir(d)]
|
|
130
|
+
for f in files:
|
|
131
|
+
if f.startswith('.'): continue
|
|
132
|
+
fpath = Path(root) / f
|
|
133
|
+
rel_path = str(fpath.relative_to(abs_path)).replace('\\', '/')
|
|
134
|
+
|
|
135
|
+
file_data = {
|
|
136
|
+
"file": rel_path,
|
|
137
|
+
"size": fpath.stat().st_size if fpath.exists() else 0,
|
|
138
|
+
"extension": fpath.suffix,
|
|
139
|
+
"relationships": []
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Universal Language Routing
|
|
143
|
+
if fpath.suffix == ".py":
|
|
144
|
+
data = index_python_file(fpath)
|
|
145
|
+
elif fpath.suffix in (".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"):
|
|
146
|
+
data = index_js_ts_file(fpath)
|
|
147
|
+
elif fpath.suffix.lower() not in BINARY_EXTS:
|
|
148
|
+
data = index_generic_file(fpath)
|
|
149
|
+
else:
|
|
150
|
+
data = {"imports": [], "symbols": []}
|
|
151
|
+
|
|
152
|
+
file_data["imports"] = data["imports"]
|
|
153
|
+
file_data["symbols"] = data["symbols"]
|
|
154
|
+
inventory.append(file_data)
|
|
155
|
+
|
|
156
|
+
nodes = []
|
|
157
|
+
edges = []
|
|
158
|
+
nodes.append({"id": "PROJECT_ROOT", "type": "project", "path": abs_path})
|
|
159
|
+
|
|
160
|
+
def get_cluster_name(file_path: str) -> str:
|
|
161
|
+
parts = file_path.replace('\\', '/').split('/')
|
|
162
|
+
ignore_dirs = {'src', 'lib', 'app', 'main', 'bin', 'scripts', 'tests', 'test', 'public', 'assets', 'static', 'dist', 'build'}
|
|
163
|
+
|
|
164
|
+
# 1. Find the most meaningful parent directory
|
|
165
|
+
for part in reversed(parts[:-1]):
|
|
166
|
+
clean_part = part.lower().strip()
|
|
167
|
+
if clean_part and clean_part not in ignore_dirs:
|
|
168
|
+
return clean_part
|
|
169
|
+
|
|
170
|
+
# 2. Fallback to filename if it's unique
|
|
171
|
+
filename = parts[-1].split('.')[0].lower()
|
|
172
|
+
if filename not in {'index', 'main', 'app', 'core'}:
|
|
173
|
+
return filename
|
|
174
|
+
|
|
175
|
+
return "core"
|
|
176
|
+
|
|
177
|
+
clusters = {}
|
|
178
|
+
|
|
179
|
+
for item in inventory:
|
|
180
|
+
file_id = item["file"]
|
|
181
|
+
nodes.append({"id": file_id, "type": "file", "extension": item.get("extension"), "size": item.get("size")})
|
|
182
|
+
edges.append({"source": "PROJECT_ROOT", "target": file_id, "relation": "contains"})
|
|
183
|
+
|
|
184
|
+
for imp in item.get("imports", []):
|
|
185
|
+
nodes.append({"id": imp, "type": "module"})
|
|
186
|
+
edges.append({"source": file_id, "target": imp, "relation": "imports"})
|
|
187
|
+
|
|
188
|
+
cluster_name = get_cluster_name(file_id)
|
|
189
|
+
if cluster_name not in clusters:
|
|
190
|
+
clusters[cluster_name] = []
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
content = (Path(abs_path) / file_id).read_text(encoding='utf-8', errors='ignore')
|
|
194
|
+
content_lower = content.lower()
|
|
195
|
+
file_lower = file_id.lower()
|
|
196
|
+
if "if __name__ ==" in content or "int main(" in content or "func main(" in content or "app.listen" in content_lower:
|
|
197
|
+
clusters[cluster_name].append(f"### File: `{file_id}` (Entrypoint/Server)\n*This file contains application startup or core routing.*\n")
|
|
198
|
+
except:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
for sym in item.get("symbols", []):
|
|
202
|
+
sym_id = f"{file_id}::{sym['name']}"
|
|
203
|
+
nodes.append({"id": sym_id, "type": sym["type"], "name": sym["name"], "line": sym.get("line")})
|
|
204
|
+
edges.append({"source": file_id, "target": sym_id, "relation": "defines"})
|
|
205
|
+
|
|
206
|
+
name = sym.get("name", "")
|
|
207
|
+
doc = sym.get("docstring") or ""
|
|
208
|
+
snippet = sym.get("snippet") or ""
|
|
209
|
+
sig = sym.get("signature", "")
|
|
210
|
+
|
|
211
|
+
md_block = f"### `{name}{sig}` in `{file_id}`\n"
|
|
212
|
+
if doc: md_block += f"**Docstring:** {doc}\n"
|
|
213
|
+
md_block += f"```text\n{snippet}\n```\n"
|
|
214
|
+
clusters[cluster_name].append(md_block)
|
|
215
|
+
|
|
216
|
+
# Dynamically generate markdown files based on organic clusters
|
|
217
|
+
generated_mds = []
|
|
218
|
+
for cname, blocks in clusters.items():
|
|
219
|
+
if not blocks: continue
|
|
220
|
+
safe_cname = re.sub(r'[^a-z0-9_]', '_', cname)
|
|
221
|
+
md_content = f"# 🧩 Architecture Component: {cname.title()}\n\n" + "\n".join(blocks)
|
|
222
|
+
md_filename = f"component_{safe_cname}.md"
|
|
223
|
+
(arch_dir / md_filename).write_text(md_content, encoding="utf-8")
|
|
224
|
+
generated_mds.append(md_filename)
|
|
225
|
+
|
|
226
|
+
domain_links = ", ".join([
|
|
227
|
+
"[file:///{}]".format(str(arch_dir.absolute() / m).replace(chr(92), "/"))
|
|
228
|
+
for m in generated_mds
|
|
229
|
+
])
|
|
230
|
+
conventions = [
|
|
231
|
+
"## Innate Project Architecture Rules",
|
|
232
|
+
"- **Organic Component Isolation**: The project is structured organically. Do not bloat central routers or core files.",
|
|
233
|
+
"- **Multifile Designs**: When adding large new features, ALWAYS create new standalone component files based on the existing directory structure.",
|
|
234
|
+
"- **MANDATORY ARCHITECTURE REVIEW**: You MUST use `read_file` on the relevant domain files below BEFORE exploring the source code manually. They contain the exact structural blueprints you need.",
|
|
235
|
+
"- **Available Domain Files**: " + domain_links
|
|
236
|
+
]
|
|
237
|
+
(arch_dir / "conventions_summary.txt").write_text("\n".join(conventions), encoding="utf-8")
|
|
238
|
+
|
|
239
|
+
unique_nodes = {n["id"]: n for n in nodes}.values()
|
|
240
|
+
|
|
241
|
+
graph_data = {
|
|
242
|
+
"project_path": abs_path,
|
|
243
|
+
"project_type": project_type,
|
|
244
|
+
"total_files": len(inventory),
|
|
245
|
+
"nodes": list(unique_nodes),
|
|
246
|
+
"edges": edges,
|
|
247
|
+
"capabilities": ["Project orientation", "Graph traversal", "Symbol resolution"]
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
with open(brain_file, 'w', encoding='utf-8') as bf:
|
|
251
|
+
json.dump(graph_data, bf, indent=2)
|
|
252
|
+
|
|
253
|
+
component_bullets = "\n".join([
|
|
254
|
+
f"- {m}: [file:///{str(arch_dir.absolute() / m).replace(chr(92), '/')}]"
|
|
255
|
+
for m in generated_mds
|
|
256
|
+
]) if generated_mds else "- None"
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
f"✓ Architecture lock successfully generated at: {arch_dir}\n"
|
|
260
|
+
f"Identified stack: {', '.join(project_type) or 'Unknown'} | Indexed files: {len(inventory)}\n\n"
|
|
261
|
+
f"WHAT WAS PROVIDED:\n"
|
|
262
|
+
f"1. Structural Dependency Graph: `00_architecture_map.json` (all file nodes, module imports, and symbol definitions)\n"
|
|
263
|
+
f"2. Architecture Conventions: `conventions_summary.txt`\n"
|
|
264
|
+
f"3. Modular Component Blueprints ({len(generated_mds)} domain modules):\n{component_bullets}\n\n"
|
|
265
|
+
f"WHY THIS IS NEEDED & NEXT ACTION:\n"
|
|
266
|
+
f"- Zero-token structural grounding eliminates hallucinations and token-wasting blind repo exploration.\n"
|
|
267
|
+
f"- NEXT STEP: Use `read_file` on the relevant `component_<domain>.md` blueprint above matching your task to inspect exact function/class signatures and dependencies before reading or editing source code."
|
|
268
|
+
)
|
|
269
|
+
|
|
@@ -5,7 +5,8 @@ A dedicated, lightweight helper miniagent sharing the core UTIM LLM execution ar
|
|
|
5
5
|
Provides real-time project insight, chat explanation, and conversational companionship
|
|
6
6
|
without interrupting the main agent's active execution loop.
|
|
7
7
|
|
|
8
|
-
Uses '
|
|
8
|
+
Uses 'nvidia/nemotron-3.5-lightning:free' as the primary model (first fallback), then falls
|
|
9
|
+
back through all registered free models from the server model_registry.
|
|
9
10
|
Has access to exactly one tool: 'read_chat'.
|
|
10
11
|
"""
|
|
11
12
|
|
|
@@ -27,16 +28,16 @@ from rich import box
|
|
|
27
28
|
|
|
28
29
|
from utim_cli.client_utils import get_free_models_fallback_chain
|
|
29
30
|
|
|
30
|
-
# Primary helper model = '
|
|
31
|
+
# Primary helper model = 'nvidia/nemotron-3.5-lightning:free' (verified free model), then falls
|
|
31
32
|
# back through the verified free models from the server DB registry.
|
|
32
33
|
_HELPER_FALLBACK_CHAIN = get_free_models_fallback_chain()
|
|
33
|
-
PRIMARY_HELPER_MODEL = _HELPER_FALLBACK_CHAIN[0] if _HELPER_FALLBACK_CHAIN else "
|
|
34
|
+
PRIMARY_HELPER_MODEL = _HELPER_FALLBACK_CHAIN[0] if _HELPER_FALLBACK_CHAIN else "nvidia/nemotron-3.5-lightning:free"
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
HELPER_SYSTEM_PROMPT = """<identity>
|
|
38
39
|
You are the UTIM Sidecar Assistant, an embedded technical companion running alongside the active UTIM CLI session.
|
|
39
|
-
- Your identity is strictly the UTIM Sidecar Assistant.
|
|
40
|
+
- Your identity is strictly the UTIM Sidecar Assistant. If asked what model you are running on, state your active model name accurately.
|
|
40
41
|
</identity>
|
|
41
42
|
|
|
42
43
|
Your mission is to provide real-time architectural insights, explain active agent operations, assess codebase states, and answer developer questions clearly and factually.
|
|
@@ -37,6 +37,7 @@ def initialize_utim() -> str:
|
|
|
37
37
|
|
|
38
38
|
# 4. Auto-create skills directory and default skills/rules
|
|
39
39
|
_write_skills_and_agents()
|
|
40
|
+
_scaffold_local_ui_skill()
|
|
40
41
|
|
|
41
42
|
# 5. Sync global/local experience database across project folders
|
|
42
43
|
try:
|
|
@@ -46,11 +47,83 @@ def initialize_utim() -> str:
|
|
|
46
47
|
_sync_global_experience(api_key, os.getcwd())
|
|
47
48
|
except Exception:
|
|
48
49
|
pass
|
|
50
|
+
|
|
51
|
+
# 6. Pre-warm instant phrase voices in background
|
|
52
|
+
try:
|
|
53
|
+
from utim_cli.voice_instant import warm_instant_phrases
|
|
54
|
+
warm_instant_phrases(background=True)
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
49
57
|
|
|
50
58
|
db_path = UTIM_DIR / 'utim_local.db'
|
|
51
59
|
return str(db_path)
|
|
52
60
|
|
|
53
61
|
|
|
62
|
+
def _scaffold_local_ui_skill():
|
|
63
|
+
"""Auto-generate UI design guide in the local workspace if .agents exists or it looks like a project."""
|
|
64
|
+
import os
|
|
65
|
+
from pathlib import Path
|
|
66
|
+
|
|
67
|
+
# Only scaffold if we are in a project directory or .agents already exists
|
|
68
|
+
if not (Path('.git').exists() or Path('.agents').exists() or Path('package.json').exists() or Path('requirements.txt').exists()):
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
skill_dir = Path('.agents/skills/Ui design Guide')
|
|
73
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
skill_md = skill_dir / 'SKILL.md'
|
|
75
|
+
|
|
76
|
+
if not skill_md.exists():
|
|
77
|
+
skill_content = """---
|
|
78
|
+
name: Ui design Guide
|
|
79
|
+
description: Directives and best practices for creating beautiful, modern, and highly polished web interfaces.
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# UI Design Guide
|
|
83
|
+
|
|
84
|
+
When the user asks you to generate something related to design, build a website, or perform a UI overhaul, you MUST apply these modern UI/UX principles to ensure the result is professional, beautiful, and highly polished. Do not settle for default browser styles.
|
|
85
|
+
|
|
86
|
+
## 1. Typography
|
|
87
|
+
- **Fonts**: Use modern, clean sans-serif fonts (e.g., Inter, Roboto, San Francisco, or system-ui).
|
|
88
|
+
- **Hierarchy**: Clearly distinguish between headings (H1, H2) and body text using font weights (e.g., bold headers, regular body) and sizes.
|
|
89
|
+
- **Readability**: Keep body text line-height around `1.5` to `1.6`. Use slightly muted colors for body text (e.g., `#4b5563` on light themes or `#a1a1aa` on dark themes) rather than stark black or pure white.
|
|
90
|
+
|
|
91
|
+
## 2. Layout & Whitespace (Negative Space)
|
|
92
|
+
- **Breathing Room**: Be generous with padding and margins. Do not cram elements together.
|
|
93
|
+
- **Alignment**: Use CSS Flexbox or Grid for perfect alignment. Center things deliberately.
|
|
94
|
+
- **Constraints**: Limit the maximum width of text containers (e.g., `max-w-3xl`) so lines don't stretch too far across wide screens.
|
|
95
|
+
|
|
96
|
+
## 3. Colors & Theme
|
|
97
|
+
- **Backgrounds**: Use soft, off-white (e.g., `#f9fafb`) or rich dark (e.g., `#0f172a`) background colors instead of pure `#ffffff` or `#000000`.
|
|
98
|
+
- **Accents**: Pick a primary brand color (e.g., a vibrant blue or purple) and use it sparingly for primary buttons, active states, and important links.
|
|
99
|
+
- **Gradients & Glassmorphism**: Where appropriate, use subtle background gradients or translucent frosted-glass effects (e.g., `backdrop-filter: blur(10px); background: rgba(255, 255, 255, 0.1);`).
|
|
100
|
+
|
|
101
|
+
## 4. Components & Shapes
|
|
102
|
+
- **Corners**: Use consistent `border-radius` (e.g., `8px` or `12px` for cards/buttons, full pill-shapes for badges).
|
|
103
|
+
- **Shadows**: Add soft, diffuse box-shadows to cards and dropdowns to create depth and elevation. Avoid harsh, dark shadows (e.g., use `box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1)`).
|
|
104
|
+
- **Borders**: If using borders, make them very subtle (e.g., `1px solid rgba(255,255,255,0.1)` or `#e5e7eb`).
|
|
105
|
+
|
|
106
|
+
## 5. Micro-Interactions & States
|
|
107
|
+
- **Transitions**: Apply `transition: all 0.2s ease;` to buttons, links, and interactive elements.
|
|
108
|
+
- **Hover/Active**: Always provide visual feedback when an element is hovered (e.g., slightly lighten/darken the background, or lift the element up by 2px) or clicked.
|
|
109
|
+
- **Focus**: Remove default browser outlines and replace them with custom, aesthetic focus rings.
|
|
110
|
+
|
|
111
|
+
## 6. Responsiveness
|
|
112
|
+
- Ensure all designs are mobile-friendly first.
|
|
113
|
+
- Use responsive units or media queries to collapse grids into single columns on smaller screens.
|
|
114
|
+
- Ensure tap targets (buttons, links) are at least `44px` tall on mobile devices.
|
|
115
|
+
|
|
116
|
+
## Directives
|
|
117
|
+
- **Never use raw unstyled HTML** for a final output unless explicitly requested.
|
|
118
|
+
- **Use modern CSS frameworks** (like Tailwind CSS) if permitted by the project, or write clean, well-structured vanilla CSS using CSS Variables for theming.
|
|
119
|
+
- **Review your design**: Before finalizing the code, ask yourself: "Does this look like a premium modern SaaS product or a 1990s web page?" Adjust accordingly.
|
|
120
|
+
"""
|
|
121
|
+
skill_md.write_text(skill_content, encoding='utf-8')
|
|
122
|
+
|
|
123
|
+
except Exception:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
|
|
54
127
|
def _sync_global_experience(api_key: str, current_folder: str):
|
|
55
128
|
"""
|
|
56
129
|
Track which project folder the user is currently in (server-side record only).
|