htmlgraph 0.20.1__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 (304) 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 +51 -1
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +26 -10
  7. htmlgraph/agent_registry.py +2 -1
  8. htmlgraph/analytics/__init__.py +8 -1
  9. htmlgraph/analytics/cli.py +86 -20
  10. htmlgraph/analytics/cost_analyzer.py +391 -0
  11. htmlgraph/analytics/cost_monitor.py +664 -0
  12. htmlgraph/analytics/cost_reporter.py +675 -0
  13. htmlgraph/analytics/cross_session.py +617 -0
  14. htmlgraph/analytics/dependency.py +10 -6
  15. htmlgraph/analytics/pattern_learning.py +771 -0
  16. htmlgraph/analytics/session_graph.py +707 -0
  17. htmlgraph/analytics/strategic/__init__.py +80 -0
  18. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  19. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  20. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  21. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  22. htmlgraph/analytics/work_type.py +67 -27
  23. htmlgraph/analytics_index.py +53 -20
  24. htmlgraph/api/__init__.py +3 -0
  25. htmlgraph/api/cost_alerts_websocket.py +416 -0
  26. htmlgraph/api/main.py +2498 -0
  27. htmlgraph/api/static/htmx.min.js +1 -0
  28. htmlgraph/api/static/style-redesign.css +1344 -0
  29. htmlgraph/api/static/style.css +1079 -0
  30. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  31. htmlgraph/api/templates/dashboard.html +794 -0
  32. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  33. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  34. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  35. htmlgraph/api/templates/partials/agents.html +317 -0
  36. htmlgraph/api/templates/partials/event-traces.html +373 -0
  37. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  38. htmlgraph/api/templates/partials/features.html +578 -0
  39. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  40. htmlgraph/api/templates/partials/metrics.html +346 -0
  41. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  42. htmlgraph/api/templates/partials/orchestration.html +198 -0
  43. htmlgraph/api/templates/partials/spawners.html +375 -0
  44. htmlgraph/api/templates/partials/work-items.html +613 -0
  45. htmlgraph/api/websocket.py +538 -0
  46. htmlgraph/archive/__init__.py +24 -0
  47. htmlgraph/archive/bloom.py +234 -0
  48. htmlgraph/archive/fts.py +297 -0
  49. htmlgraph/archive/manager.py +583 -0
  50. htmlgraph/archive/search.py +244 -0
  51. htmlgraph/atomic_ops.py +560 -0
  52. htmlgraph/attribute_index.py +2 -1
  53. htmlgraph/bounded_paths.py +539 -0
  54. htmlgraph/builders/base.py +57 -2
  55. htmlgraph/builders/bug.py +19 -3
  56. htmlgraph/builders/chore.py +19 -3
  57. htmlgraph/builders/epic.py +19 -3
  58. htmlgraph/builders/feature.py +27 -3
  59. htmlgraph/builders/insight.py +2 -1
  60. htmlgraph/builders/metric.py +2 -1
  61. htmlgraph/builders/pattern.py +2 -1
  62. htmlgraph/builders/phase.py +19 -3
  63. htmlgraph/builders/spike.py +29 -3
  64. htmlgraph/builders/track.py +42 -1
  65. htmlgraph/cigs/__init__.py +81 -0
  66. htmlgraph/cigs/autonomy.py +385 -0
  67. htmlgraph/cigs/cost.py +475 -0
  68. htmlgraph/cigs/messages_basic.py +472 -0
  69. htmlgraph/cigs/messaging.py +365 -0
  70. htmlgraph/cigs/models.py +771 -0
  71. htmlgraph/cigs/pattern_storage.py +427 -0
  72. htmlgraph/cigs/patterns.py +503 -0
  73. htmlgraph/cigs/posttool_analyzer.py +234 -0
  74. htmlgraph/cigs/reporter.py +818 -0
  75. htmlgraph/cigs/tracker.py +317 -0
  76. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  77. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  78. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  79. htmlgraph/cli/__init__.py +42 -0
  80. htmlgraph/cli/__main__.py +6 -0
  81. htmlgraph/cli/analytics.py +1424 -0
  82. htmlgraph/cli/base.py +685 -0
  83. htmlgraph/cli/constants.py +206 -0
  84. htmlgraph/cli/core.py +954 -0
  85. htmlgraph/cli/main.py +147 -0
  86. htmlgraph/cli/models.py +475 -0
  87. htmlgraph/cli/templates/__init__.py +1 -0
  88. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  89. htmlgraph/cli/work/__init__.py +239 -0
  90. htmlgraph/cli/work/browse.py +115 -0
  91. htmlgraph/cli/work/features.py +568 -0
  92. htmlgraph/cli/work/orchestration.py +676 -0
  93. htmlgraph/cli/work/report.py +728 -0
  94. htmlgraph/cli/work/sessions.py +466 -0
  95. htmlgraph/cli/work/snapshot.py +559 -0
  96. htmlgraph/cli/work/tracks.py +486 -0
  97. htmlgraph/cli_commands/__init__.py +1 -0
  98. htmlgraph/cli_commands/feature.py +195 -0
  99. htmlgraph/cli_framework.py +115 -0
  100. htmlgraph/collections/__init__.py +2 -0
  101. htmlgraph/collections/base.py +197 -14
  102. htmlgraph/collections/bug.py +2 -1
  103. htmlgraph/collections/chore.py +2 -1
  104. htmlgraph/collections/epic.py +2 -1
  105. htmlgraph/collections/feature.py +2 -1
  106. htmlgraph/collections/insight.py +2 -1
  107. htmlgraph/collections/metric.py +2 -1
  108. htmlgraph/collections/pattern.py +2 -1
  109. htmlgraph/collections/phase.py +2 -1
  110. htmlgraph/collections/session.py +194 -0
  111. htmlgraph/collections/spike.py +13 -2
  112. htmlgraph/collections/task_delegation.py +241 -0
  113. htmlgraph/collections/todo.py +14 -1
  114. htmlgraph/collections/traces.py +487 -0
  115. htmlgraph/config/cost_models.json +56 -0
  116. htmlgraph/config.py +190 -0
  117. htmlgraph/context_analytics.py +2 -1
  118. htmlgraph/converter.py +116 -7
  119. htmlgraph/cost_analysis/__init__.py +5 -0
  120. htmlgraph/cost_analysis/analyzer.py +438 -0
  121. htmlgraph/dashboard.html +2246 -248
  122. htmlgraph/dashboard.html.backup +6592 -0
  123. htmlgraph/dashboard.html.bak +7181 -0
  124. htmlgraph/dashboard.html.bak2 +7231 -0
  125. htmlgraph/dashboard.html.bak3 +7232 -0
  126. htmlgraph/db/__init__.py +38 -0
  127. htmlgraph/db/queries.py +790 -0
  128. htmlgraph/db/schema.py +1788 -0
  129. htmlgraph/decorators.py +317 -0
  130. htmlgraph/dependency_models.py +2 -1
  131. htmlgraph/deploy.py +26 -27
  132. htmlgraph/docs/API_REFERENCE.md +841 -0
  133. htmlgraph/docs/HTTP_API.md +750 -0
  134. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  135. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  136. htmlgraph/docs/README.md +532 -0
  137. htmlgraph/docs/__init__.py +77 -0
  138. htmlgraph/docs/docs_version.py +55 -0
  139. htmlgraph/docs/metadata.py +93 -0
  140. htmlgraph/docs/migrations.py +232 -0
  141. htmlgraph/docs/template_engine.py +143 -0
  142. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  143. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  144. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  145. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  146. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  147. htmlgraph/docs/version_check.py +163 -0
  148. htmlgraph/edge_index.py +2 -1
  149. htmlgraph/error_handler.py +544 -0
  150. htmlgraph/event_log.py +86 -37
  151. htmlgraph/event_migration.py +2 -1
  152. htmlgraph/file_watcher.py +12 -8
  153. htmlgraph/find_api.py +2 -1
  154. htmlgraph/git_events.py +67 -9
  155. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  156. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  157. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  158. htmlgraph/hooks/__init__.py +8 -0
  159. htmlgraph/hooks/bootstrap.py +169 -0
  160. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  161. htmlgraph/hooks/concurrent_sessions.py +208 -0
  162. htmlgraph/hooks/context.py +350 -0
  163. htmlgraph/hooks/drift_handler.py +525 -0
  164. htmlgraph/hooks/event_tracker.py +790 -99
  165. htmlgraph/hooks/git_commands.py +175 -0
  166. htmlgraph/hooks/installer.py +5 -1
  167. htmlgraph/hooks/orchestrator.py +327 -76
  168. htmlgraph/hooks/orchestrator_reflector.py +31 -4
  169. htmlgraph/hooks/post_tool_use_failure.py +32 -7
  170. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  171. htmlgraph/hooks/posttooluse.py +92 -19
  172. htmlgraph/hooks/pretooluse.py +527 -7
  173. htmlgraph/hooks/prompt_analyzer.py +637 -0
  174. htmlgraph/hooks/session_handler.py +668 -0
  175. htmlgraph/hooks/session_summary.py +395 -0
  176. htmlgraph/hooks/state_manager.py +504 -0
  177. htmlgraph/hooks/subagent_detection.py +202 -0
  178. htmlgraph/hooks/subagent_stop.py +369 -0
  179. htmlgraph/hooks/task_enforcer.py +99 -4
  180. htmlgraph/hooks/validator.py +212 -91
  181. htmlgraph/ids.py +2 -1
  182. htmlgraph/learning.py +125 -100
  183. htmlgraph/mcp_server.py +2 -1
  184. htmlgraph/models.py +217 -18
  185. htmlgraph/operations/README.md +62 -0
  186. htmlgraph/operations/__init__.py +79 -0
  187. htmlgraph/operations/analytics.py +339 -0
  188. htmlgraph/operations/bootstrap.py +289 -0
  189. htmlgraph/operations/events.py +244 -0
  190. htmlgraph/operations/fastapi_server.py +231 -0
  191. htmlgraph/operations/hooks.py +350 -0
  192. htmlgraph/operations/initialization.py +597 -0
  193. htmlgraph/operations/initialization.py.backup +228 -0
  194. htmlgraph/operations/server.py +303 -0
  195. htmlgraph/orchestration/__init__.py +58 -0
  196. htmlgraph/orchestration/claude_launcher.py +179 -0
  197. htmlgraph/orchestration/command_builder.py +72 -0
  198. htmlgraph/orchestration/headless_spawner.py +281 -0
  199. htmlgraph/orchestration/live_events.py +377 -0
  200. htmlgraph/orchestration/model_selection.py +327 -0
  201. htmlgraph/orchestration/plugin_manager.py +140 -0
  202. htmlgraph/orchestration/prompts.py +137 -0
  203. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  204. htmlgraph/orchestration/spawners/__init__.py +16 -0
  205. htmlgraph/orchestration/spawners/base.py +194 -0
  206. htmlgraph/orchestration/spawners/claude.py +173 -0
  207. htmlgraph/orchestration/spawners/codex.py +435 -0
  208. htmlgraph/orchestration/spawners/copilot.py +294 -0
  209. htmlgraph/orchestration/spawners/gemini.py +471 -0
  210. htmlgraph/orchestration/subprocess_runner.py +36 -0
  211. htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
  212. htmlgraph/orchestration.md +563 -0
  213. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  214. htmlgraph/orchestrator.py +2 -1
  215. htmlgraph/orchestrator_config.py +357 -0
  216. htmlgraph/orchestrator_mode.py +115 -4
  217. htmlgraph/parallel.py +2 -1
  218. htmlgraph/parser.py +86 -6
  219. htmlgraph/path_query.py +608 -0
  220. htmlgraph/pattern_matcher.py +636 -0
  221. htmlgraph/pydantic_models.py +476 -0
  222. htmlgraph/quality_gates.py +350 -0
  223. htmlgraph/query_builder.py +2 -1
  224. htmlgraph/query_composer.py +509 -0
  225. htmlgraph/reflection.py +443 -0
  226. htmlgraph/refs.py +344 -0
  227. htmlgraph/repo_hash.py +512 -0
  228. htmlgraph/repositories/__init__.py +292 -0
  229. htmlgraph/repositories/analytics_repository.py +455 -0
  230. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  231. htmlgraph/repositories/feature_repository.py +581 -0
  232. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  233. htmlgraph/repositories/feature_repository_memory.py +607 -0
  234. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  235. htmlgraph/repositories/filter_service.py +620 -0
  236. htmlgraph/repositories/filter_service_standard.py +445 -0
  237. htmlgraph/repositories/shared_cache.py +621 -0
  238. htmlgraph/repositories/shared_cache_memory.py +395 -0
  239. htmlgraph/repositories/track_repository.py +552 -0
  240. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  241. htmlgraph/repositories/track_repository_memory.py +508 -0
  242. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  243. htmlgraph/sdk/__init__.py +398 -0
  244. htmlgraph/sdk/__init__.pyi +14 -0
  245. htmlgraph/sdk/analytics/__init__.py +19 -0
  246. htmlgraph/sdk/analytics/engine.py +155 -0
  247. htmlgraph/sdk/analytics/helpers.py +178 -0
  248. htmlgraph/sdk/analytics/registry.py +109 -0
  249. htmlgraph/sdk/base.py +484 -0
  250. htmlgraph/sdk/constants.py +216 -0
  251. htmlgraph/sdk/core.pyi +308 -0
  252. htmlgraph/sdk/discovery.py +120 -0
  253. htmlgraph/sdk/help/__init__.py +12 -0
  254. htmlgraph/sdk/help/mixin.py +699 -0
  255. htmlgraph/sdk/mixins/__init__.py +15 -0
  256. htmlgraph/sdk/mixins/attribution.py +113 -0
  257. htmlgraph/sdk/mixins/mixin.py +410 -0
  258. htmlgraph/sdk/operations/__init__.py +12 -0
  259. htmlgraph/sdk/operations/mixin.py +427 -0
  260. htmlgraph/sdk/orchestration/__init__.py +17 -0
  261. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  262. htmlgraph/sdk/orchestration/spawner.py +204 -0
  263. htmlgraph/sdk/planning/__init__.py +19 -0
  264. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  265. htmlgraph/sdk/planning/mixin.py +211 -0
  266. htmlgraph/sdk/planning/parallel.py +186 -0
  267. htmlgraph/sdk/planning/queue.py +210 -0
  268. htmlgraph/sdk/planning/recommendations.py +87 -0
  269. htmlgraph/sdk/planning/smart_planning.py +319 -0
  270. htmlgraph/sdk/session/__init__.py +19 -0
  271. htmlgraph/sdk/session/continuity.py +57 -0
  272. htmlgraph/sdk/session/handoff.py +110 -0
  273. htmlgraph/sdk/session/info.py +309 -0
  274. htmlgraph/sdk/session/manager.py +103 -0
  275. htmlgraph/sdk/strategic/__init__.py +26 -0
  276. htmlgraph/sdk/strategic/mixin.py +563 -0
  277. htmlgraph/server.py +295 -107
  278. htmlgraph/session_hooks.py +300 -0
  279. htmlgraph/session_manager.py +285 -3
  280. htmlgraph/session_registry.py +587 -0
  281. htmlgraph/session_state.py +436 -0
  282. htmlgraph/session_warning.py +2 -1
  283. htmlgraph/sessions/__init__.py +23 -0
  284. htmlgraph/sessions/handoff.py +756 -0
  285. htmlgraph/system_prompts.py +450 -0
  286. htmlgraph/templates/orchestration-view.html +350 -0
  287. htmlgraph/track_builder.py +33 -1
  288. htmlgraph/track_manager.py +38 -0
  289. htmlgraph/transcript.py +18 -5
  290. htmlgraph/validation.py +115 -0
  291. htmlgraph/watch.py +2 -1
  292. htmlgraph/work_type_utils.py +2 -1
  293. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
  294. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
  295. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  296. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  297. htmlgraph/cli.py +0 -4839
  298. htmlgraph/sdk.py +0 -2359
  299. htmlgraph-0.20.1.dist-info/RECORD +0 -118
  300. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  301. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  302. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  303. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  304. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,350 @@
