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
htmlgraph/agents.py CHANGED
@@ -1,19 +1,85 @@
1
1
  """
2
- Agent interface for HtmlGraph.
3
-
4
- Provides a simplified API for AI agents with:
5
- - Lightweight context generation
6
- - Task claiming and completion
7
- - Progress tracking
2
+ Agent interface for simplified task management.
3
+
4
+ IMPERATIVE USAGE INSTRUCTIONS
5
+ =============================
6
+
7
+ The AgentInterface provides lightweight task claiming and progress tracking.
8
+
9
+ TASK LIFECYCLE
10
+ ==============
11
+
12
+ 1. CLAIM A TASK
13
+ ```python
14
+ from htmlgraph.agents import AgentInterface
15
+
16
+ agent = AgentInterface("features/")
17
+ task = agent.get_next_task(
18
+ agent_id="claude",
19
+ priority="high", # Optional filter
20
+ auto_claim=True # Automatically claim the task
21
+ )
22
+ ```
23
+
24
+ 2. WORK ON TASK
25
+ ```python
26
+ # Get lightweight context for LLM
27
+ context = agent.get_context(task.id)
28
+ # Returns: "# feature-001: Title\\nStatus: in-progress..."
29
+
30
+ # Complete steps as you work
31
+ agent.complete_step(task.id, step_index=0, agent_id="claude")
32
+ agent.complete_step(task.id, step_index=1, agent_id="claude")
33
+ ```
34
+
35
+ 3. COMPLETE TASK
36
+ ```python
37
+ agent.complete_task(task.id, agent_id="claude")
38
+ ```
39
+
40
+ 4. IF BLOCKED
41
+ ```python
42
+ agent.release_task(task.id, agent_id="claude")
43
+ # Task becomes available for other agents
44
+ ```
45
+
46
+ DISCOVERY METHODS
47
+ =================
48
+
49
+ | Method | Purpose |
50
+ |--------|---------|
51
+ | `get_available_tasks()` | Find all matching tasks |
52
+ | `get_next_task()` | Get single next task |
53
+ | `get_blocked_tasks()` | Find blocked work |
54
+ | `get_in_progress_tasks()` | Get agent's active work |
55
+
56
+ ANTI-PATTERNS
57
+ =============
58
+
59
+ NEVER:
60
+ - Work on unclaimed tasks
61
+ - Skip step completion updates
62
+ - Leave tasks in-progress when blocked
63
+
64
+ ALWAYS:
65
+ - Claim before working
66
+ - Update progress incrementally
67
+ - Release if you can't complete
68
+
69
+ Available Classes
70
+ =================
71
+
72
+ AgentInterface: Simplified interface for task management
73
+ AgentRegistry: Capability-based agent routing
8
74
  """
9
75
 
10
76
  from datetime import datetime
11
77
  from pathlib import Path
12
- from typing import Any
78
+ from typing import Any, Literal, cast
13
79
 
14
- from htmlgraph.models import Node, Step
80
+ from htmlgraph.agent_registry import AgentProfile, AgentRegistry
15
81
  from htmlgraph.graph import HtmlGraph
16
- from htmlgraph.agent_registry import AgentRegistry, AgentProfile
82
+ from htmlgraph.models import Node, Step
17
83
 
18
84
 
19
85
  class AgentInterface:
@@ -34,11 +100,7 @@ class AgentInterface:
34
100
  agent.complete_task(task.id, agent_id="claude")
35
101
  """
36
102
 
37
- def __init__(
38
- self,
39
- directory: Path | str,
40
- agent_id: str | None = None
41
- ):
103
+ def __init__(self, directory: Path | str, agent_id: str | None = None):
42
104
  """
43
105
  Initialize agent interface.
44
106
 
@@ -51,7 +113,11 @@ class AgentInterface:
51
113
 
52
114
  # Initialize agent registry for capability-based routing
