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
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Fluent Query Builder for HtmlGraph.
3
5
 
@@ -24,11 +26,12 @@ Example:
24
26
  .execute()
25
27
  """
26
28
 
27
- from __future__ import annotations
29
+
28
30
  import re
31
+ from collections.abc import Callable, Iterator
29
32
  from dataclasses import dataclass, field
30
33
  from enum import Enum
31
- from typing import TYPE_CHECKING, Any, Callable, Iterator
34
+ from typing import TYPE_CHECKING, Any
32
35
 
33
36
  if TYPE_CHECKING:
34
37
  from htmlgraph.graph import HtmlGraph
@@ -37,25 +40,27 @@ if TYPE_CHECKING:
37
40
 
38
41
  class Operator(Enum):
39
42
  """Query operators for comparisons."""
40
- EQ = "eq" # Equal
41
- NE = "ne" # Not equal
42
- GT = "gt" # Greater than
43
- GTE = "gte" # Greater than or equal
44
- LT = "lt" # Less than
45
- LTE = "lte" # Less than or equal
46
- IN = "in" # In list
47
- NOT_IN = "not_in" # Not in list
48
- BETWEEN = "between" # Between two values (inclusive)
49
- CONTAINS = "contains" # String contains
43
+
44
+ EQ = "eq" # Equal
45
+ NE = "ne" # Not equal
46
+ GT = "gt" # Greater than
47
+ GTE = "gte" # Greater than or equal
48
+ LT = "lt" # Less than
49
+ LTE = "lte" # Less than or equal
50
+ IN = "in" # In list
51
+ NOT_IN = "not_in" # Not in list
52
+ BETWEEN = "between" # Between two values (inclusive)
53
+ CONTAINS = "contains" # String contains
50
54
  STARTS_WITH = "starts_with"
51
55
  ENDS_WITH = "ends_with"
52
- MATCHES = "matches" # Regex match
56
+ MATCHES = "matches" # Regex match
53
57
  IS_NULL = "is_null"
54
58
  IS_NOT_NULL = "is_not_null"
55
59
 
56
60
 
57
61
  class LogicalOp(Enum):
58
62
  """Logical operators for combining conditions."""
63
+
59
64
  AND = "and"
60
65
  OR = "or"
61
66
  NOT = "not"
@@ -64,12 +69,13 @@ class LogicalOp(Enum):
64
69
  @dataclass
65
70
  class Condition:
66
71
  """A single query condition."""
72
+
67
73
  attribute: str
68
74
  operator: Operator
69
75
  value: Any = None
70
76
  logical_op: LogicalOp = LogicalOp.AND
71
77
 
72
- def evaluate(self, node: 'Node') -> bool:
78
+ def evaluate(self, node: Node) -> bool:
73
79
  """Evaluate this condition against a node."""
74
80
  # Get attribute value with nested access support
75
81
  actual = _get_nested_attr(node, self.attribute)
@@ -86,9 +92,11 @@ class Condition:
86
92
 
87
93
  # Evaluate based on operator
88
94
  if self.operator == Operator.EQ:
89
- return actual == self.value
95
+ result: bool = actual == self.value
96
+ return result
90
97
  elif self.operator == Operator.NE:
91
- return actual != self.value
98
+ result2: bool = actual != self.value
99
+ return result2
92
100
  elif self.operator == Operator.GT:
93
101
  return _compare_numeric(actual, self.value, lambda a, b: a > b)
94
102
  elif self.operator == Operator.GTE:
@@ -103,8 +111,9 @@ class Condition:
103
111
  return actual not in self.value
104
112
  elif self.operator == Operator.BETWEEN:
105
113
  low, high = self.value
106
- return _compare_numeric(actual, low, lambda a, b: a >= b) and \
107
- _compare_numeric(actual, high, lambda a, b: a <= b)
114
+ return _compare_numeric(
115
+ actual, low, lambda a, b: a >= b
116
+ ) and _compare_numeric(actual, high, lambda a, b: a <= b)
108
117
  elif self.operator == Operator.CONTAINS:
109
118
  return self.value.lower() in str(actual).lower()
110
119
  elif self.operator == Operator.STARTS_WITH:
@@ -112,7 +121,11 @@ class Condition:
112
121
  elif self.operator == Operator.ENDS_WITH:
113
122
  return str(actual).lower().endswith(self.value.lower())
114
123
  elif self.operator == Operator.MATCHES:
115
- pattern = self.value if isinstance(self.value, re.Pattern) else re.compile(self.value)
124
+ pattern = (
125
+ self.value
126
+ if isinstance(self.value, re.Pattern)
127
+ else re.compile(self.value)
128
+ )
116
129
  return bool(pattern.search(str(actual)))
117
130
 
118
131
  return False
@@ -168,11 +181,15 @@ def _compare_numeric(actual: Any, expected: Any, comparator: Callable) -> bool:
168
181
  try:
169
182
  # Convert to float for numeric comparison
170
183
  actual_num = float(actual) if not isinstance(actual, (int, float)) else actual
171
- expected_num = float(expected) if not isinstance(expected, (int, float)) else expected
172
- return comparator(actual_num, expected_num)
184
+ expected_num = (
185
+ float(expected) if not isinstance(expected, (int, float)) else expected
186
+ )
187
+ result: bool = comparator(actual_num, expected_num)
188
+ return result
173
189
  except (ValueError, TypeError):
174
190
  # Fall back to string comparison
175
- return comparator(str(actual), str(expected))
191
+ result2: bool = comparator(str(actual), str(expected))
192
+ return result2
176
193
 
177
194
 
178
195
  @dataclass
@@ -187,77 +204,77 @@ class ConditionBuilder:
187
204
  query.where("completion").gt(50)
188
205
  """