1
+ """
2
+ Hook Execution Context Manager.
3
+
4
+ Manages hook execution context including lazy-loading of expensive resources
5
+ (database, session manager) to minimize initialization overhead.
6
+
7
+ This module provides a centralized context object that hooks can use to:
8
+ - Access the graph directory and project directory
9
+ - Retrieve session information
10
+ - Access the database for event recording
11
+ - Perform unified logging
12
+
13
+ Key Design Principles:
14
+ - Lazy-loading: Expensive resources (DB, SessionManager) are only loaded on first access
15
+ - Resource cleanup: Context properly closes resources when done
16
+ - Type safety: Full type hints for all public methods and properties
17
+ - Error handling: Graceful degradation if resources fail to initialize
18
+ """
19
+
20
+ import logging
21
+ import os
22
+ from dataclasses import dataclass, field
23
+ from pathlib import Path
24
+ from typing import Any
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ @dataclass
30
+ class HookContext:
31
+ """
32
+ Hook execution context with lazy-loaded resources.
33
+
34
+ Attributes:
35
+ project_dir: Absolute path to project root directory
36
+ graph_dir: Path to .htmlgraph directory for tracking data
37
+ session_id: Unique session identifier for this execution
38
+ agent_id: Agent/tool that's executing (e.g., 'claude-code', 'codex')
39
+ hook_input: Raw hook input data from Claude Code
40
+ model_name: Specific Claude model name (e.g., 'claude-haiku', 'claude-opus', 'claude-sonnet')
41
+ _session_manager: Cached SessionManager instance (lazy-loaded)
42
+ _database: Cached HtmlGraphDB instance (lazy-loaded)
43
+ """
44
+
45
+ project_dir: str
46
+ graph_dir: Path
47
+ session_id: str
48
+ agent_id: str
49
+ hook_input: dict[str, Any]
50
+ model_name: str | None = field(default=None, repr=False)
51
+ _session_manager: Any | None = field(default=None, repr=False)
52
+ _database: Any | None = field(default=None, repr=False)
53
+
54
+ @classmethod
55
+ def from_input(cls, hook_input: dict[str, Any]) -> "HookContext":
56
+ """
57
+ Create HookContext from raw hook input.
58
+
59
+ Performs automatic environment resolution:
60
+ - Extracts session_id from hook_input
61
+ - Detects agent_id from environment or hook_input
62
+ - Detects model_name (e.g., claude-haiku, claude-opus, claude-sonnet)
63
+ - Resolves project directory via bootstrap
64
+ - Initializes graph directory
65
+
66
+ Args:
67
+ hook_input: Raw hook input dict from Claude Code hook system
68
+
69
+ Returns:
70
+ Initialized HookContext instance
71
+
72
+ Raises:
73
+ ImportError: If bootstrap module cannot be imported
74
+ OSError: If graph directory cannot be created
75
+
76
+ Example:
77
+ ```python
78
+ hook_input = {
79
+ 'session_id': 'sess-abc123',
80
+ 'type': 'pretooluse',
81
+ 'tool_name': 'Edit',
82
+ ...
83
+ }
84
+ context = HookContext.from_input(hook_input)
85
+ logger.info(f"Session: {context.session_id}, Agent: {context.agent_id}, Model: {context.model_name}")
86
+ ```
87
+ """
88
+ # Import bootstrap locally to avoid circular imports
89
+ from htmlgraph.hooks.bootstrap import (
90
+ get_graph_dir,
91
+ resolve_project_dir,
92
+ )
93
+
94
+ # Resolve project directory first
95
+ project_dir = resolve_project_dir()
96
+ graph_dir = get_graph_dir(project_dir)
97
+
98
+ # Extract session ID with multiple fallbacks
99
+ # Priority order:
100
+ # 1. hook_input["session_id"] (if Claude Code passes it)
101
+ # 2. hook_input["sessionId"] (camelCase variant)
102
+ # 3. HTMLGRAPH_SESSION_ID environment variable
103
+ # 4. CLAUDE_SESSION_ID environment variable
104
+ # 5. Most recent active session from database (NEW)
105
+ # 6. "unknown" as last resort
106
+ #
107
+ # NOTE: We intentionally do NOT use SessionManager.get_active_session()
108
+ # as a fallback because the "active session" is stored in a global file
109
+ # (.htmlgraph/session.json) that's shared across all Claude windows.
110
+ # Using it would cause cross-window event contamination where tool calls
111
+ # from Window B get linked to UserQuery events from Window A.
112
+ #
113
+ # However, we DO query the database by status='active' and created_at,
114
+ # which is different because it retrieves the most recent session that
115
+ # was explicitly marked as active (e.g., by SessionStart hook), without
116
+ # relying on a shared global agent state file.
117
+ session_id = (
118
+ hook_input.get("session_id")
119
+ or hook_input.get("sessionId")
120
+ or os.environ.get("HTMLGRAPH_SESSION_ID")
121
+ or os.environ.get("CLAUDE_SESSION_ID")
122
+ )
123
+
124
+ # Fallback: Query database for session with most recent UserQuery event
125
+ # This solves the issue where PostToolUse hooks don't receive session_id
126
+ # in hook_input. UserPromptSubmit hooks DO receive it and create UserQuery
127
+ # events with the correct session_id, so we use that as the source of truth.
128
+ if not session_id:
129
+ db_path = graph_dir / "htmlgraph.db"
130
+ if db_path.exists():
131
+ try:
132
+ import sqlite3
133
+
134
+ conn = sqlite3.connect(str(db_path), timeout=1.0)
135
+ cursor = conn.cursor()
136
+ cursor.execute("""
137
+ SELECT session_id FROM agent_events
138
+ WHERE tool_name = 'UserQuery'
139
+ ORDER BY timestamp DESC
140
+ LIMIT 1
141
+ """)
142
+ row = cursor.fetchone()
143
+ conn.close()
144
+ if row:
145
+ session_id = row[0]
146
+ logger.info(f"Resolved session_id from database: {session_id}")
147
+ except Exception as e:
148
+ logger.warning(f"Failed to query active session from database: {e}")
149
+
150
+ # Final fallback to "unknown" if database query fails
151
+ if not session_id:
152
+ session_id = "unknown"
153
+ logger.warning(
154
+ "Could not resolve session_id from hook_input, environment, or database. "
155
+ "Events will not be linked to parent UserQuery. "
156
+ "For multi-window support, set HTMLGRAPH_SESSION_ID env var."
157
+ )
158
+
159
+ # Detect agent ID (priority order)
160
+ # 1. Explicit agent_id in hook input
161
+ # 2. HTMLGRAPH_AGENT_ID environment variable
162
+ # 3. CLAUDE_AGENT_NICKNAME environment variable (Claude Code)
163
+ # 4. Default to 'unknown'
164
+ agent_id = (
165
+ hook_input.get("agent_id")
166
+ or os.environ.get("HTMLGRAPH_AGENT_ID")
167
+ or os.environ.get("CLAUDE_AGENT_NICKNAME", "unknown")
168
+ )
169
+
170
+ # Detect model name (priority order)
171
+ # 1. Explicit model_name in hook input
172
+ # 2. CLAUDE_MODEL environment variable
173
+ # 3. HTMLGRAPH_MODEL environment variable
174
+ # 4. Status line cache (from ~/.cache/claude-code/status-{session_id}.json)
175
+ # 5. None (not available)
176
+ model_name = (
177
+ hook_input.get("model_name")
178
+ or hook_input.get("model")
179
+ or os.environ.get("CLAUDE_MODEL")
180
+ or os.environ.get("HTMLGRAPH_MODEL")
181
+ )
182
+
183
+ # Fallback: Try status line cache if model not detected yet
184
+ if not model_name and session_id and session_id != "unknown":
185
+ from htmlgraph.hooks.event_tracker import get_model_from_status_cache
186
+
187
+ model_name = get_model_from_status_cache(session_id)
188
+
189
+ logger.info(
190
+ f"Initializing hook context: session={session_id}, "
191
+ f"agent={agent_id}, model={model_name}, project={project_dir}"
192
+ )
193
+
194
+ return cls(
195
+ project_dir=project_dir,
196
+ graph_dir=graph_dir,
197
+ session_id=session_id,
198
+ agent_id=agent_id,
199
+ hook_input=hook_input,
200
+ model_name=model_name,
201
+ )
202
+
203
+ @property
204
+ def session_manager(self) -> Any:
205
+ """
206
+ Lazy-load and cache SessionManager instance.
207
+
208
+ Importing SessionManager is expensive (thousands of file system operations
209
+ for graph initialization), so we defer until first access.
210
+
211
+ Returns:
212
+ SessionManager instance for session tracking and activity attribution
213
+
214
+ Raises:
215
+ ImportError: If SessionManager cannot be imported
216
+ Exception: If SessionManager initialization fails
217
+
218
+ Note:
219
+ SessionManager is cached after first access. Multiple accesses
220
+ return the same instance.
221
+ """
222
+ if self._session_manager is not None:
223
+ return self._session_manager
224
+
225
+ try:
226
+ from htmlgraph.session_manager import SessionManager
227
+
228
+ logger.debug(f"Loading SessionManager for {self.graph_dir}")
229
+ self._session_manager = SessionManager(graph_dir=self.graph_dir)
230
+ logger.info("SessionManager loaded successfully")
231
+ return self._session_manager
232
+ except ImportError as e:
233
+ logger.error(f"Failed to import SessionManager: {e}")
234
+ raise
235
+ except Exception as e:
236
+ logger.error(f"Failed to initialize SessionManager: {e}")
237
+ raise
238
+
239
+ @property
240
+ def database(self) -> Any:
241
+ """
242
+ Lazy-load and cache HtmlGraphDB instance.
243
+
244
+ Database access is needed for event recording, but we defer initialization
245
+ until first access to minimize startup overhead.
246
+
247
+ Returns:
248
+ HtmlGraphDB instance for recording events and features
249
+
250
+ Raises:
251
+ ImportError: If HtmlGraphDB cannot be imported
252
+ Exception: If database connection fails
253
+
254
+ Note:
255
+ Database connection is cached after first access. Multiple accesses
256
+ return the same instance.
257
+ """
258
+ if self._database is not None:
259
+ return self._database
260
+
261
+ try:
262
+ from htmlgraph.db.schema import HtmlGraphDB
263
+
264
+ db_path = self.graph_dir / "htmlgraph.db"
265
+ logger.debug(f"Loading HtmlGraphDB at {db_path}")
266
+ self._database = HtmlGraphDB(str(db_path))
267
+ logger.info("HtmlGraphDB loaded successfully")
268
+ return self._database
269
+ except ImportError as e:
270
+ logger.error(f"Failed to import HtmlGraphDB: {e}")
271
+ raise
272
+ except Exception as e:
273
+ logger.error(f"Failed to initialize HtmlGraphDB: {e}")
274
+ raise
275
+
276
+ def close(self) -> None:
277
+ """
278
+ Clean up and close all resources gracefully.
279
+
280
+ Closes database connections and session manager resources.
281
+ Safe to call multiple times (idempotent).
282
+
283
+ This should be called in a finally block to ensure cleanup:
284
+
285
+ Example:
286
+ ```python
287
+ context = HookContext.from_input(hook_input)
288
+ try:
289
+ # Use context
290
+ context.session_manager.track_activity(...)
291
+ finally:
292
+ context.close() # Always cleanup
293
+ ```
294
+ """
295
+ # Close database if loaded
296
+ if self._database is not None:
297
+ try:
298
+ logger.debug("Closing database connection")
299
+ self._database.close()
300
+ self._database = None
301
+ logger.info("Database closed successfully")
302
+ except Exception as e:
303
+ logger.warning(f"Error closing database: {e}")
304
+
305
+ # Close session manager if loaded
306
+ if self._session_manager is not None:
307
+ try:
308
+ logger.debug("Closing session manager")
309
+ # SessionManager doesn't currently have a close method,
310
+ # but we keep this for future resource management
311
+ self._session_manager = None
312
+ logger.info("Session manager cleaned up")
313
+ except Exception as e:
314
+ logger.warning(f"Error closing session manager: {e}")
315
+
316
+ def log(self, level: str, message: str) -> None:
317
+ """
318
+ Unified logging for hooks.
319
+
320
+ Provides consistent logging across all hook modules with context
321
+ information (session_id, agent_id, project_dir).
322
+
323
+ Args:
324
+ level: Log level as string ('debug', 'info', 'warning', 'error', 'critical')
325
+ message: Message to log
326
+
327
+ Example:
328
+ ```python
329
+ context.log('info', 'Processing user query')
330
+ context.log('error', f'Failed to track activity: {error}')
331
+ ```
332
+ """
333
+ log_func = getattr(logger, level.lower(), logger.info)
334
+
335
+ # Prefix message with context for better debugging
336
+ context_msg = f"[{self.session_id[:8]}][{self.agent_id}] {message}"
337
+ log_func(context_msg)
338
+
339
+ def __enter__(self) -> "HookContext":
340
+ """Context manager entry."""
341
+ return self
342
+
343
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
344
+ """Context manager exit with resource cleanup."""
345
+ self.close()
346
+
347
+
348
+ __all__ = [
349
+ "HookContext",
350
+ ]