htmlgraph 0.9.3__py3-none-any.whl → 0.27.5__py3-none-any.whl

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 (331) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +173 -17
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +127 -0
  7. htmlgraph/agent_registry.py +45 -30
  8. htmlgraph/agents.py +160 -107
  9. htmlgraph/analytics/__init__.py +9 -2
  10. htmlgraph/analytics/cli.py +190 -51
  11. htmlgraph/analytics/cost_analyzer.py +391 -0
  12. htmlgraph/analytics/cost_monitor.py +664 -0
  13. htmlgraph/analytics/cost_reporter.py +675 -0
  14. htmlgraph/analytics/cross_session.py +617 -0
  15. htmlgraph/analytics/dependency.py +192 -100
  16. htmlgraph/analytics/pattern_learning.py +771 -0
  17. htmlgraph/analytics/session_graph.py +707 -0
  18. htmlgraph/analytics/strategic/__init__.py +80 -0
  19. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  20. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  21. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  22. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  23. htmlgraph/analytics/work_type.py +190 -14
  24. htmlgraph/analytics_index.py +135 -51
  25. htmlgraph/api/__init__.py +3 -0
  26. htmlgraph/api/cost_alerts_websocket.py +416 -0
  27. htmlgraph/api/main.py +2498 -0
  28. htmlgraph/api/static/htmx.min.js +1 -0
  29. htmlgraph/api/static/style-redesign.css +1344 -0
  30. htmlgraph/api/static/style.css +1079 -0
  31. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  32. htmlgraph/api/templates/dashboard.html +794 -0
  33. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  34. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  35. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  36. htmlgraph/api/templates/partials/agents.html +317 -0
  37. htmlgraph/api/templates/partials/event-traces.html +373 -0
  38. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  39. htmlgraph/api/templates/partials/features.html +578 -0
  40. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  41. htmlgraph/api/templates/partials/metrics.html +346 -0
  42. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  43. htmlgraph/api/templates/partials/orchestration.html +198 -0
  44. htmlgraph/api/templates/partials/spawners.html +375 -0
  45. htmlgraph/api/templates/partials/work-items.html +613 -0
  46. htmlgraph/api/websocket.py +538 -0
  47. htmlgraph/archive/__init__.py +24 -0
  48. htmlgraph/archive/bloom.py +234 -0
  49. htmlgraph/archive/fts.py +297 -0
  50. htmlgraph/archive/manager.py +583 -0
  51. htmlgraph/archive/search.py +244 -0
  52. htmlgraph/atomic_ops.py +560 -0
  53. htmlgraph/attribute_index.py +208 -0
  54. htmlgraph/bounded_paths.py +539 -0
  55. htmlgraph/builders/__init__.py +14 -0
  56. htmlgraph/builders/base.py +118 -29
  57. htmlgraph/builders/bug.py +150 -0
  58. htmlgraph/builders/chore.py +119 -0
  59. htmlgraph/builders/epic.py +150 -0
  60. htmlgraph/builders/feature.py +31 -6
  61. htmlgraph/builders/insight.py +195 -0
  62. htmlgraph/builders/metric.py +217 -0
  63. htmlgraph/builders/pattern.py +202 -0
  64. htmlgraph/builders/phase.py +162 -0
  65. htmlgraph/builders/spike.py +52 -19
  66. htmlgraph/builders/track.py +148 -72
  67. htmlgraph/cigs/__init__.py +81 -0
  68. htmlgraph/cigs/autonomy.py +385 -0
  69. htmlgraph/cigs/cost.py +475 -0
  70. htmlgraph/cigs/messages_basic.py +472 -0
  71. htmlgraph/cigs/messaging.py +365 -0
  72. htmlgraph/cigs/models.py +771 -0
  73. htmlgraph/cigs/pattern_storage.py +427 -0
  74. htmlgraph/cigs/patterns.py +503 -0
  75. htmlgraph/cigs/posttool_analyzer.py +234 -0
  76. htmlgraph/cigs/reporter.py +818 -0
  77. htmlgraph/cigs/tracker.py +317 -0
  78. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  79. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  80. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  81. htmlgraph/cli/__init__.py +42 -0
  82. htmlgraph/cli/__main__.py +6 -0
  83. htmlgraph/cli/analytics.py +1424 -0
  84. htmlgraph/cli/base.py +685 -0
  85. htmlgraph/cli/constants.py +206 -0
  86. htmlgraph/cli/core.py +954 -0
  87. htmlgraph/cli/main.py +147 -0
  88. htmlgraph/cli/models.py +475 -0
  89. htmlgraph/cli/templates/__init__.py +1 -0
  90. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  91. htmlgraph/cli/work/__init__.py +239 -0
  92. htmlgraph/cli/work/browse.py +115 -0
  93. htmlgraph/cli/work/features.py +568 -0
  94. htmlgraph/cli/work/orchestration.py +676 -0
  95. htmlgraph/cli/work/report.py +728 -0
  96. htmlgraph/cli/work/sessions.py +466 -0
  97. htmlgraph/cli/work/snapshot.py +559 -0
  98. htmlgraph/cli/work/tracks.py +486 -0
  99. htmlgraph/cli_commands/__init__.py +1 -0
  100. htmlgraph/cli_commands/feature.py +195 -0
  101. htmlgraph/cli_framework.py +115 -0
  102. htmlgraph/collections/__init__.py +18 -0
  103. htmlgraph/collections/base.py +415 -98
  104. htmlgraph/collections/bug.py +53 -0
  105. htmlgraph/collections/chore.py +53 -0
  106. htmlgraph/collections/epic.py +53 -0
  107. htmlgraph/collections/feature.py +12 -26
  108. htmlgraph/collections/insight.py +100 -0
  109. htmlgraph/collections/metric.py +92 -0
  110. htmlgraph/collections/pattern.py +97 -0
  111. htmlgraph/collections/phase.py +53 -0
  112. htmlgraph/collections/session.py +194 -0
  113. htmlgraph/collections/spike.py +56 -16
  114. htmlgraph/collections/task_delegation.py +241 -0
  115. htmlgraph/collections/todo.py +511 -0
  116. htmlgraph/collections/traces.py +487 -0
  117. htmlgraph/config/cost_models.json +56 -0
  118. htmlgraph/config.py +190 -0
  119. htmlgraph/context_analytics.py +344 -0
  120. htmlgraph/converter.py +216 -28
  121. htmlgraph/cost_analysis/__init__.py +5 -0
  122. htmlgraph/cost_analysis/analyzer.py +438 -0
  123. htmlgraph/dashboard.html +2406 -307
  124. htmlgraph/dashboard.html.backup +6592 -0
  125. htmlgraph/dashboard.html.bak +7181 -0
  126. htmlgraph/dashboard.html.bak2 +7231 -0
  127. htmlgraph/dashboard.html.bak3 +7232 -0
  128. htmlgraph/db/__init__.py +38 -0
  129. htmlgraph/db/queries.py +790 -0
  130. htmlgraph/db/schema.py +1788 -0
  131. htmlgraph/decorators.py +317 -0
  132. htmlgraph/dependency_models.py +19 -2
  133. htmlgraph/deploy.py +142 -125
  134. htmlgraph/deployment_models.py +474 -0
  135. htmlgraph/docs/API_REFERENCE.md +841 -0
  136. htmlgraph/docs/HTTP_API.md +750 -0
  137. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  138. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  139. htmlgraph/docs/README.md +532 -0
  140. htmlgraph/docs/__init__.py +77 -0
  141. htmlgraph/docs/docs_version.py +55 -0
  142. htmlgraph/docs/metadata.py +93 -0
  143. htmlgraph/docs/migrations.py +232 -0
  144. htmlgraph/docs/template_engine.py +143 -0
  145. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  146. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  147. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  148. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  149. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  150. htmlgraph/docs/version_check.py +163 -0
  151. htmlgraph/edge_index.py +182 -27
  152. htmlgraph/error_handler.py +544 -0
  153. htmlgraph/event_log.py +100 -52
  154. htmlgraph/event_migration.py +13 -4
  155. htmlgraph/exceptions.py +49 -0
  156. htmlgraph/file_watcher.py +101 -28
  157. htmlgraph/find_api.py +75 -63
  158. htmlgraph/git_events.py +145 -63
  159. htmlgraph/graph.py +1122 -106
  160. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  161. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  162. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  163. htmlgraph/hooks/__init__.py +45 -0
  164. htmlgraph/hooks/bootstrap.py +169 -0
  165. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  166. htmlgraph/hooks/concurrent_sessions.py +208 -0
  167. htmlgraph/hooks/context.py +350 -0
  168. htmlgraph/hooks/drift_handler.py +525 -0
  169. htmlgraph/hooks/event_tracker.py +1314 -0
  170. htmlgraph/hooks/git_commands.py +175 -0
  171. htmlgraph/hooks/hooks-config.example.json +12 -0
  172. htmlgraph/hooks/installer.py +343 -0
  173. htmlgraph/hooks/orchestrator.py +674 -0
  174. htmlgraph/hooks/orchestrator_reflector.py +223 -0
  175. htmlgraph/hooks/post-checkout.sh +28 -0
  176. htmlgraph/hooks/post-commit.sh +24 -0
  177. htmlgraph/hooks/post-merge.sh +26 -0
  178. htmlgraph/hooks/post_tool_use_failure.py +273 -0
  179. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  180. htmlgraph/hooks/posttooluse.py +408 -0
  181. htmlgraph/hooks/pre-commit.sh +94 -0
  182. htmlgraph/hooks/pre-push.sh +28 -0
  183. htmlgraph/hooks/pretooluse.py +819 -0
  184. htmlgraph/hooks/prompt_analyzer.py +637 -0
  185. htmlgraph/hooks/session_handler.py +668 -0
  186. htmlgraph/hooks/session_summary.py +395 -0
  187. htmlgraph/hooks/state_manager.py +504 -0
  188. htmlgraph/hooks/subagent_detection.py +202 -0
  189. htmlgraph/hooks/subagent_stop.py +369 -0
  190. htmlgraph/hooks/task_enforcer.py +255 -0
  191. htmlgraph/hooks/task_validator.py +177 -0
  192. htmlgraph/hooks/validator.py +628 -0
  193. htmlgraph/ids.py +41 -27
  194. htmlgraph/index.d.ts +286 -0
  195. htmlgraph/learning.py +767 -0
  196. htmlgraph/mcp_server.py +69 -23
  197. htmlgraph/models.py +1586 -87
  198. htmlgraph/operations/README.md +62 -0
  199. htmlgraph/operations/__init__.py +79 -0
  200. htmlgraph/operations/analytics.py +339 -0
  201. htmlgraph/operations/bootstrap.py +289 -0
  202. htmlgraph/operations/events.py +244 -0
  203. htmlgraph/operations/fastapi_server.py +231 -0
  204. htmlgraph/operations/hooks.py +350 -0
  205. htmlgraph/operations/initialization.py +597 -0
  206. htmlgraph/operations/initialization.py.backup +228 -0
  207. htmlgraph/operations/server.py +303 -0
  208. htmlgraph/orchestration/__init__.py +58 -0
  209. htmlgraph/orchestration/claude_launcher.py +179 -0
  210. htmlgraph/orchestration/command_builder.py +72 -0
  211. htmlgraph/orchestration/headless_spawner.py +281 -0
  212. htmlgraph/orchestration/live_events.py +377 -0
  213. htmlgraph/orchestration/model_selection.py +327 -0
  214. htmlgraph/orchestration/plugin_manager.py +140 -0
  215. htmlgraph/orchestration/prompts.py +137 -0
  216. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  217. htmlgraph/orchestration/spawners/__init__.py +16 -0
  218. htmlgraph/orchestration/spawners/base.py +194 -0
  219. htmlgraph/orchestration/spawners/claude.py +173 -0
  220. htmlgraph/orchestration/spawners/codex.py +435 -0
  221. htmlgraph/orchestration/spawners/copilot.py +294 -0
  222. htmlgraph/orchestration/spawners/gemini.py +471 -0
  223. htmlgraph/orchestration/subprocess_runner.py +36 -0
  224. htmlgraph/orchestration/task_coordination.py +343 -0
  225. htmlgraph/orchestration.md +563 -0
  226. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  227. htmlgraph/orchestrator.py +669 -0
  228. htmlgraph/orchestrator_config.py +357 -0
  229. htmlgraph/orchestrator_mode.py +328 -0
  230. htmlgraph/orchestrator_validator.py +133 -0
  231. htmlgraph/parallel.py +646 -0
  232. htmlgraph/parser.py +160 -35
  233. htmlgraph/path_query.py +608 -0
  234. htmlgraph/pattern_matcher.py +636 -0
  235. htmlgraph/planning.py +147 -52
  236. htmlgraph/pydantic_models.py +476 -0
  237. htmlgraph/quality_gates.py +350 -0
  238. htmlgraph/query_builder.py +109 -72
  239. htmlgraph/query_composer.py +509 -0
  240. htmlgraph/reflection.py +443 -0
  241. htmlgraph/refs.py +344 -0
  242. htmlgraph/repo_hash.py +512 -0
  243. htmlgraph/repositories/__init__.py +292 -0
  244. htmlgraph/repositories/analytics_repository.py +455 -0
  245. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  246. htmlgraph/repositories/feature_repository.py +581 -0
  247. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  248. htmlgraph/repositories/feature_repository_memory.py +607 -0
  249. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  250. htmlgraph/repositories/filter_service.py +620 -0
  251. htmlgraph/repositories/filter_service_standard.py +445 -0
  252. htmlgraph/repositories/shared_cache.py +621 -0
  253. htmlgraph/repositories/shared_cache_memory.py +395 -0
  254. htmlgraph/repositories/track_repository.py +552 -0
  255. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  256. htmlgraph/repositories/track_repository_memory.py +508 -0
  257. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  258. htmlgraph/routing.py +8 -19
  259. htmlgraph/scripts/deploy.py +1 -2
  260. htmlgraph/sdk/__init__.py +398 -0
  261. htmlgraph/sdk/__init__.pyi +14 -0
  262. htmlgraph/sdk/analytics/__init__.py +19 -0
  263. htmlgraph/sdk/analytics/engine.py +155 -0
  264. htmlgraph/sdk/analytics/helpers.py +178 -0
  265. htmlgraph/sdk/analytics/registry.py +109 -0
  266. htmlgraph/sdk/base.py +484 -0
  267. htmlgraph/sdk/constants.py +216 -0
  268. htmlgraph/sdk/core.pyi +308 -0
  269. htmlgraph/sdk/discovery.py +120 -0
  270. htmlgraph/sdk/help/__init__.py +12 -0
  271. htmlgraph/sdk/help/mixin.py +699 -0
  272. htmlgraph/sdk/mixins/__init__.py +15 -0
  273. htmlgraph/sdk/mixins/attribution.py +113 -0
  274. htmlgraph/sdk/mixins/mixin.py +410 -0
  275. htmlgraph/sdk/operations/__init__.py +12 -0
  276. htmlgraph/sdk/operations/mixin.py +427 -0
  277. htmlgraph/sdk/orchestration/__init__.py +17 -0
  278. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  279. htmlgraph/sdk/orchestration/spawner.py +204 -0
  280. htmlgraph/sdk/planning/__init__.py +19 -0
  281. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  282. htmlgraph/sdk/planning/mixin.py +211 -0
  283. htmlgraph/sdk/planning/parallel.py +186 -0
  284. htmlgraph/sdk/planning/queue.py +210 -0
  285. htmlgraph/sdk/planning/recommendations.py +87 -0
  286. htmlgraph/sdk/planning/smart_planning.py +319 -0
  287. htmlgraph/sdk/session/__init__.py +19 -0
  288. htmlgraph/sdk/session/continuity.py +57 -0
  289. htmlgraph/sdk/session/handoff.py +110 -0
  290. htmlgraph/sdk/session/info.py +309 -0
  291. htmlgraph/sdk/session/manager.py +103 -0
  292. htmlgraph/sdk/strategic/__init__.py +26 -0
  293. htmlgraph/sdk/strategic/mixin.py +563 -0
  294. htmlgraph/server.py +685 -180
  295. htmlgraph/services/__init__.py +10 -0
  296. htmlgraph/services/claiming.py +199 -0
  297. htmlgraph/session_hooks.py +300 -0
  298. htmlgraph/session_manager.py +1392 -175
  299. htmlgraph/session_registry.py +587 -0
  300. htmlgraph/session_state.py +436 -0
  301. htmlgraph/session_warning.py +201 -0
  302. htmlgraph/sessions/__init__.py +23 -0
  303. htmlgraph/sessions/handoff.py +756 -0
  304. htmlgraph/setup.py +34 -17
  305. htmlgraph/spike_index.py +143 -0
  306. htmlgraph/sync_docs.py +12 -15
  307. htmlgraph/system_prompts.py +450 -0
  308. htmlgraph/templates/AGENTS.md.template +366 -0
  309. htmlgraph/templates/CLAUDE.md.template +97 -0
  310. htmlgraph/templates/GEMINI.md.template +87 -0
  311. htmlgraph/templates/orchestration-view.html +350 -0
  312. htmlgraph/track_builder.py +146 -15
  313. htmlgraph/track_manager.py +69 -21
  314. htmlgraph/transcript.py +890 -0
  315. htmlgraph/transcript_analytics.py +699 -0
  316. htmlgraph/types.py +323 -0
  317. htmlgraph/validation.py +115 -0
  318. htmlgraph/watch.py +8 -5
  319. htmlgraph/work_type_utils.py +3 -2
  320. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
  321. htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
  322. htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
  323. htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
  324. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
  325. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  326. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  327. htmlgraph/cli.py +0 -2688
  328. htmlgraph/sdk.py +0 -709
  329. htmlgraph-0.9.3.dist-info/RECORD +0 -61
  330. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  331. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,747 @@
