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
htmlgraph/models.py CHANGED
@@ -7,7 +7,7 @@ These models provide:
7
7
  - Lightweight context generation for AI agents
8
8
  """
9
9
 
10
- from datetime import datetime
10
+ from datetime import datetime, timezone
11
11
  from enum import Enum
12
12
  from pathlib import Path
13
13
  from typing import Any, Literal
@@ -15,6 +15,11 @@ from typing import Any, Literal
15
15
  from pydantic import BaseModel, Field
16
16
 
17
17
 
18
+ def utc_now() -> datetime:
19
+ """Return current time as UTC-aware datetime."""
20
+ return datetime.now(timezone.utc)
21
+
22
+
18
23
  class WorkType(str, Enum):
19
24
  """
20
25
  Classification of work/activity type for events and sessions.
@@ -264,15 +269,15 @@ class Node(BaseModel):
264
269
  if edge.relationship not in self.edges:
265
270
  self.edges[edge.relationship] = []
266
271
  self.edges[edge.relationship].append(edge)
267
- self.updated = datetime.now()
272
+ self.updated = utc_now()
268
273
 
269
274
  def complete_step(self, index: int, agent: str | None = None) -> bool:
270
275
  """Mark a step as completed."""
271
276
  if 0 <= index < len(self.steps):
272
277
  self.steps[index].completed = True
273
278
  self.steps[index].agent = agent
274
- self.steps[index].timestamp = datetime.now()
275
- self.updated = datetime.now()
279
+ self.steps[index].timestamp = utc_now()
280
+ self.updated = utc_now()
276
281
  return True
277
282
  return False
278
283
 
@@ -300,7 +305,7 @@ class Node(BaseModel):
300
305
  self.context_tokens_used += tokens_used
301
306
  self.context_peak_tokens = max(self.context_peak_tokens, peak_tokens)
302
307
  self.context_cost_usd += cost_usd
303
- self.updated = datetime.now()
308
+ self.updated = utc_now()
304
309
 
305
310
  def context_stats(self) -> dict:
306
311
  """
@@ -317,6 +322,24 @@ class Node(BaseModel):
317
322
  "session_ids": self.context_sessions,
318
323
  }
319
324
 
325
+ def to_dict(self) -> dict:
326
+ """
327
+ Convert Node to dictionary format.
328
+
329
+ This is a convenience alias for Pydantic's model_dump() method,
330
+ providing a more discoverable API for serialization.
331
+
332
+ Returns:
333
+ dict: Dictionary representation of the Node with all fields
334
+
335
+ Example:
336
+ >>> feature = sdk.features.create("My Feature").save()
337
+ >>> data = feature.to_dict()
338
+ >>> print(data['title'])
339
+ 'My Feature'
340
+ """
341
+ return self.model_dump()
342
+
320
343
  def to_html(self, stylesheet_path: str = "../styles.css") -> str:
321
344
  """
322
345
  Convert node to full HTML document.
@@ -813,9 +836,7 @@ class ContextSnapshot(BaseModel):
813
836
  def from_dict(cls, data: dict) -> "ContextSnapshot":
814
837
  """Create from dictionary."""
815
838
  return cls(
816
- timestamp=datetime.fromisoformat(data["ts"])
817
- if "ts" in data
818
- else datetime.now(),
839
+ timestamp=datetime.fromisoformat(data["ts"]) if "ts" in data else utc_now(),
819
840
  input_tokens=data.get("in", 0),
820
841
  output_tokens=data.get("out", 0),
821
842
  cache_creation_tokens=data.get("cache_create", 0),
@@ -829,6 +850,48 @@ class ContextSnapshot(BaseModel):
829
850
  )
830
851
 
831
852
 