189
206
 
190
- _query_builder: 'QueryBuilder'
207
+ _query_builder: QueryBuilder
191
208
  _attribute: str
192
209
  _logical_op: LogicalOp = LogicalOp.AND
193
210
 
194
- def eq(self, value: Any) -> 'QueryBuilder':
211
+ def eq(self, value: Any) -> QueryBuilder:
195
212
  """Equal to value."""
196
213
  return self._add_condition(Operator.EQ, value)
197
214
 
198
- def ne(self, value: Any) -> 'QueryBuilder':
215
+ def ne(self, value: Any) -> QueryBuilder:
199
216
  """Not equal to value."""
200
217
  return self._add_condition(Operator.NE, value)
201
218
 
202
- def gt(self, value: Any) -> 'QueryBuilder':
219
+ def gt(self, value: Any) -> QueryBuilder:
203
220
  """Greater than value."""
204
221
  return self._add_condition(Operator.GT, value)
205
222
 
206
- def gte(self, value: Any) -> 'QueryBuilder':
223
+ def gte(self, value: Any) -> QueryBuilder:
207
224
  """Greater than or equal to value."""
208
225
  return self._add_condition(Operator.GTE, value)
209
226
 
210
- def lt(self, value: Any) -> 'QueryBuilder':
227
+ def lt(self, value: Any) -> QueryBuilder:
211
228
  """Less than value."""
212
229
  return self._add_condition(Operator.LT, value)
213
230
 
214
- def lte(self, value: Any) -> 'QueryBuilder':
231
+ def lte(self, value: Any) -> QueryBuilder:
215
232
  """Less than or equal to value."""
216
233
  return self._add_condition(Operator.LTE, value)
217
234
 
218
- def in_(self, values: list) -> 'QueryBuilder':
235
+ def in_(self, values: list) -> QueryBuilder:
219
236
  """Value is in list."""
220
237
  return self._add_condition(Operator.IN, values)
221
238
 
222
- def not_in(self, values: list) -> 'QueryBuilder':
239
+ def not_in(self, values: list) -> QueryBuilder:
223
240
  """Value is not in list."""
224
241
  return self._add_condition(Operator.NOT_IN, values)
225
242
 
226
- def between(self, low: Any, high: Any) -> 'QueryBuilder':
243
+ def between(self, low: Any, high: Any) -> QueryBuilder:
227
244
  """Value is between low and high (inclusive)."""
228
245
  return self._add_condition(Operator.BETWEEN, (low, high))
229
246
 
230
- def contains(self, substring: str) -> 'QueryBuilder':
247
+ def contains(self, substring: str) -> QueryBuilder:
231
248
  """String contains substring (case-insensitive)."""
232
249
  return self._add_condition(Operator.CONTAINS, substring)
233
250
 
234
- def starts_with(self, prefix: str) -> 'QueryBuilder':
251
+ def starts_with(self, prefix: str) -> QueryBuilder:
235
252
  """String starts with prefix (case-insensitive)."""
236
253
  return self._add_condition(Operator.STARTS_WITH, prefix)
237
254
 
238
- def ends_with(self, suffix: str) -> 'QueryBuilder':
255
+ def ends_with(self, suffix: str) -> QueryBuilder:
239
256
  """String ends with suffix (case-insensitive)."""
240
257
  return self._add_condition(Operator.ENDS_WITH, suffix)
241
258
 
