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/find_api.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  BeautifulSoup-style Find API for HtmlGraph.
3
5
 
@@ -22,9 +24,10 @@ Example:
22
24
  nodes = graph.find_all(properties__effort__gt=8)
23
25
  """
24
26
 
25
- from __future__ import annotations
27
+
26
28
  import re
27
- from typing import TYPE_CHECKING, Any, Callable, Iterator
29
+ from collections.abc import Callable
30
+ from typing import TYPE_CHECKING, Any
28
31
 
29
32
  if TYPE_CHECKING:
30
33
  from htmlgraph.graph import HtmlGraph
@@ -56,14 +59,10 @@ class FindAPI:
56
59
  nodes = graph.find_all(title__contains="auth")
57
60
  """
58
61
 
59
- def __init__(self, graph: 'HtmlGraph'):
62
+ def __init__(self, graph: HtmlGraph):
60
63
  self._graph = graph
61
64
 
62
- def find(
63
- self,
64
- type: str | None = None,
65
- **kwargs
66
- ) -> 'Node | None':
65
+ def find(self, type: str | None = None, **kwargs: Any) -> Node | None:
67
66
  """
68
67
  Find the first node matching the given criteria.
69
68
 
@@ -80,7 +79,7 @@ class FindAPI:
80
79
  node = graph.find(properties__effort__gt=8)
81
80
  """
82
81
  if type is not None:
83
- kwargs['type'] = type
82
+ kwargs["type"] = type
84
83
 
85
84
  for node in self._graph:
86
85
  if self._matches(node, kwargs):
@@ -88,11 +87,8 @@ class FindAPI:
88
87
  return None
89
88
 
90
89
  def find_all(
91
- self,
92
- type: str | None = None,
93
- limit: int | None = None,
94
- **kwargs
95
- ) -> list['Node']:
90
+ self, type: str | None = None, limit: int | None = None, **kwargs: Any
91
+ ) -> list[Node]:
96
92
  """
97
93
  Find all nodes matching the given criteria.
98
94
 
@@ -109,7 +105,7 @@ class FindAPI:
109
105
  nodes = graph.find_all(status__in=["todo", "blocked"], limit=10)
110
106
  """
111
107
  if type is not None:
112
- kwargs['type'] = type
108
+ kwargs["type"] = type
113
109
 
114
110
  results = []
115
111
  for node in self._graph:
@@ -119,7 +115,7 @@ class FindAPI:
119
115
  break
120
116
  return results
121
117
 
122
- def find_by_id(self, node_id: str) -> 'Node | None':
118
+ def find_by_id(self, node_id: str) -> Node | None:
123
119
  """
124
120
  Find node by exact ID.
125
121
 
@@ -131,7 +127,7 @@ class FindAPI:
131
127
  """
132
128
  return self._graph.get(node_id)
133
129
 
134
- def find_by_title(self, title: str, exact: bool = False) -> list['Node']:
130
+ def find_by_title(self, title: str, exact: bool = False) -> list[Node]:
135
131
  """
136
132
  Find nodes by title.
137
133
 
@@ -148,11 +144,8 @@ class FindAPI:
148
144
  return self.find_all(title__icontains=title)
149
145
 
150
146
  def find_related(
151
- self,
152
- node_id: str,
153
- relationship: str | None = None,
154
- direction: str = "outgoing"
155
- ) -> list['Node']:
147
+ self, node_id: str, relationship: str | None = None, direction: str = "outgoing"
148
+ ) -> list[Node]:
156
149
  """
157
150
  Find nodes related to a given node.
158
151
 
@@ -165,9 +158,10 @@ class FindAPI:
165
158
  List of related nodes
166
159
  """
167
160
  neighbor_ids = self._graph.get_neighbors(node_id, relationship, direction)
168
- return [self._graph.get(nid) for nid in neighbor_ids if self._graph.get(nid)]
161
+ nodes = [self._graph.get(nid) for nid in neighbor_ids]
162
+ return [n for n in nodes if n is not None]
169
163
 
170
- def find_blocking(self, node_id: str) -> list['Node']:
164
+ def find_blocking(self, node_id: str) -> list[Node]:
171
165
  """