1
+ """
2
+ SuggestionEngine - Generates context-aware suggestions with ranking.
3
+
4
+ This module provides intelligent suggestion generation based on detected patterns:
5
+ 1. Proactive suggestions - Suggest next actions based on detected patterns
6
+ 2. Delegation recommendations - Suggest which subagent would be best for current task
7
+ 3. Parameter optimization - Suggest optimal token budgets, parallelization levels
8
+ 4. Risk assessment - Suggest when to use Sonnet vs Opus based on task complexity
9
+
10
+ Usage:
11
+ from htmlgraph.analytics.strategic import SuggestionEngine
12
+
13
+ engine = SuggestionEngine(db_path)
14
+
15
+ # Get suggestions for current context
16
+ suggestions = engine.suggest(context)
17
+
18
+ # Rank suggestions by relevance
19
+ ranked = engine.rank(suggestions)
20
+
21
+ # Generate Task() code from pattern
22
+ code = engine.generate_task_code(pattern)
23
+ """
24
+
25
+ import hashlib
26
+ import json
27
+ import logging
28
+ import sqlite3
29
+ from dataclasses import dataclass, field
30
+ from enum import Enum
31
+ from pathlib import Path
32
+ from typing import Any
33
+
34
+ from htmlgraph.analytics.strategic.pattern_detector import (
35
+ DelegationChain,
36
+ ErrorPattern,
37
+ Pattern,
38
+ PatternDetector,
39
+ ToolSequencePattern,
40
+ )
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class SuggestionType(Enum):
46
+ """Types of suggestions that can be generated."""
47
+
48
+ NEXT_ACTION = "next_action" # Suggest next tool/action
49
+ DELEGATION = "delegation" # Suggest which agent to delegate to
50
+ PARAMETER = "parameter" # Suggest optimal parameters
51
+ MODEL_SELECTION = "model_selection" # Suggest which model to use
52
+ ERROR_RESOLUTION = "error_resolution" # Suggest how to resolve error
53
+ WORKFLOW = "workflow" # Suggest workflow optimization
54
+
55
+
56
+ @dataclass
57
+ class TaskContext:
58
+ """
59
+ Context for generating suggestions.
60
+
61
+ Captures current state to enable context-aware suggestions.
62
+
63
+ Attributes:
64
+ session_id: Current session ID
65
+ recent_tools: List of recently used tools (most recent last)
66
+ current_task: Description of current task (if known)
67
+ recent_errors: List of recent error messages
68
+ agent_type: Current agent type (orchestrator, researcher, etc.)
69
+ model: Current model being used
70
+ token_budget: Remaining token budget (if tracked)
71
+ feature_id: Current feature being worked on (if known)
72
+ metadata: Additional context data
73
+ """
74
+
75
+ session_id: str = ""
76
+ recent_tools: list[str] = field(default_factory=list)
77
+ current_task: str = ""
78
+ recent_errors: list[str] = field(default_factory=list)
79
+ agent_type: str = "orchestrator"
80
+ model: str = "claude-sonnet"
81
+ token_budget: int | None = None
82
+ feature_id: str | None = None
83
+ metadata: dict[str, Any] = field(default_factory=dict)
84
+
85
+ @classmethod
86
+ def from_hook_input(cls, hook_input: dict[str, Any]) -> "TaskContext":
87
+ """
88
+ Create TaskContext from PreToolUse hook input.
89
+
90
+ Args:
91
+ hook_input: Hook input dictionary
92
+
93
+ Returns:
94
+ TaskContext instance
95
+ """
96
+ return cls(
97
+ session_id=hook_input.get("session_id", ""),
98
+ recent_tools=hook_input.get("recent_tools", []),
99
+ current_task=hook_input.get("prompt", ""),
100
+ recent_errors=hook_input.get("recent_errors", []),
101
+ agent_type=hook_input.get("agent_type", "orchestrator"),
102
+ model=hook_input.get("model", "claude-sonnet"),
103
+ token_budget=hook_input.get("token_budget"),
104
+ feature_id=hook_input.get("feature_id"),
105
+ metadata=hook_input.get("metadata", {}),
106
+ )
107
+
108
+
109
+ @dataclass
110
+ class Suggestion:
111
+ """
112
+ A suggestion generated by the engine.
113
+
114
+ Attributes:
115
+ suggestion_id: Unique identifier
116
+ suggestion_type: Type of suggestion
117
+ title: Short title for the suggestion
118
+ description: Detailed explanation
119
+ confidence: Confidence score (0.0-1.0)
120
+ relevance: Relevance to current context (0.0-1.0)
121
+ action_code: Code snippet to execute the suggestion (if applicable)
122
+ source_patterns: Pattern IDs that informed this suggestion
123
+ metadata: Additional suggestion-specific data
124
+ """
125
+
126
+ suggestion_id: str
127
+ suggestion_type: SuggestionType
128
+ title: str
129
+ description: str
130
+ confidence: float = 0.0
131
+ relevance: float = 0.0
132
+ action_code: str | None = None
133
+ source_patterns: list[str] = field(default_factory=list)
134
+ metadata: dict[str, Any] = field(default_factory=dict)
135
+
136
+ def to_dict(self) -> dict[str, Any]:
137
+ """Convert suggestion to dictionary for serialization."""
138
+ return {
139
+ "suggestion_id": self.suggestion_id,
140
+ "suggestion_type": self.suggestion_type.value,
141
+ "title": self.title,
142
+ "description": self.description,
143
+ "confidence": self.confidence,
144
+ "relevance": self.relevance,
145
+ "action_code": self.action_code,
146
+ "source_patterns": self.source_patterns,
147
+ "metadata": self.metadata,
148
+ }
149
+
150
+ @staticmethod
151
+ def generate_id(suggestion_type: SuggestionType, content: str) -> str:
152
+ """Generate unique suggestion ID."""
153
+ hash_input = f"{suggestion_type.value}:{content}"
154
+ hash_obj = hashlib.md5(hash_input.encode())
155
+ return f"sug-{hash_obj.hexdigest()[:8]}"
156
+
157
+ def score(self) -> float:
158
+ """
159
+ Calculate overall score for ranking.
160
+
161
+ Score = (confidence * 0.5) + (relevance * 0.5)
162
+ """
163
+ return (self.confidence * 0.5) + (self.relevance * 0.5)
164
+
165
+
166
+ class SuggestionEngine:
167
+ """
168
+ Generates context-aware suggestions based on detected patterns.
169
+
170
+ The engine:
171
+ 1. Analyzes current context (recent tools, errors, task)
172
+ 2. Matches context against known patterns
173
+ 3. Generates relevant suggestions
174
+ 4. Ranks suggestions by confidence and relevance
175
+ """
176
+
177
+ def __init__(self, db_path: Path | str | None = None):
178
+ """
179
+ Initialize suggestion engine.
180
+
181
+ Args:
182
+ db_path: Path to HtmlGraph database. If None, uses default location.
183
+ """
184
+ if db_path is None:
185
+ from htmlgraph.config import get_database_path
186
+
187
+ db_path = get_database_path()
188
+
189
+ self.db_path = Path(db_path)
190
+ self._pattern_detector = PatternDetector(db_path)
191
+ self._conn: sqlite3.Connection | None = None
192
+
193
+ def _get_connection(self) -> sqlite3.Connection:
194
+ """Get database connection with row factory."""
195
+ if self._conn is None:
196
+ self._conn = sqlite3.connect(str(self.db_path))
197
+ self._conn.row_factory = sqlite3.Row
198
+ return self._conn
199
+
200
+ def close(self) -> None:
201
+ """Close database connections."""
202
+ if self._conn:
203
+ self._conn.close()
204
+ self._conn = None
205
+ self._pattern_detector.close()
206
+
207
+ def suggest(
208
+ self,
209
+ context: TaskContext,
210
+ max_suggestions: int = 5,
211
+ ) -> list[Suggestion]:
212
+ """
213
+ Generate suggestions based on current context.
214
+
215
+ Args:
216
+ context: Current task context
217
+ max_suggestions: Maximum number of suggestions to return
218
+
219
+ Returns:
220
+ List of suggestions sorted by score
221
+ """
222
+ suggestions: list[Suggestion] = []
223
+
224
+ # Generate suggestions from different sources
225
+ suggestions.extend(self._suggest_from_tool_patterns(context))
226
+ suggestions.extend(self._suggest_from_delegation_patterns(context))
227
+ suggestions.extend(self._suggest_from_error_patterns(context))
228
+ suggestions.extend(self._suggest_model_selection(context))
229
+
230
+ # Rank and limit
231
+ ranked = self.rank(suggestions)
232
+ return ranked[:max_suggestions]
233
+
234
+ def _suggest_from_tool_patterns(self, context: TaskContext) -> list[Suggestion]:
235
+ """
236
+ Generate next-action suggestions from tool sequence patterns.
237
+
238
+ If recent tools match the start of a known high-success pattern,
239
+ suggest completing the pattern.
240
+
241
+ Args:
242
+ context: Current task context
243
+
244
+ Returns:
245
+ List of next-action suggestions
246
+ """
247
+ suggestions: list[Suggestion] = []
248
+
249
+ if len(context.recent_tools) < 2:
250
+ return suggestions
251
+
252
+ try:
253
+ # Get tool sequence patterns
254
+ patterns = self._pattern_detector.detect_tool_sequences(
255
+ window_size=3, min_frequency=3, days_back=30
256
+ )
257
+
258
+ # Find patterns that match recent tool sequence
259
+ recent = context.recent_tools[-2:] # Last 2 tools
260
+
261
+ for pattern in patterns:
262
+ if not isinstance(pattern, ToolSequencePattern):
263
+ continue
264
+
265
+ # Check if recent tools match start of pattern
266
+ if len(pattern.sequence) >= 3 and pattern.sequence[:2] == recent:
267
+ next_tool = pattern.sequence[2]
268
+
269
+ # Calculate relevance based on pattern success rate
270
+ relevance = pattern.success_rate / 100
271
+
272
+ suggestion = Suggestion(
273
+ suggestion_id=Suggestion.generate_id(
274
+ SuggestionType.NEXT_ACTION, f"next:{next_tool}"
275
+ ),
276
+ suggestion_type=SuggestionType.NEXT_ACTION,
277
+ title=f"Consider using {next_tool} next",
278
+ description=(
279
+ f"Pattern '{' → '.join(pattern.sequence)}' has "
280
+ f"{pattern.success_rate:.0f}% success rate across "
281
+ f"{pattern.frequency} occurrences."
282
+ ),
283
+ confidence=pattern.confidence,
284
+ relevance=relevance,
285
+ source_patterns=[pattern.pattern_id],
286
+ metadata={
287
+ "next_tool": next_tool,
288
+ "full_sequence": pattern.sequence,
289
+ "success_rate": pattern.success_rate,
290
+ },
291
+ )
292
+ suggestions.append(suggestion)
293
+
294
+ except Exception as e:
295
+ logger.warning(f"Error generating tool pattern suggestions: {e}")
296
+
297
+ return suggestions
298
+
299
+ def _suggest_from_delegation_patterns(
300
+ self, context: TaskContext
301
+ ) -> list[Suggestion]:
302
+ """
303
+ Generate delegation suggestions from chain patterns.
304
+
305
+ Suggest which agent to delegate to based on successful delegation chains.
306
+
307
+ Args:
308
+ context: Current task context
309
+
310
+ Returns:
311
+ List of delegation suggestions
312
+ """
313
+ suggestions: list[Suggestion] = []
314
+
315
+ try:
316
+ # Get delegation chain patterns
317
+ patterns = self._pattern_detector.detect_delegation_chains(
318
+ min_frequency=2, days_back=30
319
+ )
320
+
321
+ current_agent = context.agent_type
322
+
323
+ for pattern in patterns:
324
+ if not isinstance(pattern, DelegationChain):
325
+ continue
326
+
327
+ # Check if current agent is in chain
328
+ if current_agent in pattern.agents:
329
+ idx = pattern.agents.index(current_agent)
330
+ if idx < len(pattern.agents) - 1:
331
+ next_agent = pattern.agents[idx + 1]
332
+
333
+ # Calculate relevance based on pattern success rate
334
+ relevance = pattern.success_rate / 100
335
+
336
+ suggestion = Suggestion(
337
+ suggestion_id=Suggestion.generate_id(
338
+ SuggestionType.DELEGATION, f"delegate:{next_agent}"
339
+ ),
340
+ suggestion_type=SuggestionType.DELEGATION,
341
+ title=f"Consider delegating to {next_agent}",
342
+ description=(
343
+ f"Delegation chain '{' → '.join(pattern.agents)}' has "
344
+ f"{pattern.success_rate:.0f}% success rate."
345
+ ),
346
+ confidence=pattern.confidence,
347
+ relevance=relevance,
348
+ action_code=self._generate_delegation_code(
349
+ next_agent, context
350
+ ),
351
+ source_patterns=[pattern.pattern_id],
352
+ metadata={
353
+ "next_agent": next_agent,
354
+ "full_chain": pattern.agents,
355
+ "success_rate": pattern.success_rate,
356
+ },
357
+ )
358
+ suggestions.append(suggestion)
359
+
360
+ except Exception as e:
361
+ logger.warning(f"Error generating delegation suggestions: {e}")
362
+
363
+ return suggestions
364
+
365
+ def _suggest_from_error_patterns(self, context: TaskContext) -> list[Suggestion]:
366
+ """
367
+ Generate error resolution suggestions from error patterns.
368
+
369
+ If recent errors match known patterns, suggest resolution strategies.
370
+
371
+ Args:
372
+ context: Current task context
373
+
374
+ Returns:
375
+ List of error resolution suggestions
376
+ """
377
+ suggestions: list[Suggestion] = []
378
+
379
+ if not context.recent_errors:
380
+ return suggestions
381
+
382
+ try:
383
+ # Get error patterns
384
+ patterns = self._pattern_detector.detect_error_patterns(
385
+ min_frequency=2, days_back=30
386
+ )
387
+
388
+ for error in context.recent_errors[-3:]: # Check last 3 errors
389
+ error_lower = error.lower()
390
+
391
+ for pattern in patterns:
392
+ if not isinstance(pattern, ErrorPattern):
393
+ continue
394
+
395
+ # Check if error matches pattern
396
+ if pattern.error_type in error_lower or any(
397
+ word in error_lower
398
+ for word in pattern.error_message_pattern.lower().split()
399
+ ):
400
+ # Build resolution description
401
+ resolution_desc = ""
402
+ if pattern.resolution_strategies:
403
+ strategies = ", ".join(pattern.resolution_strategies[:3])
404
+ resolution_desc = f" Common resolutions: {strategies}."
405
+
406
+ suggestion = Suggestion(
407
+ suggestion_id=Suggestion.generate_id(
408
+ SuggestionType.ERROR_RESOLUTION,
409
+ f"error:{pattern.error_type}",
410
+ ),
411
+ suggestion_type=SuggestionType.ERROR_RESOLUTION,
412
+ title=f"Resolve {pattern.error_type}",
413
+ description=(
414
+ f"This error pattern has occurred {pattern.frequency} times "
415
+ f"with {pattern.success_rate:.0f}% resolution rate.{resolution_desc}"
416
+ ),
417
+ confidence=pattern.confidence,
418
+ relevance=0.8, # High relevance for matching errors
419
+ source_patterns=[pattern.pattern_id],
420
+ metadata={
421
+ "error_type": pattern.error_type,
422
+ "resolution_strategies": pattern.resolution_strategies,
423
+ "tool_context": pattern.tool_context,
424
+ },
425
+ )
426
+ suggestions.append(suggestion)
427
+ break # One suggestion per error
428
+
429
+ except Exception as e:
430
+ logger.warning(f"Error generating error resolution suggestions: {e}")
431
+
432
+ return suggestions
433
+
434
+ def _suggest_model_selection(self, context: TaskContext) -> list[Suggestion]:
435
+ """
436
+ Suggest model selection based on task complexity.
437
+
438
+ Uses heuristics to determine if Haiku, Sonnet, or Opus is appropriate.
439
+
440
+ Args:
441
+ context: Current task context
442
+
443
+ Returns:
444
+ List of model selection suggestions
445
+ """
446
+ suggestions: list[Suggestion] = []
447
+
448
+ task = context.current_task.lower()
449
+
450
+ # Simple heuristics for model selection
451
+ # Complex tasks that benefit from Opus
452
+ complex_indicators = [
453
+ "refactor",
454
+ "architecture",
455
+ "design",
456
+ "optimize",
457
+ "security",
458
+ "performance",
459
+ "complex",
460
+ "critical",
461
+ ]
462
+
463
+ # Simple tasks suitable for Haiku
464
+ simple_indicators = [
465
+ "format",
466
+ "lint",
467
+ "typo",
468
+ "rename",
469
+ "move",
470
+ "copy",
471
+ "list",
472
+ "show",
473
+ "status",
474
+ ]
475
+
476
+ is_complex = any(ind in task for ind in complex_indicators)
477
+ is_simple = any(ind in task for ind in simple_indicators)
478
+
479
+ current_model = context.model.lower()
480
+
481
+ if is_complex and "opus" not in current_model:
482
+ suggestion = Suggestion(
483
+ suggestion_id=Suggestion.generate_id(
484
+ SuggestionType.MODEL_SELECTION, "use:opus"
485
+ ),
486
+ suggestion_type=SuggestionType.MODEL_SELECTION,
487
+ title="Consider using Opus for this task",
488
+ description=(
489
+ "This task appears complex and may benefit from Claude Opus's "
490
+ "enhanced reasoning capabilities."
491
+ ),
492
+ confidence=0.6,
493
+ relevance=0.7,
494
+ metadata={"recommended_model": "claude-opus", "reason": "complex_task"},
495
+ )
496
+ suggestions.append(suggestion)
497
+
498
+ elif is_simple and "haiku" not in current_model:
499
+ suggestion = Suggestion(
500
+ suggestion_id=Suggestion.generate_id(
501
+ SuggestionType.MODEL_SELECTION, "use:haiku"
502
+ ),
503
+ suggestion_type=SuggestionType.MODEL_SELECTION,
504
+ title="Consider using Haiku for this task",
505
+ description=(
506
+ "This task appears simple and Haiku could complete it faster "
507
+ "and more cost-effectively."
508
+ ),
509
+ confidence=0.6,
510
+ relevance=0.6,
511
+ metadata={"recommended_model": "claude-haiku", "reason": "simple_task"},
512
+ )
513
+ suggestions.append(suggestion)
514
+
515
+ return suggestions
516
+
517
+ def rank(self, suggestions: list[Suggestion]) -> list[Suggestion]:
518
+ """
519
+ Rank suggestions by score.
520
+
521
+ Args:
522
+ suggestions: List of suggestions to rank
523
+
524
+ Returns:
525
+ Sorted list of suggestions (highest score first)
526
+ """
527
+ return sorted(suggestions, key=lambda s: s.score(), reverse=True)
528
+
529
+ def explain(self, suggestion: Suggestion) -> str:
530
+ """
531
+ Generate a human-readable explanation for a suggestion.
532
+
533
+ Args:
534
+ suggestion: Suggestion to explain
535
+
536
+ Returns:
537
+ Explanation text
538
+ """
539
+ explanation_parts = [
540
+ f"**{suggestion.title}**",
541
+ "",
542
+ f"{suggestion.description}",
543
+ "",
544
+ f"Confidence: {suggestion.confidence:.0%}",
545
+ f"Relevance: {suggestion.relevance:.0%}",
546
+ ]
547
+
548
+ if suggestion.source_patterns:
549
+ explanation_parts.append(
550
+ f"Based on patterns: {', '.join(suggestion.source_patterns)}"
551
+ )
552
+
553
+ if suggestion.action_code:
554
+ explanation_parts.extend(
555
+ [
556
+ "",
557
+ "Suggested code:",
558
+ "```python",
559
+ suggestion.action_code,
560
+ "```",
561
+ ]
562
+ )
563
+
564
+ return "\n".join(explanation_parts)
565
+
566
+ def generate_task_code(self, pattern: Pattern) -> str:
567
+ """
568
+ Generate Task() code snippet from a pattern.
569
+
570
+ Creates executable code that applies the pattern.
571
+
572
+ Args:
573
+ pattern: Pattern to generate code from
574
+
575
+ Returns:
576
+ Python code snippet for Task() call
577
+ """
578
+ if isinstance(pattern, ToolSequencePattern):
579
+ return self._generate_sequence_task_code(pattern)
580
+ elif isinstance(pattern, DelegationChain):
581
+ return self._generate_chain_task_code(pattern)
582
+ elif isinstance(pattern, ErrorPattern):
583
+ return self._generate_error_resolution_code(pattern)
584
+ else:
585
+ return "# No code generation available for this pattern type"
586
+
587
+ def _generate_sequence_task_code(self, pattern: ToolSequencePattern) -> str:
588
+ """Generate Task() code for tool sequence pattern."""
589
+ sequence_desc = " → ".join(pattern.sequence)
590
+ return f'''# Apply pattern: {sequence_desc}
591
+ # Success rate: {pattern.success_rate:.0f}%
592
+ Task(
593
+ prompt="""
594
+ Follow this successful workflow pattern:
595
+ {sequence_desc}
596
+
597
+ Execute each step in sequence, validating results before proceeding.
598
+ """,
599
+ max_tokens=8000
600
+ )'''
601
+
602
+ def _generate_chain_task_code(self, pattern: DelegationChain) -> str:
603
+ """Generate Task() code for delegation chain pattern."""
604
+ if len(pattern.agents) < 2:
605
+ return "# Invalid delegation chain"
606
+
607
+ first_agent = pattern.agents[0]
608
+ second_agent = pattern.agents[1]
609
+ chain_desc = " → ".join(pattern.agents)
610
+
611
+ return f'''# Apply delegation chain: {chain_desc}
612
+ # Success rate: {pattern.success_rate:.0f}%
613
+ Task(
614
+ prompt="""
615
+ As {first_agent}, analyze the task and delegate to {second_agent}.
616
+
617
+ Successful chain pattern: {chain_desc}
618
+ """,
619
+ max_tokens=8000
620
+ )'''
621
+
622
+ def _generate_error_resolution_code(self, pattern: ErrorPattern) -> str:
623
+ """Generate Task() code for error resolution pattern."""
624
+ strategies = (
625
+ ", ".join(pattern.resolution_strategies[:3])
626
+ if pattern.resolution_strategies
627
+ else "analyze and fix"
628
+ )
629
+
630
+ return f'''# Resolve {pattern.error_type}
631
+ # Resolution rate: {pattern.success_rate:.0f}%
632
+ Task(
633
+ prompt="""
634
+ Resolve {pattern.error_type} error.
635
+
636
+ Common resolution strategies: {strategies}
637
+
638
+ Tools commonly used in context: {", ".join(pattern.tool_context[:3]) if pattern.tool_context else "various"}
639
+ """,
640
+ max_tokens=8000
641
+ )'''
642
+
643
+ def _generate_delegation_code(self, target_agent: str, context: TaskContext) -> str:
644
+ """Generate Task() code for delegation suggestion."""
645
+ return f'''Task(
646
+ prompt="""
647
+ Delegate to {target_agent} agent for the following task:
648
+ {context.current_task[:200] if context.current_task else "Current task"}
649
+ """,
650
+ max_tokens=8000
651
+ )'''
652
+
653
+ def store_suggestion(self, suggestion: Suggestion, context: TaskContext) -> bool:
654
+ """
655
+ Store a suggestion in the database for feedback tracking.
656
+
657
+ Args:
658
+ suggestion: Suggestion to store
659
+ context: Context in which suggestion was generated
660
+
661
+ Returns:
662
+ True if stored successfully, False otherwise
663
+ """
664
+ conn = self._get_connection()
665
+ cursor = conn.cursor()
666
+
667
+ try:
668
+ cursor.execute(
669
+ """
670
+ INSERT INTO delegation_suggestions
671
+ (suggestion_id, suggestion_type, session_id, title, description,
672
+ confidence, relevance, action_code, source_patterns, metadata,
673
+ created_at)
674
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
675
+ """,
676
+ (
677
+ suggestion.suggestion_id,
678
+ suggestion.suggestion_type.value,
679
+ context.session_id,
680
+ suggestion.title,
681
+ suggestion.description,
682
+ suggestion.confidence,
683
+ suggestion.relevance,
684
+ suggestion.action_code,
685
+ json.dumps(suggestion.source_patterns),
686
+ json.dumps(suggestion.metadata),
687
+ ),
688
+ )
689
+
690
+ conn.commit()
691
+ return True
692
+
693
+ except sqlite3.Error as e:
694
+ logger.error(f"Error storing suggestion: {e}")
695
+ return False
696
+
697
+ def get_recent_suggestions(
698
+ self,
699
+ session_id: str,
700
+ limit: int = 10,
701
+ ) -> list[Suggestion]:
702
+ """
703
+ Get recent suggestions for a session.
704
+
705
+ Args:
706
+ session_id: Session to query
707
+ limit: Maximum number of suggestions
708
+
709
+ Returns:
710
+ List of recent suggestions
711
+ """
712
+ conn = self._get_connection()
713
+ cursor = conn.cursor()
714
+
715
+ try:
716
+ cursor.execute(
717
+ """
718
+ SELECT * FROM delegation_suggestions
719
+ WHERE session_id = ?
720
+ ORDER BY created_at DESC
721
+ LIMIT ?
722
+ """,
723
+ (session_id, limit),
724
+ )
725
+
726
+ suggestions = []
727
+ for row in cursor.fetchall():
728
+ suggestion = Suggestion(
729
+ suggestion_id=row["suggestion_id"],
730
+ suggestion_type=SuggestionType(row["suggestion_type"]),
731
+ title=row["title"],
732
+ description=row["description"],
733
+ confidence=row["confidence"],
734
+ relevance=row["relevance"],
735
+ action_code=row["action_code"],
736
+ source_patterns=json.loads(row["source_patterns"])
737
+ if row["source_patterns"]
738
+ else [],
739
+ metadata=json.loads(row["metadata"]) if row["metadata"] else {},
740
+ )
741
+ suggestions.append(suggestion)
742
+
743
+ return suggestions
744
+
745
+ except sqlite3.Error as e:
746
+ logger.error(f"Error retrieving suggestions: {e}")
747
+ return []