53
115
  # Assumes .htmlgraph is parent of directory
54
- htmlgraph_dir = Path(directory).parent if Path(directory).name == "features" else Path(directory)
116
+ htmlgraph_dir = (
117
+ Path(directory).parent
118
+ if Path(directory).name == "features"
119
+ else Path(directory)
120
+ )
55
121
  self.registry = AgentRegistry(htmlgraph_dir)
56
122
 
57
123
  def reload(self) -> None:
@@ -67,7 +133,7 @@ class AgentInterface:
67
133
  status: str = "todo",
68
134
  priority: str | None = None,
69
135
  node_type: str | None = None,
70
- limit: int = 10
136
+ limit: int = 10,
71
137
  ) -> list[Node]:
72
138
  """
73
139
  Get available tasks matching criteria.
@@ -81,6 +147,7 @@ class AgentInterface:
81
147
  Returns:
82
148
  List of matching nodes, sorted by priority
83
149
  """
150
+
84
151
  def matches(node: Node) -> bool:
85
152
  if node.status != status:
86
153
  return False
@@ -106,7 +173,7 @@ class AgentInterface:
106
173
  agent_id: str | None = None,
107
174
  priority: str | None = None,
108
175
  node_type: str | None = None,
109
- auto_claim: bool = False
176
+ auto_claim: bool = False,
110
177
  ) -> Node | None:
111
178
  """
112
179
  Get the next available task.
@@ -122,9 +189,7 @@ class AgentInterface:
122
189
  """
123
190
  agent_id = agent_id or self.agent_id
124
191
  tasks = self.get_available_tasks(
125
- priority=priority,
126
- node_type=node_type,
127
- limit=1
192
+ priority=priority, node_type=node_type, limit=1
128
193
  )
129
194
 
130
195
  if not tasks:
@@ -135,7 +200,9 @@ class AgentInterface:
135
200
  if auto_claim and agent_id:
136
201
  self.claim_task(task.id, agent_id)
137
202
  # Reload to get updated state
138
- task = self.graph.get(task.id)
203
+ reloaded_task = self.graph.get(task.id)
204
+ if reloaded_task:
205
+ task = reloaded_task
139
206
 
140
207
  return task
141
208
 
@@ -158,10 +225,7 @@ class AgentInterface:
158
225
  return tasks
159
226
 
160
227
  def get_tasks_by_capability(
161
- self,
162
- agent_capabilities: list[str],
163
- status: str = "todo",
164
- limit: int = 10
228
+ self, agent_capabilities: list[str], status: str = "todo", limit: int = 10
165
229
  ) -> list[Node]:
166
230
  """
167
231
  Get tasks that match agent capabilities.
@@ -176,6 +240,7 @@ class AgentInterface:
176
240
  Returns:
177
241
  List of matching nodes, prioritized by exact matches first
178
242
  """
243
+
179
244
  def matches(node: Node) -> bool:
180
245
  if node.status != status:
181
246
  return False
@@ -193,10 +258,11 @@ class AgentInterface:
193
258
  # Sort by capability match quality
194
259
  agent_caps = set(agent_capabilities)
195
260
 
196
- def capability_score(node: Node) -> tuple[int, str]:
197
- """Return (negative_exact_matches, priority) for sorting."""
261
+ def capability_score(node: Node) -> tuple[int, int]:
262
+ """Return (negative_exact_matches, priority_order) for sorting."""
198
263
  if not node.required_capabilities:
199
- return (0, node.priority)
264
+ priority_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
265
+ return (0, priority_order.get(node.priority, 99))
200
266
  exact_matches = len(set(node.required_capabilities) & agent_caps)
201
267
  # Sort by exact matches (descending), then by priority
202
268
  priority_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
@@ -210,7 +276,7 @@ class AgentInterface:
210
276
  self,
211
277
  agent_capabilities: list[str],
212
278
  agent_id: str | None = None,