242
- def matches(self, pattern: str | re.Pattern) -> 'QueryBuilder':
259
+ def matches(self, pattern: str | re.Pattern) -> QueryBuilder:
243
260
  """String matches regex pattern."""
244
261
  return self._add_condition(Operator.MATCHES, pattern)
245
262
 
246
- def is_null(self) -> 'QueryBuilder':
263
+ def is_null(self) -> QueryBuilder:
247
264
  """Attribute is None/null."""
248
265
  return self._add_condition(Operator.IS_NULL, None)
249
266
 
250
- def is_not_null(self) -> 'QueryBuilder':
267
+ def is_not_null(self) -> QueryBuilder:
251
268
  """Attribute is not None/null."""
252
269
  return self._add_condition(Operator.IS_NOT_NULL, None)
253
270
 
254
- def _add_condition(self, operator: Operator, value: Any) -> 'QueryBuilder':
271
+ def _add_condition(self, operator: Operator, value: Any) -> QueryBuilder:
255
272
  """Add condition and return query builder for chaining."""
256
273
  condition = Condition(
257
274
  attribute=self._attribute,
258
275
  operator=operator,
259
276
  value=value,
260
- logical_op=self._logical_op
277
+ logical_op=self._logical_op,
261
278
  )
262
279
  self._query_builder._conditions.append(condition)
263
280
  return self._query_builder
@@ -282,13 +299,15 @@ class QueryBuilder:
282
299
  .execute()
283
300
  """
284
301
 
285
- _graph: 'HtmlGraph'
302
+ _graph: HtmlGraph
286
303
  _conditions: list[Condition] = field(default_factory=list)
287
304
  _type_filter: str | None = None
288
305
  _limit: int | None = None
289
306
  _offset: int = 0
290
307
 
291
- def where(self, attribute: str, value: Any = None) -> 'QueryBuilder | ConditionBuilder':
308
+ def where(
309
+ self, attribute: str, value: Any = None
310
+ ) -> QueryBuilder | ConditionBuilder:
292
311
  """
293
312
  Start a query condition.
294
313
 
@@ -309,19 +328,19 @@ class QueryBuilder:
309
328
  attribute=attribute,
310
329
  operator=Operator.EQ,
311
330
  value=value,
312
- logical_op=LogicalOp.AND
331
+ logical_op=LogicalOp.AND,
313
332
  )
314
333
  self._conditions.append(condition)
315
334
  return self
316
335
  else:
317
336
  # Return condition builder for fluent operator
318
337
  return ConditionBuilder(
319
- _query_builder=self,
320
- _attribute=attribute,
321
- _logical_op=LogicalOp.AND
338
+ _query_builder=self, _attribute=attribute, _logical_op=LogicalOp.AND
322
339
  )
323
340
 
324
- def and_(self, attribute: str, value: Any = None) -> 'QueryBuilder | ConditionBuilder':
341
+ def and_(
342
+ self, attribute: str, value: Any = None
343
+ ) -> QueryBuilder | ConditionBuilder:
325
344
  """
326
345
  Add an AND condition.
327
346
 
@@ -337,18 +356,16 @@ class QueryBuilder:
337
356
  attribute=attribute,
338
357
  operator=Operator.EQ,
339
358
  value=value,
340
- logical_op=LogicalOp.AND
359
+ logical_op=LogicalOp.AND,
341
360
  )
342
361
  self._conditions.append(condition)
343
362
  return self
344
363
  else:
345
364
  return ConditionBuilder(
346
- _query_builder=self,
347
- _attribute=attribute,
348
- _logical_op=LogicalOp.AND
365
+ _query_builder=self, _attribute=attribute, _logical_op=LogicalOp.AND
349
366
  )
350
367
 
351
- def or_(self, attribute: str, value: Any = None) -> 'QueryBuilder | ConditionBuilder':
368
+ def or_(self, attribute: str, value: Any = None) -> QueryBuilder | ConditionBuilder:
352
369
  """
353
370
  Add an OR condition.
354
371
 
@@ -364,18 +381,16 @@ class QueryBuilder:
364
381
  attribute=attribute,
365
382
  operator=Operator.EQ,
366
383
  value=value,
367
- logical_op=LogicalOp.OR
384
+ logical_op=LogicalOp.OR,
368
385
  )
369
386
  self._conditions.append(condition)
370
387
  return self
371
388
  else:
372
389
  return ConditionBuilder(
373
- _query_builder=self,
374
- _attribute=attribute,
375
- _logical_op=LogicalOp.OR
390
+ _query_builder=self, _attribute=attribute, _logical_op=LogicalOp.OR
376
391
  )
