utim-cli 2.2.0__tar.gz → 2.2.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.
Files changed (131) hide show
  1. {utim_cli-2.2.0 → utim_cli-2.2.2}/CHANGELOG.md +17 -0
  2. {utim_cli-2.2.0/utim_cli.egg-info → utim_cli-2.2.2}/PKG-INFO +3 -4
  3. {utim_cli-2.2.0 → utim_cli-2.2.2}/pyproject.toml +4 -4
  4. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/__init__.py +1 -1
  5. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_version.py +2 -1
  6. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/bootstrap.py +155 -23
  7. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/brain.py +1 -1
  8. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/client_utils.py +1 -0
  9. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/config.py +2 -1
  10. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/context_pruner.py +470 -40
  11. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/harbor.py +75 -13
  12. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/mcp_client.py +6 -1
  13. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/orchestrator.py +448 -222
  14. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/reflection.py +101 -90
  15. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/auth.py +9 -3
  16. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/cli_auth.py +29 -0
  17. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/db.py +62 -1
  18. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/refund.md +2 -2
  19. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/email_utils.py +182 -0
  20. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/model_agent.py +90 -75
  21. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/models.py +218 -72
  22. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/pricing_updater.py +4 -8
  23. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/rewards_engine.py +4 -2
  24. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/router.py +176 -3
  25. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/__init__.py +6 -1
  26. utim_cli-2.2.2/utim_cli/server/routes/billing_routes.py +262 -0
  27. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/completion_routes.py +10 -1
  28. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/credit_routes.py +33 -1
  29. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/quota_routes.py +48 -0
  30. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/referral_routes.py +11 -2
  31. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/rewards_routes.py +47 -22
  32. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/share_routes.py +18 -12
  33. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/share.py +165 -29
  34. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/share_tui.py +154 -7
  35. utim_cli-2.2.2/utim_cli/subagent_manager.py +724 -0
  36. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tools.py +124 -27
  37. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/feedback_dialog.py +47 -47
  38. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/history_dialog.py +213 -211
  39. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/mcp_dialog.py +16 -14
  40. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/model_dialog.py +137 -158
  41. utim_cli-2.2.2/utim_cli/tui/quota_dialog.py +72 -0
  42. utim_cli-2.2.2/utim_cli/tui/quota_redeem_dialog.py +207 -0
  43. utim_cli-2.2.2/utim_cli/tui/quota_share_dialog.py +446 -0
  44. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/rewards_tui.py +15 -4
  45. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/skills_dialog.py +154 -138
  46. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/subagents_dialog.py +15 -15
  47. utim_cli-2.2.2/utim_cli/tui/tools_dialog.py +273 -0
  48. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/utim.py +53 -84
  49. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/wheel.py +1 -2
  50. {utim_cli-2.2.0 → utim_cli-2.2.2/utim_cli.egg-info}/PKG-INFO +3 -4
  51. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli.egg-info/SOURCES.txt +1 -0
  52. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli.egg-info/requires.txt +0 -1
  53. utim_cli-2.2.0/utim_cli/subagent_manager.py +0 -366
  54. utim_cli-2.2.0/utim_cli/tui/quota_dialog.py +0 -36
  55. utim_cli-2.2.0/utim_cli/tui/quota_redeem_dialog.py +0 -181
  56. utim_cli-2.2.0/utim_cli/tui/quota_share_dialog.py +0 -491
  57. utim_cli-2.2.0/utim_cli/tui/tools_dialog.py +0 -248
  58. {utim_cli-2.2.0 → utim_cli-2.2.2}/LICENSE +0 -0
  59. {utim_cli-2.2.0 → utim_cli-2.2.2}/MANIFEST.in +0 -0
  60. {utim_cli-2.2.0 → utim_cli-2.2.2}/README.md +0 -0
  61. {utim_cli-2.2.0 → utim_cli-2.2.2}/setup.cfg +0 -0
  62. {utim_cli-2.2.0 → utim_cli-2.2.2}/setup.py +0 -0
  63. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_check_obj.py +0 -0
  64. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_find_mcp.py +0 -0
  65. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_find_plan.py +0 -0
  66. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_find_tool.py +0 -0
  67. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_patch_multimodal.py +0 -0
  68. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/_test_regex.py +0 -0
  69. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/agent.py +0 -0
  70. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/auth.py +0 -0
  71. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/backup.py +0 -0
  72. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/billing.py +0 -0
  73. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/blender_agent.py +0 -0
  74. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/constants.py +0 -0
  75. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/doctor.py +0 -0
  76. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/knowledge_graph.py +0 -0
  77. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/local_db.py +0 -0
  78. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/logger.py +0 -0
  79. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/mcp_clean_wrapper.py +0 -0
  80. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/mcp_registry.json +0 -0
  81. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/report.py +0 -0
  82. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/scrapy_search.py +0 -0
  83. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/__init__.py +0 -0
  84. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/attribution.py +0 -0
  85. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/audit_log.py +0 -0
  86. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/batch_processor.py +0 -0
  87. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/captcha.py +0 -0
  88. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  89. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/about.md +0 -0
  90. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/changelog.md +0 -0
  91. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/docs.md +0 -0
  92. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/features.md +0 -0
  93. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/license.md +0 -0
  94. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/pricing.md +0 -0
  95. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/privacy.md +0 -0
  96. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/support.md +0 -0
  97. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/docs_md/terms.md +0 -0
  98. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/exchange_rate.py +0 -0
  99. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/firebase.py +0 -0
  100. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/fix_duplicate_users.py +0 -0
  101. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/history.py +0 -0
  102. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/local_llm.py +0 -0
  103. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/logging_config.py +0 -0
  104. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/micro_batcher.py +0 -0
  105. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/provision_build.py +0 -0
  106. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/rate_limit.py +0 -0
  107. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/auth_routes.py +0 -0
  108. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/feedback_routes.py +0 -0
  109. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/marketplace_routes.py +0 -0
  110. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/quota_share_routes.py +0 -0
  111. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/security_routes.py +0 -0
  112. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/routes/session_routes.py +0 -0
  113. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/server.py +0 -0
  114. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/server/storage_nodes.py +0 -0
  115. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/situational_scoring.py +0 -0
  116. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/state.py +0 -0
  117. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/__init__.py +0 -0
  118. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/marketplace_app_state.py +0 -0
  119. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/marketplace_dialog.py +0 -0
  120. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  121. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/miniagents_dialog.py +0 -0
  122. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/publish_dialog.py +0 -0
  123. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/resume_dialog.py +0 -0
  124. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/thinking_display.py +0 -0
  125. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/tui/update_dialog.py +0 -0
  126. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/utilities.py +0 -0
  127. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/vector_memory.py +0 -0
  128. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli/workspace.py +0 -0
  129. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli.egg-info/dependency_links.txt +0 -0
  130. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli.egg-info/entry_points.txt +0 -0
  131. {utim_cli-2.2.0 → utim_cli-2.2.2}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.2] - 2026-08-15