213
- auto_claim: bool = False
279
+ auto_claim: bool = False,
214
280
  ) -> Node | None:
215
281
  """
216
282
  Get next task matching agent capabilities.
@@ -234,7 +300,9 @@ class AgentInterface:
234
300
  if auto_claim and agent_id:
235
301
  self.claim_task(task.id, agent_id)
236
302
  # Reload to get updated state
237
- task = self.graph.get(task.id)
303
+ reloaded_task = self.graph.get(task.id)
304
+ if reloaded_task:
305
+ task = reloaded_task
238
306
 
239
307
  return task
240
308
 
@@ -332,10 +400,7 @@ class AgentInterface:
332
400
  return True
333
401
 
334
402
  def block_task(
335
- self,
336
- node_id: str,
337
- blocked_by: str,
338
- reason: str | None = None
403
+ self, node_id: str, blocked_by: str, reason: str | None = None
339
404
  ) -> bool:
340
405
  """
341
406
  Mark a task as blocked.
@@ -357,11 +422,12 @@ class AgentInterface:
357
422
 
358
423
  # Add blocking edge if not present
359
424
  from htmlgraph.models import Edge
425
+
360
426
  blocking_edge = Edge(
361
427
  target_id=blocked_by,
362
428
  relationship="blocked_by",
363
429
  since=datetime.now(),
364
- properties={"reason": reason} if reason else {}
430
+ properties={"reason": reason} if reason else {},
365
431
  )
366
432
  node.add_edge(blocking_edge)
367
433
 
@@ -373,10 +439,7 @@ class AgentInterface:
373
439
  # =========================================================================
374
440
 
375
441
  def complete_step(
376
- self,
377
- node_id: str,
378
- step_index: int,
379
- agent_id: str | None = None
442
+ self, node_id: str, step_index: int, agent_id: str | None = None
380
443
  ) -> bool:
381
444
  """
382
445
  Mark a step as completed.
@@ -401,11 +464,7 @@ class AgentInterface:
401
464
 
402
465
  return False
403
466
 
404
- def add_step(
405
- self,
406
- node_id: str,
407
- description: str
408
- ) -> bool:
467
+ def add_step(self, node_id: str, description: str) -> bool:
409
468
  """
410
469
  Add a new step to a task.
411
470
 
@@ -532,9 +591,7 @@ class AgentInterface:
532
591
  # =========================================================================
533
592
 
534
593
  def find_bottlenecks(
535
- self,
536
- top_n: int = 5,
537
- status_filter: list[str] | None = None
594
+ self, top_n: int = 5, status_filter: list[str] | None = None
538
595
  ) -> list[dict[str, Any]]:
539
596
  """
540
597
  Identify tasks blocking the most downstream work.
@@ -555,8 +612,7 @@ class AgentInterface:
555
612
 
556
613
  analytics = DependencyAnalytics(self.graph)
557
614
  bottlenecks = analytics.find_bottlenecks(
558
- status_filter=status_filter,
559
- top_n=top_n
615
+ status_filter=status_filter, top_n=top_n
560
616
  )
561
617
 
562
618
  # Convert to agent-friendly dict format
@@ -568,15 +624,13 @@ class AgentInterface:
568
624
  "priority": bn.priority,
569
625
  "blocks_count": bn.transitive_blocking,
570
626
  "impact_score": bn.weighted_impact,
571
- "blocked_tasks": bn.blocked_nodes[:5] # Limit for readability
627
+ "blocked_tasks": bn.blocked_nodes[:5], # Limit for readability
572
628
  }
573
629
  for bn in bottlenecks
574
630
  ]
575
631
 
576
632
  def get_parallel_work(
577
- self,
578
- status: str = "todo",
579
- max_agents: int = 5
633
+ self, status: str = "todo", max_agents: int = 5
580
634
  ) -> dict[str, Any]:
581
635
  """
582
636
  Find tasks that can be worked on simultaneously.