853
+ class ErrorEntry(BaseModel):
854
+ """
855
+ An error record for session error tracking and debugging.
856
+
857
+ Stored inline within Session nodes for error analysis and debugging.
858
+ """
859
+
860
+ timestamp: datetime = Field(default_factory=datetime.now)
861
+ error_type: str # Exception class name (ValueError, FileNotFoundError, etc.)
862
+ message: str # Error message
863
+ traceback: str | None = None # Full traceback for debugging
864
+ tool: str | None = None # Tool that caused the error (Edit, Bash, etc.)
865
+ context: str | None = None # Additional context information
866
+ session_id: str | None = None # Session ID for cross-referencing
867
+ locals_dump: str | None = None # JSON-serialized local variables at error point
868
+ stack_frames: list[dict[str, Any]] | None = (
869
+ None # Structured stack frame information
870
+ )
871
+ command_args: dict[str, Any] | None = None # Command arguments being executed
872
+ display_level: str = "minimal" # Display level: minimal, verbose, or debug
873
+
874
+ def to_html(self) -> str:
875
+ """Convert error to HTML details element."""
876
+ attrs = [
877
+ f'data-ts="{self.timestamp.isoformat()}"',
878
+ f'data-error-type="{self.error_type}"',
879
+ ]
880
+ if self.tool:
881
+ attrs.append(f'data-tool="{self.tool}"')
882
+
883
+ summary = f"<span class='error-type'>{self.error_type}</span>: {self.message}"
884
+ details = ""
885
+ if self.traceback:
886
+ details = f"<pre class='traceback'>{self.traceback}</pre>"
887
+
888
+ return f"<details class='error-item' {' '.join(attrs)}><summary>{summary}</summary>{details}</details>"
889
+
890
+ def to_context(self) -> str:
891
+ """Lightweight context for AI agents."""
892
+ return f"[{self.timestamp.strftime('%H:%M:%S')}] ERROR {self.error_type}: {self.message}"
893
+
894
+
832
895
  class ActivityEntry(BaseModel):
833
896
  """
834
897
  A lightweight activity log entry for high-frequency events.
@@ -907,10 +970,18 @@ class Session(BaseModel):
907
970
  worked_on: list[str] = Field(default_factory=list) # Feature IDs
908
971
  continued_from: str | None = None # Previous session ID
909
972
 
910
- # Handoff context
973
+ # Parent session context (for nested Task() calls)
974
+ parent_session: str | None = None # Parent session ID
975
+ parent_activity: str | None = None # Parent activity ID
976
+ nesting_depth: int = 0 # Depth of nesting (0 = top-level)
977
+
978
+ # Handoff context (Phase 2 Feature 3: Cross-Session Continuity)
911
979
  handoff_notes: str | None = None
912
980
  recommended_next: str | None = None
913
981
  blockers: list[str] = Field(default_factory=list)
982
+ recommended_context: list[str] = Field(
983
+ default_factory=list
984
+ ) # File paths to keep context for
914
985
 
915
986
  # High-frequency activity log
916
987
  activity_log: list[ActivityEntry] = Field(default_factory=list)
@@ -937,20 +1008,67 @@ class Session(BaseModel):
937
1008
  transcript_synced_at: datetime | None = None # Last sync timestamp
938
1009
  transcript_git_branch: str | None = None # Git branch from transcript
939
1010
 
1011
+ # Pattern detection (inline storage to avoid file bloat)
1012
+ detected_patterns: list[dict[str, Any]] = Field(default_factory=list)
1013
+ """
1014
+ Patterns detected during this session.
1015
+
1016
+ Format:
1017
+ {
1018
+ "sequence": ["Bash", "Read", "Edit"],
1019
+ "pattern_type": "neutral", # or "optimal", "anti_pattern"
1020
+ "detection_count": 3,
1021
+ "first_detected": "2026-01-02T10:00:00",
1022
+ "last_detected": "2026-01-02T10:30:00"
1023
+ }
1024
+ """
1025
+
1026
+ # Error handling (Phase 1B)
1027
+ error_log: list[ErrorEntry] = Field(default_factory=list)
1028
+ """Error records for this session with full tracebacks for debugging."""
1029
+
940
1030
  def add_activity(self, entry: ActivityEntry) -> None:
941
1031
  """Add an activity entry to the log."""
942
1032
  self.activity_log.append(entry)
943
1033
  self.event_count += 1
944
- self.last_activity = datetime.now()
1034
+ self.last_activity = utc_now()
945
1035
 
946
1036
  # Track features worked on
947
1037
  if entry.feature_id and entry.feature_id not in self.worked_on:
948
1038
  self.worked_on.append(entry.feature_id)
949
1039
 
1040
+ def add_error(
1041
+ self,
1042
+ error_type: str,
1043
+ message: str,
1044
+ traceback: str | None = None,
1045
+ tool: str | None = None,
1046
+ context: str | None = None,
1047
+ ) -> None:
1048
+ """
1049
+ Add an error entry to the error log.
1050
+
1051
+ Args:
1052
+ error_type: Exception class name (ValueError, FileNotFoundError, etc.)
1053
+ message: Error message
1054
+ traceback: Full traceback for debugging
1055
+ tool: Tool that caused the error (Edit, Bash, etc.)
1056
+ context: Additional context information
1057
+ """
1058
+ error = ErrorEntry(
1059
+ error_type=error_type,
1060
+ message=message,
1061
+ traceback=traceback,
1062
+ tool=tool,
1063
+ context=context,
1064
+ session_id=self.id,
1065
+ )
1066
+ self.error_log.append(error)
1067
+
950
1068
  def end(self) -> None:
951
1069
  """Mark session as ended."""
952
1070
  self.status = "ended"
953
- self.ended_at = datetime.now()
1071
+ self.ended_at = utc_now()
954
1072
 
955
1073
  def record_context(
956
1074
  self, snapshot: ContextSnapshot, sample_interval: int = 10
@@ -1267,7 +1385,12 @@ class Session(BaseModel):
1267
1385
 
1268
1386
  # Build handoff HTML
1269
1387
  handoff_html = ""
1270
- if self.handoff_notes or self.recommended_next or self.blockers:
1388
+ if (
1389
+ self.handoff_notes
1390
+ or self.recommended_next
1391
+ or self.blockers
1392
+ or self.recommended_context
1393
+ ):
1271
1394
  handoff_section = """