4
+
5
+ ### 🚀 Features & Enhancements
6
+ - **Order History & Invoices**: Added comprehensive order history and tax invoice viewing with printable receipts on the pricing page.
7
+ - **Automated Refund Request Workflow**: Policy-enforced refund claim submission system with automatic admin email alerts and user autoresponder (3-day money-back guarantee window).
8
+ - **Zero-Flicker Configuration Dialogs**: Upgraded Tools, Model Settings, and Skills configuration dialogs to in-memory live toggling with single-commit on exit, eliminating screen flashes.
9
+ - **TUI Dialog Polish**: Refined `/rewind`, `/undo`, `/quotashare`, and `/redeem` interactive dialog workflows.
10
+
11
+ ## [2.2.1] - 2026-08-13
12
+
13
+ ### 🛠️ Fixes & Minor Polish
14
+ - **TUI Input Divider Line**: Updated input area horizontal divider line to extend full width across the terminal screen (`shutil.get_terminal_size().columns`) with line-wrap protection.
15
+ - **Subagent Swarm Visualizer Mobile Responsiveness**: Added mobile responsive breakpoint styles (`.st-swarm-canvas`), card container bounds, text truncation, and flex badge anchors (`DISPATCHER`, `DONE`, etc.) to prevent layout overflow on mobile screens.
16
+ - **OS-Specific Requirements Architecture**: Added dedicated lightweight requirement files (`requirements_android.txt`, `requirements_windows.txt`, `requirements_desktop.txt`, `requirements_full.txt`) and updated installer scripts (`install.sh`, `setup_utim1.py`) to select pure-Python requirements for Android/Termux without PyPI binary wheel dependencies.
17
+ - **Reflection Pipeline Primary Model**: Updated reflection and experience extraction model fallback chain to use `openrouter/free` as the primary model (`REFLECTION_PRIMARY_MODEL`).
18
+ - **Correction Fast-Path Engine**: Removed keyword-matching gate on correction detection, delegating judgment directly to the reflection LLM for zero false negatives.
19
+
3
20
  ## [2.2.0] - 2026-08-13