@@ -598,20 +652,22 @@ class AgentInterface:
598
652
  analytics = DependencyAnalytics(self.graph)
599
653
  report = analytics.find_parallelizable_work(status=status)
600
654
 
601
- ready_now = report.dependency_levels[0].nodes if report.dependency_levels else []
655
+ ready_now = (
656
+ report.dependency_levels[0].nodes if report.dependency_levels else []
657
+ )
602
658
 
603
659
  return {
604
660
  "max_parallelism": report.max_parallelism,
605
661
  "ready_now": ready_now[:max_agents],
606
662
  "total_ready": len(ready_now),
607
663
  "level_count": len(report.dependency_levels),
608
- "next_level": report.dependency_levels[1].nodes if len(report.dependency_levels) > 1 else []
664
+ "next_level": report.dependency_levels[1].nodes
665
+ if len(report.dependency_levels) > 1
666
+ else [],
609
667
  }
610
668
 
611
669
  def recommend_next_work(
612
- self,
613
- agent_count: int = 1,
614
- lookahead: int = 5
670
+ self, agent_count: int = 1, lookahead: int = 5
615
671
  ) -> list[dict[str, Any]]:
616
672
  """
617
673
  Get smart recommendations for what to work on next.
@@ -635,8 +691,7 @@ class AgentInterface:
635
691
 
636
692
  analytics = DependencyAnalytics(self.graph)
637
693
  recommendations = analytics.recommend_next_tasks(
638
- agent_count=agent_count,
639
- lookahead=lookahead
694
+ agent_count=agent_count, lookahead=lookahead
640
695
  )
641
696
 
642
697
  return [
@@ -648,7 +703,7 @@ class AgentInterface:
648
703
  "reasons": rec.reasons,
649
704
  "estimated_hours": rec.estimated_effort,
650
705
  "unlocks_count": len(rec.unlocks),
651
- "unlocks": rec.unlocks[:3] # Show first 3
706
+ "unlocks": rec.unlocks[:3], # Show first 3
652
707
  }
653
708
  for rec in recommendations.recommendations
654
709
  ]
@@ -682,14 +737,14 @@ class AgentInterface:
682
737
  "id": node.id,
683
738
  "title": node.title,
684
739
  "risk_score": node.risk_score,
685
- "risk_factors": [f.description for f in node.risk_factors]
740
+ "risk_factors": [f.description for f in node.risk_factors],
686
741
  }
687
742
  for node in risk.high_risk
688
743
  ],
689
744
  "circular_dependencies": risk.circular_dependencies,
690
745
  "orphaned_count": len(risk.orphaned_nodes),
691
746
  "orphaned_tasks": risk.orphaned_nodes[:5], # First 5
692
- "recommendations": risk.recommendations
747
+ "recommendations": risk.recommendations,
693
748
  }
694
749
 
695
750
  def analyze_impact(self, node_id: str) -> dict[str, Any]:
@@ -720,7 +775,7 @@ class AgentInterface:
720
775
  "total_impact": impact.transitive_dependents,
721
776
  "completion_impact": impact.completion_impact,
722
777
  "unlocks_count": len(impact.affected_nodes),
723
- "affected_tasks": impact.affected_nodes[:10] # First 10
778
+ "affected_tasks": impact.affected_nodes[:10], # First 10
724
779
  }
725
780
 
726
781
  # =========================================================================
@@ -734,7 +789,7 @@ class AgentInterface:
734
789
  description: str = "",
735
790
  priority: str = "medium",
736
791
  node_type: str = "task",
737
- steps: list[str] | None = None
792
+ steps: list[str] | None = None,
738
793
  ) -> Node:
739
794
  """
740
795
  Create a new task.
@@ -754,9 +809,9 @@ class AgentInterface:
754
809
  id=task_id,
755
810
  title=title,
756
811
  type=node_type,
