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
@@ -4,22 +4,24 @@ CLI Analytics Command - Beautiful work type analytics with rich formatting.
4
4
  This module provides the `htmlgraph analytics` command for analyzing work patterns.
5
5
  """
6
6
 
7
+ import argparse
8
+ from collections.abc import Iterator
9
+ from contextlib import AbstractContextManager, nullcontext
7
10
  from pathlib import Path
8
- from datetime import datetime, timedelta
11
+
12
+ from rich import box
9
13
  from rich.console import Console
10
- from rich.table import Table
11
14
  from rich.panel import Panel
12
- from rich.layout import Layout
13
- from rich import box
14
- from rich.text import Text
15
+ from rich.table import Table
15
16
 
16
17
  from htmlgraph import SDK, WorkType
17
18
  from htmlgraph.converter import html_to_session
18
19
 
19
20
 
20
- def cmd_analytics(args):
21
+ def cmd_analytics(args: argparse.Namespace) -> int:
21
22
  """Display work type analytics with beautiful rich formatting."""
22
23
  console = Console()
24
+ quiet = getattr(args, "quiet", False)
23
25
 
24
26
  try:
25
27
  sdk = SDK(agent=args.agent or "cli")
@@ -31,13 +33,13 @@ def cmd_analytics(args):
31
33
  sessions_dir = Path(args.graph_dir) / "sessions"
32
34
 
33
35
  if not sessions_dir.exists():
34
- console.print("[yellow]No sessions found. Run some work to generate analytics![/yellow]")
36
+ console.print(
37
+ "[yellow]No sessions found. Run some work to generate analytics![/yellow]"
38
+ )
35
39
  return 0
36
40
 
37
41
  session_files = sorted(
38
- sessions_dir.glob("*.html"),
39
- key=lambda p: p.stat().st_mtime,
40
- reverse=True
42
+ sessions_dir.glob("*.html"), key=lambda p: p.stat().st_mtime, reverse=True
41
43
  )
42
44
 
43
45
  if not session_files:
@@ -47,18 +49,68 @@ def cmd_analytics(args):
47
49
  # Determine scope
48
50
  if args.session_id:
49
51
  # Single session analysis
50
- _display_session_analytics(console, sdk, args.session_id, args.graph_dir)
52
+ _display_session_analytics(
53
+ console, sdk, args.session_id, args.graph_dir, quiet=quiet
54
+ )
51
55
  elif args.recent:
52
56
  # Recent sessions
53
- _display_recent_sessions(console, sdk, session_files[:args.recent], args.graph_dir)
57
+ _display_recent_sessions(
58
+ console, sdk, session_files[: args.recent], args.graph_dir, quiet=quiet
59
+ )
54
60
  else:
55
61
  # Project-wide overview
56
- _display_project_analytics(console, sdk, session_files, args.graph_dir)
62
+ _display_project_analytics(
63
+ console, sdk, session_files, args.graph_dir, quiet=quiet
64
+ )
57
65
 
58
66
  return 0
59
67
 
60
68
 
61
- def _display_session_analytics(console: Console, sdk: SDK, session_id: str, graph_dir: str):
69
+ def _status_context(
70
+ console: Console, quiet: bool, message: str
71
+ ) -> AbstractContextManager[object]:
72
+ if quiet:
73
+ return nullcontext()
74
+ return console.status(message)
75
+
76
+
77
+ def _iter_with_progress(
78
+ console: Console, quiet: bool, items: list[Path], description: str
79
+ ) -> Iterator[Path]:
80
+ if quiet:
81
+ for item in items:
82
+ yield item
83
+ return
84
+ try:
85
+ from rich.progress import (
86
+ BarColumn,
87
+ Progress,
88
+ SpinnerColumn,
89
+ TextColumn,
90
+ TimeElapsedColumn,
91
+ )
92
+ except Exception:
93
+ for item in items:
94
+ yield item
95
+ return
96
+
97
+ with Progress(
98
+ SpinnerColumn(),
99
+ TextColumn("{task.description}"),
100
+ BarColumn(),
101
+ TimeElapsedColumn(),
102
+ console=console,
103
+ transient=True,
104
+ ) as progress:
105
+ task_id = progress.add_task(description, total=len(items))
106
+ for item in items:
107
+ yield item
108
+ progress.advance(task_id)
109
+
110
+
111
+ def _display_session_analytics(
112
+ console: Console, sdk: "SDK", session_id: str, graph_dir: str, quiet: bool
113
+ ) -> None:
62
114
  """Display analytics for a single session."""
63
115
  from htmlgraph.converter import html_to_session
64
116
 
@@ -69,18 +121,23 @@ def _display_session_analytics(console: Console, sdk: SDK, session_id: str, grap
69
121
  return
70
122
 
71
123
  try:
72
- session = html_to_session(session_path)
124
+ with _status_context(console, quiet, "Loading session data..."):
125
+ session = html_to_session(session_path)
73
126
  except Exception as e:
74
127
  console.print(f"[red]Error loading session: {e}[/red]")
75
128
  return
76
129
 
77
130
  # Get analytics
78
- dist = sdk.analytics.work_type_distribution(session_id=session_id)
79
- ratio = sdk.analytics.spike_to_feature_ratio(session_id=session_id)
80
- burden = sdk.analytics.maintenance_burden(session_id=session_id)
81
- primary = sdk.analytics.calculate_session_primary_work_type(session_id)
82
- breakdown = sdk.analytics.calculate_session_work_breakdown(session_id)
83
- total_events = sum(breakdown.values()) if breakdown else session.event_count
131
+ with _status_context(console, quiet, "Computing session analytics..."):
132
+ dist = sdk.analytics.work_type_distribution(session_id=session_id)
133
+ ratio = sdk.analytics.spike_to_feature_ratio(session_id=session_id)
134
+ burden = sdk.analytics.maintenance_burden(session_id=session_id)
135
+ primary = sdk.analytics.calculate_session_primary_work_type(session_id)
136
+ breakdown = sdk.analytics.calculate_session_work_breakdown(session_id)
137
+ total_events = sum(breakdown.values()) if breakdown else session.event_count
138
+ transition_metrics = sdk.analytics.transition_time_metrics(
139
+ session_id=session_id
140
+ )
84
141
 
85
142
  # Header panel
86
143
  header = Panel(
@@ -88,7 +145,7 @@ def _display_session_analytics(console: Console, sdk: SDK, session_id: str, grap
88
145
  f"Agent: {session.agent} | Status: {session.status}\n"
89
146
  f"Started: {session.started_at.strftime('%Y-%m-%d %H:%M')} | Events: {total_events}",
90
147
  title="📊 Session Analytics",
91
- border_style="cyan"
148
+ border_style="cyan",
92
149
  )
93
150
  console.print(header)
94
151
  console.print()
@@ -138,16 +195,45 @@ def _display_session_analytics(console: Console, sdk: SDK, session_id: str, grap
138
195
  burden_label = "[green]Low[/green]"
139
196
  metrics.add_row("Maintenance Burden:", f"{burden_text} {burden_label}")
140
197
 
198
+ # Add transition time metrics
199
+ if transition_metrics.get("total_minutes", 0) > 0:
200
+ trans_pct = transition_metrics["transition_percent"]
201
+ trans_mins = transition_metrics["transition_minutes"]
202
+ feat_mins = transition_metrics["feature_minutes"]
203
+
204
+ # Format transition time
205
+ if trans_pct > 30:
206
+ trans_label = "[yellow]High - Lots of context switching[/yellow]"
207
+ elif trans_pct > 15:
208
+ trans_label = "[cyan]Moderate - Normal transitions[/cyan]"
209
+ else:
210
+ trans_label = "[green]Low - Focused work[/green]"
211
+
212
+ metrics.add_row("", "")
213
+ metrics.add_row(
214
+ "Transition Time:",
215
+ f"{trans_pct:.1f}% ({trans_mins:.0f} min) - {trans_label}",
216
+ )
217
+ metrics.add_row("Feature Work Time:", f"{feat_mins:.0f} minutes")
218
+
141
219
  console.print(Panel(metrics, title="📈 Key Metrics", border_style="green"))
142
220
 
143
221
 
144
- def _display_recent_sessions(console: Console, sdk: SDK, session_files: list, graph_dir: str):
222
+ def _display_recent_sessions(
223
+ console: Console,
224
+ sdk: "SDK",
225
+ session_files: list[Path],
226
+ graph_dir: str,
227
+ quiet: bool,
228
+ ) -> None:
145
229
  """Display analytics for recent sessions."""
146
- console.print(Panel(
147
- f"[bold cyan]Analyzing {len(session_files)} Recent Sessions[/bold cyan]",
148
- title="📊 Recent Sessions Analytics",
149
- border_style="cyan"
150
- ))
230
+ console.print(
231
+ Panel(
232
+ f"[bold cyan]Analyzing {len(session_files)} Recent Sessions[/bold cyan]",
233
+ title="📊 Recent Sessions Analytics",
234
+ border_style="cyan",
235
+ )
236
+ )
151
237
  console.print()
152
238
 
153
239
  # Sessions table
@@ -159,20 +245,26 @@ def _display_recent_sessions(console: Console, sdk: SDK, session_files: list, gr
159
245
  table.add_column("Primary Type", style="yellow")
160
246
  table.add_column("Spike Ratio", justify="right")
161
247
 
162
- for session_path in session_files:
248
+ for session_path in _iter_with_progress(
249
+ console, quiet, session_files, "Processing sessions"
250
+ ):
163
251
  try:
164
252
  session = html_to_session(session_path)
165
253
  session_id = session.id
166
254
 
167
255
  # Get metrics
168
- primary = sdk.analytics.calculate_session_primary_work_type(session_id) or "-"
256
+ primary = (
257
+ sdk.analytics.calculate_session_primary_work_type(session_id) or "-"
258
+ )
169
259
  ratio = sdk.analytics.spike_to_feature_ratio(session_id=session_id)
170
260
  breakdown = sdk.analytics.calculate_session_work_breakdown(session_id)
171
261
  total_events = sum(breakdown.values()) if breakdown else session.event_count
172
262
 
173
263
  # Format primary type
174
264
  if primary and len(primary) > 20:
175
- primary = primary.replace("-implementation", "").replace("-investigation", "")
265
+ primary = primary.replace("-implementation", "").replace(
266
+ "-investigation", ""
267
+ )
176
268
 
177
269
  # Format ratio
178
270
  ratio_str = f"{ratio:.2f}" if ratio > 0 else "-"
@@ -180,10 +272,10 @@ def _display_recent_sessions(console: Console, sdk: SDK, session_files: list, gr
180
272
  table.add_row(
181
273
  session_id[:30] + "..." if len(session_id) > 30 else session_id,
182
274
  session.agent,
183
- session.started_at.strftime('%m-%d %H:%M'),
275
+ session.started_at.strftime("%m-%d %H:%M"),
184
276
  str(total_events),
185
277
  primary,
186
- ratio_str
278
+ ratio_str,
187
279
  )
188
280
  except Exception as e:
189
281
  console.print(f"[dim red]Error loading {session_path.name}: {e}[/dim red]")
@@ -192,20 +284,30 @@ def _display_recent_sessions(console: Console, sdk: SDK, session_files: list, gr
192
284
  console.print(table)
193
285
 
194
286
 
195
- def _display_project_analytics(console: Console, sdk: SDK, session_files: list, graph_dir: str):
287
+ def _display_project_analytics(
288
+ console: Console,
289
+ sdk: "SDK",
290
+ session_files: list[Path],
291
+ graph_dir: str,
292
+ quiet: bool,
293
+ ) -> None:
196
294
  """Display project-wide analytics."""
197
- console.print(Panel(
198
- f"[bold cyan]Project-Wide Analytics[/bold cyan]\n"
199
- f"Analyzing {len(session_files)} total sessions",
200
- title="📊 HtmlGraph Project Analytics",
201
- border_style="cyan"
202
- ))
295
+ console.print(
296
+ Panel(
297
+ f"[bold cyan]Project-Wide Analytics[/bold cyan]\n"
298
+ f"Analyzing {len(session_files)} total sessions",
299
+ title="📊 HtmlGraph Project Analytics",
300
+ border_style="cyan",
301
+ )
302
+ )
203
303
  console.print()
204
304
 
205
305
  # Get project-wide metrics
206
- all_dist = sdk.analytics.work_type_distribution()
207
- all_ratio = sdk.analytics.spike_to_feature_ratio()
208
- all_burden = sdk.analytics.maintenance_burden()
306
+ with _status_context(console, quiet, "Computing project analytics..."):
307
+ all_dist = sdk.analytics.work_type_distribution()
308
+ all_ratio = sdk.analytics.spike_to_feature_ratio()
309
+ all_burden = sdk.analytics.maintenance_burden()
310
+ all_transition = sdk.analytics.transition_time_metrics()
209
311
 
210
312
  # Work distribution table
211
313
  if all_dist:
@@ -214,7 +316,9 @@ def _display_project_analytics(console: Console, sdk: SDK, session_files: list,
214
316
  table.add_column("Percentage", justify="right", style="green")
215
317
  table.add_column("Bar", style="magenta", min_width=50)
216
318
 
217
- for work_type, pct in sorted(all_dist.items(), key=lambda x: x[1], reverse=True):
319
+ for work_type, pct in sorted(
320
+ all_dist.items(), key=lambda x: x[1], reverse=True
321
+ ):
218
322
  bar_length = int(pct / 2) # Scale to 50 chars max
219
323
  bar = "█" * bar_length
220
324
  table.add_row(work_type, f"{pct:.1f}%", bar)
@@ -253,14 +357,38 @@ def _display_project_analytics(console: Console, sdk: SDK, session_files: list,
253
357
  burden_desc = "[green]Low - Mostly New Development[/green]"
254
358
 
255
359
  metrics.add_row("Maintenance Burden:", f"{burden_text} - {burden_desc}")
360
+ metrics.add_row("", "")
361
+
362
+ # Transition Time metrics
363
+ if all_transition.get("total_minutes", 0) > 0:
364
+ trans_pct = all_transition["transition_percent"]
365
+ trans_mins = all_transition["transition_minutes"]
366
+ all_transition["feature_minutes"]
367
+ total_mins = all_transition["total_minutes"]
368
+
369
+ if trans_pct > 30:
370
+ trans_desc = "[yellow]⚠️ High Context Switching Overhead[/yellow]"
371
+ elif trans_pct > 15:
372
+ trans_desc = "[cyan]Moderate Transition Time[/cyan]"
373
+ else:
374
+ trans_desc = "[green]Low - Focused Development[/green]"
256
375
 
257
- console.print(Panel(metrics, title="📈 Project Health Metrics", border_style="green"))
376
+ metrics.add_row(
377
+ "Transition Time:",
378
+ f"{trans_pct:.1f}% ({trans_mins:.0f}m of {total_mins:.0f}m) - {trans_desc}",
379
+ )
380
+
381
+ console.print(
382
+ Panel(metrics, title="📈 Project Health Metrics", border_style="green")
383
+ )
258
384
  console.print()
259
385
 
260
386
  # Session type breakdown
261
387
  spike_sessions = sdk.analytics.get_sessions_by_work_type(WorkType.SPIKE.value)
262
388
  feature_sessions = sdk.analytics.get_sessions_by_work_type(WorkType.FEATURE.value)
263
- maintenance_sessions = sdk.analytics.get_sessions_by_work_type(WorkType.MAINTENANCE.value)
389
+ maintenance_sessions = sdk.analytics.get_sessions_by_work_type(
390
+ WorkType.MAINTENANCE.value
391
+ )
264
392
 
265
393
  session_table = Table(title="Session Types", box=box.SIMPLE)
266
394
  session_table.add_column("Session Type", style="cyan")
@@ -277,18 +405,29 @@ def _display_project_analytics(console: Console, sdk: SDK, session_files: list,
277
405
  console.print("[bold]Recent Sessions:[/bold]")
278
406
  recent_files = session_files[:5]
279
407
 
280
- for session_path in recent_files:
408
+ for session_path in _iter_with_progress(
409
+ console, quiet, recent_files, "Loading recent sessions"
410
+ ):
281
411
  try:
282
412
  session = html_to_session(session_path)
283
- primary = sdk.analytics.calculate_session_primary_work_type(session.id) or "unknown"
413
+ primary = (
414
+ sdk.analytics.calculate_session_primary_work_type(session.id)
415
+ or "unknown"
416
+ )
284
417
 
285
418
  # Shorten work type
286
419
  if primary != "unknown":
287
- primary = primary.replace("-implementation", "").replace("-investigation", "")
420
+ primary = primary.replace("-implementation", "").replace(
421
+ "-investigation", ""
422
+ )
288
423
 
289
- console.print(f" • [cyan]{session.id[:40]}[/cyan] - {session.agent} - [yellow]{primary}[/yellow]")
424
+ console.print(
425
+ f" • [cyan]{session.id[:40]}[/cyan] - {session.agent} - [yellow]{primary}[/yellow]"
426
+ )
290
427
  except Exception:
291
428
  continue
292
429
 
293
430
  console.print()
294
- console.print("[dim]Run 'htmlgraph analytics --recent 10' for detailed recent session analysis[/dim]")
431
+ console.print(
432
+ "[dim]Run 'htmlgraph analytics --recent 10' for detailed recent session analysis[/dim]"
433
+ )