4
21
 
5
22
  ### 🛠️ Fixes
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
5
- License: MIT
5
+ License: Emend AI Proprietary EULA
6
6
  Project-URL: Homepage, https://utim.dev
7
7
  Project-URL: Documentation, https://utim.dev/docs
8
8
  Project-URL: Changelog, https://github.com/emendai/utim/blob/main/CHANGELOG.md
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
- Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: License :: Other/Proprietary License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Development Status :: 5 - Production/Stable
18
18
  Classifier: Environment :: Console
@@ -31,7 +31,6 @@ 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
35
34
  Provides-Extra: search
36
35
  Requires-Dist: scrapy>=2.11.0; extra == "search"
37
36
  Requires-Dist: scrapy-playwright>=0.0.34; extra == "search"
@@ -4,18 +4,18 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "2.2.0"
7
+ version = "2.2.2"
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"
11
- license = { text = "MIT" }
11
+ license = { text = "Emend AI Proprietary EULA" }
12
12
  classifiers = [
13
13
  "Programming Language :: Python :: 3",
14
14
  "Programming Language :: Python :: 3.9",
15
15
  "Programming Language :: Python :: 3.10",
16
16
  "Programming Language :: Python :: 3.11",
17
17
  "Programming Language :: Python :: 3.12",
18
- "License :: OSI Approved :: MIT License",
18
+ "License :: Other/Proprietary License",
19
19
  "Operating System :: OS Independent",
20
20
  "Development Status :: 5 - Production/Stable",
21
21
  "Environment :: Console",
@@ -33,7 +33,6 @@ 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",
37
36
  ]
38
37
 
39
38
  [project.urls]
@@ -104,4 +103,5 @@ markers = [
104
103
  "smoke: fast install validation tests (no live credentials required)",
105
104
  "slow: tests that make network calls or take more than 10 seconds",
106
105
  "integration: tests requiring live credentials or a running UTIM account",
106
+ "benchmark: UTIM benchmark suite — tests all 9 agent benchmark families",
107
107
  ]
@@ -5,7 +5,7 @@ try:
5
5
  from utim_cli._version import VERSION as __version__
6
6
  except ImportError:
7
7
  # Fallback during first-install / editable-install edge cases
8
- __version__ = "2.2.0"
8
+ __version__ = "2.2.2"
9
9
 
10
10
  import builtins, os, pathlib, urllib.parse, re
11
11
 
@@ -11,4 +11,5 @@
11
11
  # from here so every surface stays in sync automatically.
12
12
  # ─────────────────────────────────────────────────────────────────────────────
13
13
 
14
- VERSION = "2.2.0"
14
+ VERSION = "2.2.2"
15
+
@@ -146,13 +146,15 @@ def scan_available_skills():
146
146
 
147
147
  skills = {}