757
- priority=priority,
812
+ priority=cast(Literal["low", "medium", "high", "critical"], priority),
758
813
  content=f"<p>{description}</p>" if description else "",
759
- steps=[Step(description=s) for s in (steps or [])]
814
+ steps=[Step(description=s) for s in (steps or [])],
760
815
  )
761
816
 
762
817
  self.graph.add(node)
@@ -785,7 +840,7 @@ class AgentInterface:
785
840
  "agent_id": agent_id,
786
841
  "in_progress": len(in_progress),
787
842
  "completed": len(completed),
788
- "tasks": [t.id for t in in_progress]
843
+ "tasks": [t.id for t in in_progress],
789
844
  }
790
845
 
791
846
  # =========================================================================
@@ -793,10 +848,7 @@ class AgentInterface:
793
848
  # =========================================================================
794
849
 
795
850
  def calculate_task_score(
796
- self,
797
- task: Node,
798
- agent: AgentProfile,
799
- current_workload: int = 0
851
+ self, task: Node, agent: AgentProfile, current_workload: int = 0
800
852
  ) -> float:
801
853
  """
802
854
  Calculate routing score for a task-agent pair.
@@ -814,12 +866,7 @@ class AgentInterface:
814
866
  score = 0.0
815
867
 
816
868
  # Priority score (0-30 points)
817
- priority_scores = {
818
- "critical": 30,
819
- "high": 20,
820
- "medium": 10,
821
- "low": 5
822
- }
869
+ priority_scores = {"critical": 30, "high": 20, "medium": 10, "low": 5}
823
870
  score += priority_scores.get(task.priority, 5)
824
871
 
825
872
  # Capability match score (0-40 points)
@@ -828,24 +875,27 @@ class AgentInterface:
828
875
  # Perfect match gets 40 points
829
876
  score += 40
830
877
  # Bonus for exact capability match (no extra capabilities)
831
- matching_caps = sum(1 for cap in task.required_capabilities if cap in agent.capabilities)
878
+ matching_caps = sum(
879
+ 1 for cap in task.required_capabilities if cap in agent.capabilities
880
+ )
832
881
  score += (matching_caps / len(task.required_capabilities)) * 5
833
882
  else:
834
883
  # No match = very low score
835
884
  score = max(score - 30, 0)
836
885
 
837
886
  # Complexity match score (0-20 points)
838
- if task.complexity:
839
- if agent.can_handle_complexity(task.complexity):
887
+ task_complexity = getattr(task, "complexity", None)
888
+ if task_complexity:
889
+ if agent.can_handle_complexity(task_complexity):
840
890
  score += 20
841
891
  # Bonus for preferred complexity
842
892
  complexity_preference = {
843
893
  "low": 2,
844
894
  "medium": 5,
845
895
  "high": 3,
846
- "very-high": 1
896
+ "very-high": 1,
847
897
  }
848
- score += complexity_preference.get(task.complexity, 0)
898
+ score += complexity_preference.get(task_complexity, 0)
849
899
  else:
850
900
  score = max(score - 15, 0)
851
901
 
@@ -865,9 +915,7 @@ class AgentInterface:
865
915
  return len(in_progress)
866
916
 
867
917
  def find_best_match(
868
- self,
869
- task: Node,
870
- candidate_agents: list[str] | None = None
918
+ self, task: Node, candidate_agents: list[str] | None = None
871
919
  ) -> tuple[str, float] | None:
872
920
  """
873
921
  Find the best agent for a task using smart routing.
@@ -880,15 +928,16 @@ class AgentInterface:
880
928
  Tuple of (agent_id, score) or None if no match
