utim-cli 2.2.1__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.1 → utim_cli-2.2.2}/CHANGELOG.md +8 -0
  2. {utim_cli-2.2.1/utim_cli.egg-info → utim_cli-2.2.2}/PKG-INFO +3 -3
  3. {utim_cli-2.2.1 → utim_cli-2.2.2}/pyproject.toml +4 -3
  4. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/__init__.py +1 -1
  5. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_version.py +2 -1
  6. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/bootstrap.py +155 -23
  7. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/client_utils.py +1 -0
  8. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/config.py +2 -1
  9. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/context_pruner.py +470 -40
  10. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/harbor.py +75 -13
  11. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/mcp_client.py +6 -1
  12. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/orchestrator.py +448 -222
  13. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/reflection.py +100 -89
  14. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/auth.py +9 -3
  15. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/cli_auth.py +29 -0
  16. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/db.py +62 -1
  17. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/refund.md +2 -2
  18. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/email_utils.py +182 -0
  19. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/model_agent.py +90 -75
  20. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/models.py +218 -72
  21. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/pricing_updater.py +4 -8
  22. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/rewards_engine.py +4 -2
  23. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/router.py +176 -3
  24. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/__init__.py +6 -1
  25. utim_cli-2.2.2/utim_cli/server/routes/billing_routes.py +262 -0
  26. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/completion_routes.py +10 -1
  27. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/credit_routes.py +33 -1
  28. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/quota_routes.py +48 -0
  29. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/referral_routes.py +11 -2
  30. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/rewards_routes.py +47 -22
  31. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/share_routes.py +18 -12
  32. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/share.py +165 -29
  33. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/share_tui.py +154 -7
  34. utim_cli-2.2.2/utim_cli/subagent_manager.py +724 -0
  35. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tools.py +124 -27
  36. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/feedback_dialog.py +47 -47
  37. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/history_dialog.py +213 -211
  38. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/mcp_dialog.py +16 -14
  39. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/model_dialog.py +137 -158
  40. utim_cli-2.2.2/utim_cli/tui/quota_dialog.py +72 -0
  41. utim_cli-2.2.2/utim_cli/tui/quota_redeem_dialog.py +207 -0
  42. utim_cli-2.2.2/utim_cli/tui/quota_share_dialog.py +446 -0
  43. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/rewards_tui.py +15 -4
  44. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/skills_dialog.py +154 -138
  45. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/subagents_dialog.py +15 -15
  46. utim_cli-2.2.2/utim_cli/tui/tools_dialog.py +273 -0
  47. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/utim.py +53 -84
  48. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/wheel.py +1 -2
  49. {utim_cli-2.2.1 → utim_cli-2.2.2/utim_cli.egg-info}/PKG-INFO +3 -3
  50. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli.egg-info/SOURCES.txt +1 -0
  51. utim_cli-2.2.1/utim_cli/subagent_manager.py +0 -366
  52. utim_cli-2.2.1/utim_cli/tui/quota_dialog.py +0 -36
  53. utim_cli-2.2.1/utim_cli/tui/quota_redeem_dialog.py +0 -181
  54. utim_cli-2.2.1/utim_cli/tui/quota_share_dialog.py +0 -491
  55. utim_cli-2.2.1/utim_cli/tui/tools_dialog.py +0 -248
  56. {utim_cli-2.2.1 → utim_cli-2.2.2}/LICENSE +0 -0
  57. {utim_cli-2.2.1 → utim_cli-2.2.2}/MANIFEST.in +0 -0
  58. {utim_cli-2.2.1 → utim_cli-2.2.2}/README.md +0 -0
  59. {utim_cli-2.2.1 → utim_cli-2.2.2}/setup.cfg +0 -0
  60. {utim_cli-2.2.1 → utim_cli-2.2.2}/setup.py +0 -0
  61. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_check_obj.py +0 -0
  62. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_find_mcp.py +0 -0
  63. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_find_plan.py +0 -0
  64. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_find_tool.py +0 -0
  65. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_patch_multimodal.py +0 -0
  66. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/_test_regex.py +0 -0
  67. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/agent.py +0 -0
  68. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/auth.py +0 -0
  69. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/backup.py +0 -0
  70. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/billing.py +0 -0
  71. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/blender_agent.py +0 -0
  72. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/brain.py +0 -0
  73. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/constants.py +0 -0
  74. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/doctor.py +0 -0
  75. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/knowledge_graph.py +0 -0
  76. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/local_db.py +0 -0
  77. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/logger.py +0 -0
  78. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/mcp_clean_wrapper.py +0 -0
  79. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/mcp_registry.json +0 -0
  80. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/report.py +0 -0
  81. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/scrapy_search.py +0 -0
  82. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/__init__.py +0 -0
  83. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/attribution.py +0 -0
  84. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/audit_log.py +0 -0
  85. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/batch_processor.py +0 -0
  86. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/captcha.py +0 -0
  87. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  88. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/about.md +0 -0
  89. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/changelog.md +0 -0
  90. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/docs.md +0 -0
  91. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/features.md +0 -0
  92. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/license.md +0 -0
  93. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/pricing.md +0 -0
  94. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/privacy.md +0 -0
  95. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/support.md +0 -0
  96. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/docs_md/terms.md +0 -0
  97. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/exchange_rate.py +0 -0
  98. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/firebase.py +0 -0
  99. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/fix_duplicate_users.py +0 -0
  100. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/history.py +0 -0
  101. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/local_llm.py +0 -0
  102. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/logging_config.py +0 -0
  103. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/micro_batcher.py +0 -0
  104. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/provision_build.py +0 -0
  105. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/rate_limit.py +0 -0
  106. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/auth_routes.py +0 -0
  107. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/feedback_routes.py +0 -0
  108. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/marketplace_routes.py +0 -0
  109. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/quota_share_routes.py +0 -0
  110. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/security_routes.py +0 -0
  111. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/routes/session_routes.py +0 -0
  112. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/server.py +0 -0
  113. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/server/storage_nodes.py +0 -0
  114. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/situational_scoring.py +0 -0
  115. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/state.py +0 -0
  116. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/__init__.py +0 -0
  117. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/marketplace_app_state.py +0 -0
  118. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/marketplace_dialog.py +0 -0
  119. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  120. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/miniagents_dialog.py +0 -0
  121. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/publish_dialog.py +0 -0
  122. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/resume_dialog.py +0 -0
  123. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/thinking_display.py +0 -0
  124. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/tui/update_dialog.py +0 -0
  125. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/utilities.py +0 -0
  126. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/vector_memory.py +0 -0
  127. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli/workspace.py +0 -0
  128. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli.egg-info/dependency_links.txt +0 -0
  129. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli.egg-info/entry_points.txt +0 -0
  130. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli.egg-info/requires.txt +0 -0
  131. {utim_cli-2.2.1 → utim_cli-2.2.2}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,5 +1,13 @@
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
+
3
11
  ## [2.2.1] - 2026-08-13
4
12
 
5
13
  ### 🛠️ Fixes & Minor Polish
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.2.1
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
@@ -4,18 +4,18 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "2.2.1"
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",
@@ -103,4 +103,5 @@ markers = [
103
103
  "smoke: fast install validation tests (no live credentials required)",
104
104
  "slow: tests that make network calls or take more than 10 seconds",
105
105
  "integration: tests requiring live credentials or a running UTIM account",
106
+ "benchmark: UTIM benchmark suite — tests all 9 agent benchmark families",
106
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.1"
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.1"
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
 
@@ -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