172
166
  Find nodes that are blocking the given node.
173
167
 
@@ -177,9 +171,11 @@ class FindAPI:
177
171
  Returns:
178
172
  List of blocking nodes
179
173
  """
180
- return self.find_related(node_id, relationship="blocked_by", direction="outgoing")
174
+ return self.find_related(
175
+ node_id, relationship="blocked_by", direction="outgoing"
176
+ )
181
177
 
182
- def find_blocked_by(self, node_id: str) -> list['Node']:
178
+ def find_blocked_by(self, node_id: str) -> list[Node]:
183
179
  """
184
180
  Find nodes that are blocked by the given node.
185
181
 
@@ -191,9 +187,10 @@ class FindAPI:
191
187
  """
192
188
  # Nodes that have blocked_by pointing to this node
193
189
  incoming = self._graph.get_incoming_edges(node_id, "blocked_by")
194
- return [self._graph.get(ref.source_id) for ref in incoming if self._graph.get(ref.source_id)]
190
+ nodes = [self._graph.get(ref.source_id) for ref in incoming]
191
+ return [n for n in nodes if n is not None]
195
192
 
196
- def _matches(self, node: 'Node', filters: dict[str, Any]) -> bool:
193
+ def _matches(self, node: Node, filters: dict[str, Any]) -> bool:
197
194
  """
198
195
  Check if a node matches all filters.
199
196
 
@@ -209,7 +206,7 @@ class FindAPI:
209
206
  return False
210
207
  return True
211
208
 
212
- def _check_filter(self, node: 'Node', key: str, expected: Any) -> bool:
209
+ def _check_filter(self, node: Node, key: str, expected: Any) -> bool:
213
210
  """
214
211
  Check a single filter against a node.
215
212
 
@@ -223,33 +220,34 @@ class FindAPI:
223
220
  """
224
221
  # Parse lookup type from key
225
222
  # Check if the last part is a lookup keyword
226
- parts = key.split('__')
223
+ parts = key.split("__")
227
224
 
228
225
  if len(parts) == 1:
229
226
  # Simple attribute
230
227
  attr_path = parts[0]
231
- lookup = 'exact'
228
+ lookup = "exact"
232
229
  elif parts[-1] in self._lookups:
233
230
  # Last part is a lookup
234
- attr_path = '__'.join(parts[:-1])
231
+ attr_path = "__".join(parts[:-1])
235
232
  lookup = parts[-1]
236
233
  else:
237
234
  # All parts are attribute path, use exact match
238
235
  attr_path = key
239
- lookup = 'exact'
236
+ lookup = "exact"
240
237
 
241
238
  # Get actual value
242
239
  actual = self._get_attr(node, attr_path)
243
240
 
244
241
  # Apply lookup
245
242
  lookup_fn = self._lookups.get(lookup, self._exact)
246
- return lookup_fn(actual, expected)
243
+ result: bool = lookup_fn(actual, expected)
244
+ return result
247
245
 
248
- def _get_attr(self, node: 'Node', path: str) -> Any:
246
+ def _get_attr(self, node: Node, path: str) -> Any:
249
247
  """Get attribute value supporting nested access with double underscore."""
250
248
  # Convert double underscore to dot notation for nested access
251
- path = path.replace('__', '.')
252
- parts = path.split('.')
249
+ path = path.replace("__", ".")
250
+ parts = path.split(".")
253
251
  current = node
254
252
 
255
253
  for part in parts:
@@ -266,12 +264,14 @@ class FindAPI:
266
264
 
267
265
  # Lookup functions
268
266
  def _exact(self, actual: Any, expected: Any) -> bool:
269
- return actual == expected
267
+ result: bool = actual == expected
268
+ return result
270
269
 
271
270
  def _iexact(self, actual: Any, expected: Any) -> bool:
272
271
  if actual is None:
273
272
  return expected is None
274
- return str(actual).lower() == str(expected).lower()
273
+ result: bool = str(actual).lower() == str(expected).lower()
274
+ return result
275
275
 