881
929
  """
882
930
  # Get candidate agents
931
+ agents: list[AgentProfile]
883
932
  if candidate_agents:
884
- agents = [self.registry.get(aid) for aid in candidate_agents]
885
- agents = [a for a in agents if a and a.active]
933
+ agents_maybe = [self.registry.get(aid) for aid in candidate_agents]
934
+ agents = [a for a in agents_maybe if a and a.active]
886
935
  else:
887
936
  # Find capable agents based on requirements
888
- if task.required_capabilities:
937
+ required_caps = getattr(task, "required_capabilities", None)
938
+ if required_caps:
889
939
  agents = self.registry.find_capable_agents(
890
- task.required_capabilities,
891
- task.complexity
940
+ required_caps, getattr(task, "complexity", None)
892
941
  )
893
942
  else:
894
943
  # No requirements, all active agents
@@ -914,10 +963,7 @@ class AgentInterface:
914
963
  return None
915
964
 
916
965
  def get_work_queue(
917
- self,
918
- agent_id: str | None = None,
919
- limit: int = 10,
920
- min_score: float = 20.0
966
+ self, agent_id: str | None = None, limit: int = 10, min_score: float = 20.0
921
967
  ) -> list[dict[str, Any]]:
922
968
  """
923
969
  Get prioritized work queue for an agent using smart routing.
@@ -949,19 +995,26 @@ class AgentInterface:
949
995
  score = self.calculate_task_score(task, agent, workload)
950
996
 
951
997
  if score >= min_score:
952
- queue.append({
953
- "task_id": task.id,
954
- "title": task.title,
955
- "priority": task.priority,
956
- "status": task.status,
957
- "score": round(score, 2),
958
- "required_capabilities": task.required_capabilities,
959
- "complexity": task.complexity,
960
- "estimated_effort": task.estimated_effort
961
- })
998
+ queue.append(
999
+ {
1000
+ "task_id": task.id,
1001
+ "title": task.title,
1002
+ "priority": task.priority,
1003
+ "status": task.status,
1004
+ "score": round(score, 2),
1005
+ "required_capabilities": getattr(
1006
+ task, "required_capabilities", None
1007
+ ),
1008
+ "complexity": getattr(task, "complexity", None),
1009
+ "estimated_effort": getattr(task, "estimated_effort", None),
1010
+ }
1011
+ )
962
1012
 
963
1013
  # Sort by score (highest first)
964
- queue.sort(key=lambda x: x["score"], reverse=True)
1014
+ queue.sort(
1015
+ key=lambda x: float(x["score"]) if x["score"] is not None else 0.0,
1016
+ reverse=True,
1017
+ )
965
1018
 
966
1019
  return queue[:limit]
967
1020
 
@@ -969,7 +1022,7 @@ class AgentInterface:
969
1022
  self,
970
1023
  agent_id: str | None = None,
971
1024
  auto_claim: bool = False,
972
- min_score: float = 20.0
1025
+ min_score: float = 20.0,
973
1026
  ) -> Node | None:
974
1027
  """
975
1028
  Get next task using smart routing based on capabilities.
@@ -1,13 +1,20 @@
1
1
  """
2
2
  Analytics modules for HtmlGraph.
3
3
 
4
- Provides work type analysis, dependency analytics, and CLI analytics.
4
+ Provides work type analysis, dependency analytics, cross-session analytics, CLI analytics,
5
+ and cost attribution analysis for OTEL ROI.
5
6
  """
6
7
 
7
- from htmlgraph.analytics.work_type import Analytics
8
+ from htmlgraph.analytics.cost_analyzer import CostAnalyzer
9
+ from htmlgraph.analytics.cost_reporter import CostReporter
10
+ from htmlgraph.analytics.cross_session import CrossSessionAnalytics
8
11
  from htmlgraph.analytics.dependency import DependencyAnalytics
12
+ from htmlgraph.analytics.work_type import Analytics
9
13
 
10
14
  __all__ = [
11
15
  "Analytics",
12
16
  "DependencyAnalytics",
17
+ "CrossSessionAnalytics",
18
+ "CostAnalyzer",
19
+ "CostReporter",
13
20
  ]