1272
1395
  <section data-handoff>
1273
1396
  <h3>Handoff Context</h3>"""
@@ -1290,6 +1413,18 @@ class Session(BaseModel):
1290
1413
  </ul>
1291
1414
  </div>"""
1292
1415
 
1416
+ if self.recommended_context:
1417
+ context_items = "\n ".join(
1418
+ f"<li>{file_path}</li>" for file_path in self.recommended_context
1419
+ )
1420
+ handoff_section += f"""
1421
+ <div data-recommended-context>
1422
+ <strong>Recommended Context:</strong>
1423
+ <ul>
1424
+ {context_items}
1425
+ </ul>
1426
+ </div>"""
1427
+
1293
1428
  handoff_section += "\n </section>"
1294
1429
  handoff_html = handoff_section
1295
1430
 
@@ -1324,6 +1459,14 @@ class Session(BaseModel):
1324
1459
  if self.primary_work_type
1325
1460
  else ""
1326
1461
  )
1462
+ # Parent session attributes
1463
+ parent_session_attrs = ""
1464
+ if self.parent_session:
1465
+ parent_session_attrs += f' data-parent-session="{self.parent_session}"'
1466
+ if self.parent_activity:
1467
+ parent_session_attrs += f' data-parent-activity="{self.parent_activity}"'
1468
+ if self.nesting_depth > 0:
1469
+ parent_session_attrs += f' data-nesting-depth="{self.nesting_depth}"'
1327
1470
 
1328
1471
  # Serialize work_breakdown as JSON if present
1329
1472
  import json
@@ -1378,6 +1521,62 @@ class Session(BaseModel):
1378
1521
  </dl>