276
276
  def _contains(self, actual: Any, expected: Any) -> bool:
277
277
  if actual is None:
@@ -312,7 +312,11 @@ class FindAPI:
312
312
  def _iregex(self, actual: Any, expected: Any) -> bool:
313
313
  if actual is None:
314
314
  return False
315
- pattern = expected if isinstance(expected, re.Pattern) else re.compile(expected, re.IGNORECASE)
315
+ pattern = (
316
+ expected
317
+ if isinstance(expected, re.Pattern)
318
+ else re.compile(expected, re.IGNORECASE)
319
+ )
316
320
  return bool(pattern.search(str(actual)))
317
321
 
318
322
  def _gt(self, actual: Any, expected: Any) -> bool:
@@ -342,38 +346,44 @@ class FindAPI:
342
346
  if actual is None:
343
347
  return False
344
348
  try:
345
- actual_num = float(actual) if not isinstance(actual, (int, float)) else actual
346
- expected_num = float(expected) if not isinstance(expected, (int, float)) else expected
347
- return op(actual_num, expected_num)
349
+ actual_num = (
350
+ float(actual) if not isinstance(actual, (int, float)) else actual
351
+ )
352
+ expected_num = (
353
+ float(expected) if not isinstance(expected, (int, float)) else expected
354
+ )
355
+ result: bool = op(actual_num, expected_num)
356
+ return result
348
357
  except (ValueError, TypeError):
349
- return op(str(actual), str(expected))
358
+ result2: bool = op(str(actual), str(expected))
359
+ return result2
350
360
 
351
361
  @property
352
362
  def _lookups(self) -> dict[str, Callable]:
353
363
  """Available lookup functions."""
354
364
  return {
355
- 'exact': self._exact,
356
- 'iexact': self._iexact,
357
- 'contains': self._contains,
358
- 'icontains': self._icontains,
359
- 'startswith': self._startswith,
360
- 'istartswith': self._istartswith,
361
- 'endswith': self._endswith,
362
- 'iendswith': self._iendswith,
363
- 'regex': self._regex,
364
- 'iregex': self._iregex,
365
- 'gt': self._gt,
366
- 'gte': self._gte,
367
- 'lt': self._lt,
368
- 'lte': self._lte,
369
- 'in': self._in,
370
- 'not_in': self._not_in,
371
- 'isnull': self._isnull,
365
+ "exact": self._exact,
366
+ "iexact": self._iexact,
367
+ "contains": self._contains,
368
+ "icontains": self._icontains,
369
+ "startswith": self._startswith,
370
+ "istartswith": self._istartswith,
371
+ "endswith": self._endswith,
372
+ "iendswith": self._iendswith,
373
+ "regex": self._regex,
374
+ "iregex": self._iregex,
375
+ "gt": self._gt,
376
+ "gte": self._gte,
377
+ "lt": self._lt,
378
+ "lte": self._lte,
379
+ "in": self._in,
380
+ "not_in": self._not_in,
381
+ "isnull": self._isnull,
372
382
  }
373
383
 
374
384
 
375
385
  # Convenience functions for direct import
376
- def find(graph: 'HtmlGraph', type: str | None = None, **kwargs) -> 'Node | None':
386
+ def find(graph: HtmlGraph, type: str | None = None, **kwargs: Any) -> Node | None:
377
387
  """
378
388
  Find first node matching criteria.
379
389
 
@@ -388,7 +398,9 @@ def find(graph: 'HtmlGraph', type: str | None = None, **kwargs) -> 'Node | None'
388
398
  return FindAPI(graph).find(type=type, **kwargs)
389
399
 
390
400
 
391
- def find_all(graph: 'HtmlGraph', type: str | None = None, limit: int | None = None, **kwargs) -> list['Node']:
401
+ def find_all(
402
+ graph: HtmlGraph, type: str | None = None, limit: int | None = None, **kwargs: Any
403
+ ) -> list[Node]:
392
404
  """
393
405
  Find all nodes matching criteria.
394
406