148
148
  paths_to_check = [
149
+ Path('.agents/skills'),
150
+ Path('.agentskills'),
151
+ Path('.agents'),
152
+ Path('.utim/skills'),
153
+ Path('.utim/agentskills'),
149
154
  UTIM_DIR / 'skills',
150
155
  UTIM_DIR / 'agentskills',
151
156
  Path.home() / '.utim' / 'skills',
152
157
  Path.home() / '.utim' / 'agentskills',
153
- Path('.utim/skills'),
154
- Path('.utim/agentskills'),
155
- Path('.agents/skills'),
156
158
  ]
157
159
 
158
160
  for base_dir in paths_to_check:
@@ -216,28 +218,156 @@ def scan_available_skills():
216
218
  }
217
219
  return skills
218
220
 
221
+
222
+ DOMAIN_INTENT_MAP = {
223
+ "terminal-ui-design": [
224
+ "tui", "rich", "console", "prompt_toolkit", "layout", "dialog", "menu",
225
+ "scrollbar", "terminal ui", "cli ui", "ansi", "color scheme", "quotashare", "redeem"
226
+ ],
227
+ "software-architecture": [
228
+ "architecture", "refactor", "modular", "decouple", "state management",
229
+ "router", "fastapi", "sqlite", "orm", "dependency injection", "package structure", "module design"
230
+ ],
231
+ "mcp-server-development": [
232
+ "mcp", "model context protocol", "mcp server", "stdio", "json-rpc", "jsonrpc",
233
+ "connection pool", "protocol wrapper", "tool handler", "tool server"
234
+ ],
235
+ "async-python": [
236
+ "async", "asyncio", "coroutine", "event loop", "threadsafe", "subprocess stream",
237
+ "deadlock", "lock", "thread", "concurrent", "non-blocking", "await", "to_thread"
238
+ ],
239
+ "llm-orchestration": [
240
+ "orchestrat", "llm loop", "prompt", "context prun", "compaction", "rag",
241
+ "token budget", "overclock", "memory retrieval", "react loop", "reflection", "system prompt",
242
+ "compression", "context envelope", "modelcontext"
243
+ ],
244
+ "cli-ux-patterns": [
245
+ "cli ux", "user confirmation", "dry-run", "undo", "scroll pollution",
246
+ "tty", "non-interactive", "cli prompt", "command line experience"
247
+ ],
248
+ "web-design-premium": [
249
+ "web design", "html", "css", "vite", "react", "frontend", "glassmorphism",
250
+ "landing page", "tailwind", "responsive", "hsl", "seo"
251
+ ],
252
+ "creative-scrollytelling": [
253
+ "scrollytelling", "kinetic", "split-door", "viewport animation", "kinetic sweep"
254
+ ],
255
+ }
256
+
257
+
258
+ def rank_relevant_skills(
259
+ query_text: str,
260
+ skills: dict = None,
261
+ min_score: float = 14.0,
262
+ max_results: int = 2,
263
+ ) -> list:
264
+ """
265
+ High-precision BM25 & Domain-Intent Skill Ranker.
266
+
267
+ Prevents weak/random skill injection by evaluating:
268
+ 1. Domain Intent Triggers: Matches architectural problem domains to canonical skills.
269
+ 2. Exact Phrase & Slug Matching: High-weight bonus for exact multi-word / hyphenated concepts.
270
+ 3. Inverse Document Frequency (IDF): Suppresses generic words ('python', 'code', 'data', 'user')
271
+ and elevates domain-specific technical terminology ('coroutine', 'prompt_toolkit', 'mcp').
272
+ 4. Minimum Activation Threshold: Rejects any skill below min_score to prevent context pollution.
273
+
274
+ Returns:
275
+ List of (skill_name, score) sorted in descending order of relevance.
276
+ """
277
+ import math
278
+ import re
279
+
280
+ if not query_text or not query_text.strip():
281
+ return []
282
+
283
+ if skills is None:
284
+ skills = scan_available_skills()
285
+ if not skills:
286
+ return []
287
+
288
+ query_lower = query_text.lower()
289
+ query_tokens = [w for w in re.split(r"[^\w\d\-]+", query_lower) if len(w) >= 3]
290
+ if not query_tokens:
291
+ return []
292
+
293
+ # 1. Compute document frequency across all skills to calculate IDF
294
+ total_docs = len(skills)
295
+ doc_freq = {}
296
+ for s_name, s_info in skills.items():
297
+ doc_words = set(s_info.get("keywords", []))
298
+ for w in s_info.get("name", "").lower().split():
299
+ if len(w) >= 3:
300
+ doc_words.add(w)
301
+ for w in doc_words:
302
+ doc_freq[w] = doc_freq.get(w, 0) + 1
303
+
304
+ def _idf(word: str) -> float:
305
+ df = doc_freq.get(word, 0)
306
+ # Suppress over-represented words across >= 12 skills
307
+ if df >= 12:
308
+ return 0.1
309
+ return max(0.2, math.log((total_docs - df + 0.5) / (df + 0.5) + 1.0))
310
+
311
+ scored_skills = []
312
+
313
+ for skill_name, skill_info in skills.items():
314
+ score = 0.0
315
+ s_name_lower = skill_name.lower()
316
+ s_title_lower = skill_info.get("name", "").lower()
317
+ s_desc_lower = skill_info.get("description", "").lower()
318
+
319
+ # Extract token sets to ensure exact word matches (prevent substring false-positives like 'tell' in 'scrollytelling')
320
+ s_name_tokens = set(re.split(r"[^\w\d]+", s_name_lower))
321
+ s_title_tokens = set(re.split(r"[^\w\d]+", s_title_lower))
322
+ s_desc_tokens = set(re.split(r"[^\w\d]+", s_desc_lower))
323
+
324
+ # ── Check 1: Domain Intent Mapping (Authoritative Trigger Boost) ──
325
+ for target_skill, triggers in DOMAIN_INTENT_MAP.items():
326
+ if target_skill in s_name_lower:
327
+ for trig in triggers:
328
+ if re.search(r"\b" + re.escape(trig) + r"\b", query_lower):
329
+ score += 35.0
330
+ break
331
+
332
+ # ── Check 2: Exact Name / Hyphenated Slug Matching ───────────────
333
+ if re.search(r"\b" + re.escape(s_name_lower) + r"\b", query_lower):
334
+ score += 40.0
335
+ elif s_name_lower.replace("-", " ") in query_lower or s_name_lower.replace("_", " ") in query_lower:
336
+ score += 30.0
337
+
338
+ # ── Check 3: High-Entropy Keyword TF-IDF Matching ─────────────────
339
+ skill_kw = set(skill_info.get("keywords", []))
340
+ for tok in query_tokens:
341
+ if tok in skill_kw:
342
+ score += 8.0 * _idf(tok)
343
+ if tok in s_name_tokens or tok in s_title_tokens:
344
+ score += 6.0 * _idf(tok)
345
+ elif tok in s_desc_tokens:
346
+ score += 2.0 * _idf(tok)
347
+
348
+ # ── Check 4: Penalize Generic Auto-Learned Skills ────────────────
349
+ if any(prefix in s_name_lower for prefix in ["user-", "user_", "communication", "interaction", "conversation", "response-"]):
350
+ # Only allow if strongly and explicitly triggered
351
+ score *= 0.50
352
+
353
+ if score >= min_score:
354
+ scored_skills.append((skill_name, score))
355
+
356
+ scored_skills.sort(key=lambda x: x[1], reverse=True)
357
+ return scored_skills[:max_results]
358
+
359
+
219
360
  def get_rag_context(user_prompt: str = "") -> str:
220
- """Get RAG context string for system prompt injection by matching user_prompt keywords to skills."""
361
+ """Get RAG context string for system prompt injection by matching user_prompt using rank_relevant_skills."""
221
362
  try:
222
363
  from utim_cli.state import STATE
223
364
  STATE["injected_contexts"] = []
224
365
 
225
366
  skills = scan_available_skills()
226
- matched_skills = []
227
- user_prompt_lower = user_prompt.lower()
367
+ ranked = rank_relevant_skills(user_prompt, skills=skills, min_score=14.0, max_results=2)
228
368
 
229
- for skill_name, skill_info in skills.items():
230
- matched = False
231
- if any(kw in user_prompt_lower for kw in skill_info["keywords"]):
232
- matched = True
233
- if not matched:
234
- if skill_name.replace("-", " ") in user_prompt_lower or skill_name in user_prompt_lower:
235
- matched = True
236
- if matched:
237
- matched_skills.append(skill_name)
238
-
239
- # If no keywords matched, default to AGENTS.md
240
- if not matched_skills:
369
+ # If no skills passed threshold, default to AGENTS.md
370
+ if not ranked:
241
371
  agents_path = UTIM_DIR / 'AGENTS.md'
242
372
  if not agents_path.exists():
243
373
  from pathlib import Path