377
392
 
378
- def not_(self, attribute: str) -> 'ConditionBuilder':
393
+ def not_(self, attribute: str) -> ConditionBuilder:
379
394
  """
380
395
  Add a NOT condition.
381
396
 
@@ -389,12 +404,10 @@ class QueryBuilder:
389
404
  ConditionBuilder for specifying the condition
390
405
  """
391
406
  return ConditionBuilder(
392
- _query_builder=self,
393
- _attribute=attribute,
394
- _logical_op=LogicalOp.NOT
407
+ _query_builder=self, _attribute=attribute, _logical_op=LogicalOp.NOT
395
408
  )
396
409
 
397
- def of_type(self, node_type: str) -> 'QueryBuilder':
410
+ def of_type(self, node_type: str) -> QueryBuilder:
398
411
  """
399
412
  Filter by node type.
400
413
 
@@ -407,7 +420,7 @@ class QueryBuilder:
407
420
  self._type_filter = node_type
408
421
  return self
409
422
 
410
- def limit(self, count: int) -> 'QueryBuilder':
423
+ def limit(self, count: int) -> QueryBuilder:
411
424
  """
412
425
  Limit number of results.
413
426
 
@@ -420,7 +433,7 @@ class QueryBuilder:
420
433
  self._limit = count
421
434
  return self
422
435
 
423
- def offset(self, skip: int) -> 'QueryBuilder':
436
+ def offset(self, skip: int) -> QueryBuilder:
424
437
  """
425
438
  Skip first N results.
426
439
 
@@ -433,7 +446,7 @@ class QueryBuilder:
433
446
  self._offset = skip
434
447
  return self
435
448
 
436
- def execute(self) -> list['Node']:
449
+ def execute(self) -> list[Node]:
437
450
  """
438
451
  Execute the query and return matching nodes.
439
452
 
@@ -453,13 +466,13 @@ class QueryBuilder:
453
466
 
454
467
  # Apply offset and limit
455
468
  if self._offset:
456
- results = results[self._offset:]
469
+ results = results[self._offset :]
457
470
  if self._limit:
458
- results = results[:self._limit]
471
+ results = results[: self._limit]
459
472
 
460
473
  return results
461
474
 
462
- def first(self) -> 'Node | None':
475
+ def first(self) -> Node | None:
463
476
  """
464
477
  Execute query and return first matching node.
465
478
 
@@ -493,7 +506,7 @@ class QueryBuilder:
493
506
  """
494
507
  return self.first() is not None
495
508
 
496
- def _evaluate_conditions(self, node: 'Node') -> bool:
509
+ def _evaluate_conditions(self, node: Node) -> bool:
497
510
  """
498
511
  Evaluate all conditions against a node.
499
512
 
@@ -533,11 +546,34 @@ class QueryBuilder:
533
546
 
534
547
  return result if result is not None else True
535
548
 
536
- def __iter__(self) -> Iterator['Node']:
537
- """Iterate over query results."""
549
+ def __iter__(self) -> Iterator[Node]:
550
+ """
551
+ Iterate over query results.
552
+
553
+ Enables using QueryBuilder directly in for loops without calling execute().
554
+ This provides a more Pythonic interface similar to Django ORM or SQLAlchemy.
555
+
556
+ Yields:
557
+ Node: Each node matching the query
558
+
559
+ Example:
560
+ >>> # Instead of: for node in graph.query_builder().execute()
561
+ >>> # You can do:
562
+ >>> query = graph.query_builder().where("status", "todo").and_("priority", "high")
563
+ >>> for node in query:
564
+ ... print(f"{node.id}: {node.title}")
565
+ feat-001: User Authentication
566
+ feat-002: Database Migration
567
+
568
+ >>> # Works with comprehensions
569
+ >>> titles = [n.title for n in query]
570
+ >>>
571
+ >>> # Works with any iterable operation
572
+ >>> first = next(iter(query), None)
573
+ """
538
574
  return iter(self.execute())
539
575
 
540
- def to_predicate(self) -> Callable[['Node'], bool]:
576
+ def to_predicate(self) -> Callable[[Node], bool]:
541
577
  """
542
578
  Convert query to a predicate function.
543
579
 
@@ -546,7 +582,8 @@ class QueryBuilder:
546
582
  Returns:
547
583
  Function that takes a Node and returns bool
548
584
  """
549
- def predicate(node: 'Node') -> bool:
585
+
586
+ def predicate(node: Node) -> bool:
550
587
  if self._type_filter and node.type != self._type_filter:
551
588
  return False
552
589
  return self._evaluate_conditions(node)