1379
1522
  </section>"""
1380
1523
 
1524
+ # Build detected patterns section
1525
+ patterns_html = ""
1526
+ if self.detected_patterns:
1527
+ patterns_html = f"""
1528
+ <section data-detected-patterns>
1529
+ <h3>Detected Patterns ({len(self.detected_patterns)})</h3>
1530
+ <table class="patterns-table">
1531
+ <thead>
1532
+ <tr>
1533
+ <th>Sequence</th>
1534
+ <th>Type</th>
1535
+ <th>Count</th>
1536
+ <th>First/Last Detected</th>
1537
+ </tr>
1538
+ </thead>
1539
+ <tbody>"""
1540
+
1541
+ for pattern in self.detected_patterns:
1542
+ seq_str = " → ".join(pattern.get("sequence", []))
1543
+ pattern_type = pattern.get("pattern_type", "neutral")
1544
+ count = pattern.get("detection_count", 0)
1545
+ first = pattern.get("first_detected", "")
1546
+ last = pattern.get("last_detected", "")
1547
+
1548
+ patterns_html += f"""
1549
+ <tr data-pattern-type="{pattern_type}">
1550
+ <td class="sequence">{seq_str}</td>
1551
+ <td><span class="badge pattern-{pattern_type}">{pattern_type}</span></td>
1552
+ <td>{count}</td>
1553
+ <td>{first} / {last}</td>
1554
+ </tr>"""
1555
+
1556
+ patterns_html += """
1557
+ </tbody>
1558
+ </table>
1559
+ </section>"""
1560
+
1561
+ # Build error log section
1562
+ error_html = ""
1563
+ if self.error_log:
1564
+ error_items = "\n ".join(
1565
+ error.to_html() for error in self.error_log
1566
+ )
1567
+ error_html = f"""
1568
+ <section data-error-log>
1569
+ <h3>Errors ({len(self.error_log)})</h3>
1570
+ <div class="error-log">
1571
+ {error_items}
1572
+ </div>
1573
+ <style>
1574
+ .error-item {{ margin: 10px 0; padding: 10px; border-left: 3px solid #ff6b6b; }}
1575
+ .error-type {{ font-weight: bold; color: #ff6b6b; }}
1576
+ .traceback {{ background: #f5f5f5; padding: 10px; overflow-x: auto; font-size: 0.9em; margin-top: 5px; }}
1577
+ </style>
1578
+ </section>"""
1579
+
1381
1580
  title = self.title or f"Session {self.id}"
1382
1581
 
1383
1582
  return f'''<!DOCTYPE html>
@@ -1396,7 +1595,7 @@ class Session(BaseModel):
1396
1595
  data-agent="{self.agent}"
1397
1596
  data-started-at="{self.started_at.isoformat()}"
1398
1597
  data-last-activity="{self.last_activity.isoformat()}"
1399
- data-event-count="{self.event_count}"{subagent_attr}{commit_attr}{ended_attr}{primary_work_type_attr}{work_breakdown_attr}{context_attrs}{transcript_attrs}>
1598
+ data-event-count="{self.event_count}"{subagent_attr}{commit_attr}{ended_attr}{primary_work_type_attr}{work_breakdown_attr}{context_attrs}{transcript_attrs}{parent_session_attrs}>
1400
1599
 
1401
1600
  <header>
1402
1601
  <h1>{title}</h1>
@@ -1406,7 +1605,7 @@ class Session(BaseModel):
1406
1605
  <span class="badge">{self.event_count} events</span>
1407
1606
  </div>
1408
1607
  </header>
1409
- {edges_html}{handoff_html}{context_html}{activity_html}
1608
+ {edges_html}{handoff_html}{context_html}{error_html}{patterns_html}{activity_html}
1410
1609
  </article>
1411
1610
  </body>
1412
1611
  </html>
@@ -2058,16 +2257,16 @@ class Todo(BaseModel):
2058
2257
  def start(self) -> "Todo":
2059
2258
  """Mark todo as in progress."""
2060
2259
  self.status = "in_progress"
2061
- self.started_at = datetime.now()
2062
- self.updated = datetime.now()
2260
+ self.started_at = utc_now()
2261
+ self.updated = utc_now()
2063
2262
  return self
2064
2263
 
2065
2264
  def complete(self, agent: str | None = None) -> "Todo":
2066
2265
  """Mark todo as completed."""
2067
2266
  self.status = "completed"
2068
- self.completed_at = datetime.now()
2267
+ self.completed_at = utc_now()
2069
2268
  self.completed_by = agent
2070
- self.updated = datetime.now()
2269
+ self.updated = utc_now()
2071
2270
 
2072
2271
  # Calculate duration if started
2073
2272
  if self.started_at:
@@ -0,0 +1,62 @@
1
+ # HtmlGraph Operations Layer
2
+
3
+ This module defines a shared, backend operations layer for HtmlGraph. The CLI and SDK
4
+ should call these operations rather than duplicating logic. The operations layer is
5
+ pure Python, stateless, and returns structured data instead of printing.
6
+
7
+ ## Design Principles
8
+
9
+ - Stateless: all inputs passed explicitly; no global CLI state.
10
+ - Typed: full type hints, dataclasses for results.
11
+ - Structured results: return data, warnings, and metadata; no printing.
12
+ - Exceptions for errors: no sys.exit.
13
+ - Path-first: accept Path objects for filesystem inputs.
14
+ - Reusable: callable from CLI, SDK, and tests.
15
+
16
+ ## Module Structure
17
+
18
+ - `operations/server.py` Server startup and lifecycle helpers
19
+ - `operations/hooks.py` Git hook installation and configuration
20
+ - `operations/events.py` Event export, index rebuild, event queries
21
+ - `operations/analytics.py` Analytics summaries and report generation
22
+
23
+ ## Example Signature
24
+
25
+ ```python
26
+ from dataclasses import dataclass
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ @dataclass
31
+ class ServerHandle:
32
+ url: str
33
+ port: int
34
+ host: str
35
+
36
+ @dataclass
37
+ class ServerStartResult:
38
+ handle: ServerHandle
39
+ warnings: list[str]
40
+ config_used: dict[str, Any]
41
+
42
+ class ServerStartError(RuntimeError):
43
+ pass
44
+
45
+
46
+ def start_server(
47
+ *,
48
+ port: int,
49
+ graph_dir: Path,
50
+ host: str = "localhost",
51
+ watch: bool = True,
52
+ auto_port: bool = False,
53
+ ) -> ServerStartResult:
54
+ """Start HtmlGraph server with validated config."""
55
+ raise NotImplementedError
56
+ ```
57
+
58
+ ## Conventions
59
+
60
+ - Functions should avoid any CLI-specific formatting.
61
+ - Results should be serializable for JSON output.
62
+ - Keep modules focused on a single domain (server, hooks, events, analytics).
@@ -0,0 +1,79 @@
1
+ """Shared operations layer for HtmlGraph CLI and SDK."""
2
+
3
+ from .analytics import (
4
+ AnalyticsProjectResult,
5
+ AnalyticsSessionResult,
6
+ analyze_project,
7
+ analyze_session,
8
+ )
9
+ from .events import (
10
+ EventExportResult,
11
+ EventQueryResult,
12
+ EventRebuildResult,
13
+ EventStats,
14
+ export_sessions,
15
+ get_event_stats,
16
+ query_events,
17
+ rebuild_index,
18
+ )
19
+ from .hooks import (
20
+ HookInstallResult,
21
+ HookListResult,
22
+ HookValidationResult,
23
+ install_hooks,
24
+ list_hooks,
25
+ validate_hook_config,
26
+ )
27
+ from .initialization import (
28
+ create_analytics_index,
29
+ create_config_files,
30
+ create_database,
31
+ create_directory_structure,
32
+ initialize_htmlgraph,
33
+ install_git_hooks,
34
+ update_gitignore,
35
+ validate_directory,
36
+ )
37
+ from .server import (
38
+ ServerHandle,
39
+ ServerStartResult,
40
+ ServerStatus,
41
+ get_server_status,
42
+ start_server,
43
+ stop_server,
44
+ )
45
+
46
+ __all__ = [
47
+ "AnalyticsProjectResult",
48
+ "AnalyticsSessionResult",
49
+ "analyze_project",
50
+ "analyze_session",
51
+ "EventExportResult",
52
+ "EventQueryResult",
53
+ "EventRebuildResult",
54
+ "EventStats",
55
+ "export_sessions",
56
+ "get_event_stats",
57
+ "query_events",
58
+ "rebuild_index",
59
+ "HookInstallResult",
60
+ "HookListResult",
61
+ "HookValidationResult",
62
+ "install_hooks",
63
+ "list_hooks",
64
+ "validate_hook_config",
65
+ "ServerHandle",
66
+ "ServerStartResult",
67
+ "ServerStatus",
68
+ "start_server",
69
+ "stop_server",
70
+ "get_server_status",
71
+ "initialize_htmlgraph",
72
+ "validate_directory",
73
+ "create_directory_structure",
74
+ "create_database",
75
+ "create_analytics_index",
76
+ "create_config_files",
77
+ "update_gitignore",
78
+ "install_git_hooks",
79
+ ]