@@ -250,7 +380,7 @@ def get_rag_context(user_prompt: str = "") -> str:
250
380
  return ""
251
381
 
252
382
  context = ""
253
- for skill_name in matched_skills[:2]: # Load top 2 matched skills
383
+ for skill_name, score in ranked:
254
384
  skill_info = skills[skill_name]
255
385
  content = skill_info["content"]
256
386
  if content.startswith('---'):
@@ -258,7 +388,7 @@ def get_rag_context(user_prompt: str = "") -> str:
258
388
  if len(parts) >= 3:
259
389
  content = parts[2].strip()
260
390
  STATE["injected_contexts"].append(content)
261
- context += f"\n### RELEVANT CORE SKILL: {skill_name.upper()} ###\n{content[:1500]}\n"
391
+ context += f"\n### RELEVANT CORE SKILL: {skill_name.upper()} (Relevance: {score:.1f}) ###\n{content[:1500]}\n"
262
392
 
263
393
  return context
264
394
  except Exception as e:
@@ -283,11 +413,13 @@ def get_subagent_rag_context(subagent_name: str, query: str = "") -> str:
283
413
  if skill_name in skills:
284
414
  skill_path = skills[skill_name]["path"]
285
415
  else:
286
- skill_path = UTIM_DIR / 'skills' / skill_name / 'SKILL.md'
416
+ skill_path = Path('.agents/skills') / skill_name / 'SKILL.md'
287
417
  if not skill_path.exists():
288
- skill_path = UTIM_DIR / 'agentskills' / skill_name / 'SKILL.md'
418
+ skill_path = Path('.agentskills') / skill_name / 'SKILL.md'
289
419
  if not skill_path.exists():
290
- skill_path = Path('.agents/skills') / skill_name / 'SKILL.md'
420
+ skill_path = UTIM_DIR / 'skills' / skill_name / 'SKILL.md'
421
+ if not skill_path.exists():
422
+ skill_path = UTIM_DIR / 'agentskills' / skill_name / 'SKILL.md'
291
423
  if not skill_path.exists():
292
424
  return ""
293
425
 
@@ -87,7 +87,7 @@ QWEN_BRAIN_MODEL = "qwen/qwen2.5-1.5b-instruct" # fast reasoning model on
87
87
  try:
88
88
  from utim_cli.reflection import REFLECTION_MODELS
89
89
  except Exception:
90
- REFLECTION_MODELS = [QWEN_BRAIN_MODEL, "openrouter/free"]
90
+ REFLECTION_MODELS = ["openrouter/free", QWEN_BRAIN_MODEL]
91
91
 
92
92
  # ── Paths ──────────────────────────────────────────────────────────────────────
93
93
 
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import json
3
4
  import math
@@ -494,7 +494,8 @@ class Config:
494
494
  return [
495
495
  "cohere/north-mini-code:free",
496
496
  "openrouter/free",
497
- "inclusionai/ling-3.0-flash:free",
497
+ "meta-llama/llama-3.3-70b-instruct:free",
498
+ "google/gemini-2.5-flash:free",
498
499
  ]
499
500
 
500
501