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,22 +1,27 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Base builder class for fluent node creation.
3
5
 
4
6
  Provides common builder patterns shared across all node types.
5
7
  """
6
8
 
7
- from __future__ import annotations
8
- from typing import TYPE_CHECKING, Any, TypeVar, Generic
9
+
9
10
  from datetime import datetime
11
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
10
12
 
11
13
  if TYPE_CHECKING:
12
- from htmlgraph.sdk import SDK
13
14
  from htmlgraph.models import Node
15
+ from htmlgraph.sdk import SDK
14
16
 
15
- from htmlgraph.models import Step, Edge
16
17
  from htmlgraph.ids import generate_id
18
+ from htmlgraph.models import Edge, Step
17
19
 
18
20
  # Generic type for the builder subclass
19
- BuilderT = TypeVar('BuilderT', bound='BaseBuilder')
21
+ BuilderT = TypeVar("BuilderT", bound="BaseBuilder")
22
+
23
+ # For type hints in helper methods
24
+ from typing_extensions import Self
20
25
 
21
26
 
22
27
  class BaseBuilder(Generic[BuilderT]):
@@ -38,7 +43,7 @@ class BaseBuilder(Generic[BuilderT]):
38
43
 
39
44
  node_type: str = "node" # Override in subclasses
40
45
 
41
- def __init__(self, sdk: 'SDK', title: str, **kwargs):
46
+ def __init__(self, sdk: SDK, title: str, **kwargs: Any):
42
47
  """
43
48
  Initialize builder.
44
49
 
@@ -56,9 +61,36 @@ class BaseBuilder(Generic[BuilderT]):
56
61
  "steps": [],
57
62
  "edges": {},
58
63
  "properties": {},
59
- **kwargs
64
+ **kwargs,
60
65
  }
61
66
 
67
+ # Helper methods for common patterns
68
+ def _add_edge(
69
+ self, edge_type: str, target_id: str, relationship: str | None = None
70
+ ) -> Self:
71
+ """Add an edge to the node being built."""
72
+ if edge_type not in self._data["edges"]:
73
+ self._data["edges"][edge_type] = []
74
+ self._data["edges"][edge_type].append(
75
+ Edge(target_id=target_id, relationship=relationship or edge_type)
76
+ )
77
+ return self
78
+
79
+ def _set_date(self, field_name: str, date_value: Any) -> Self:
80
+ """Set a date field in properties, converting to ISO format if needed."""
81
+ iso_date = (
82
+ date_value.isoformat() if hasattr(date_value, "isoformat") else date_value
83
+ )
84
+ self._data["properties"][field_name] = iso_date
85
+ return self
86
+
87
+ def _append_to_list(self, field_name: str, value: Any) -> Self:
88
+ """Append a value to a list field in properties, creating list if needed."""
89
+ if field_name not in self._data["properties"]:
90
+ self._data["properties"][field_name] = []
91
+ self._data["properties"][field_name].append(value)
92
+ return self
93
+
62
94
  def set_priority(self, priority: str) -> BuilderT:
63
95
  """Set node priority (low, medium, high, critical)."""
64
96
  self._data["priority"] = priority
@@ -87,21 +119,11 @@ class BaseBuilder(Generic[BuilderT]):
87
119
 
88
120
  def blocks(self, node_id: str) -> BuilderT:
89
121
  """Add blocking relationship (this node blocks another)."""
90
- if "blocks" not in self._data["edges"]:
91
- self._data["edges"]["blocks"] = []
92
- self._data["edges"]["blocks"].append(
93
- Edge(target_id=node_id, relationship="blocks")
94
- )
95
- return self # type: ignore
122
+ return self._add_edge("blocks", node_id) # type: ignore
96
123
 
97
124
  def blocked_by(self, node_id: str) -> BuilderT:
98
125
  """Add blocked-by relationship (this node is blocked by another)."""
99
- if "blocked_by" not in self._data["edges"]:
100
- self._data["edges"]["blocked_by"] = []
101
- self._data["edges"]["blocked_by"].append(
102
- Edge(target_id=node_id, relationship="blocked_by")
103
- )
104
- return self # type: ignore
126
+ return self._add_edge("blocked_by", node_id) # type: ignore
105
127
 
106
128
  def set_track(self, track_id: str) -> BuilderT:
107
129
  """Link to a track."""
@@ -131,7 +153,7 @@ class BaseBuilder(Generic[BuilderT]):
131
153
  self._data["handoff_timestamp"] = datetime.now()
132
154
  return self # type: ignore
133
155
 
134
- def save(self) -> 'Node':
156
+ def save(self) -> Node:
135
157
  """
136
158
  Save the node and return the Node instance.
137
159
 
@@ -140,6 +162,9 @@ class BaseBuilder(Generic[BuilderT]):
140
162
 
141
163
  Returns:
142
164
  Created Node instance
165
+
166
+ Raises:
167
+ ValueError: If node type requires track_id but none is set
143
168
  """
144
169
  # Generate collision-resistant ID if not provided
145
170
  if "id" not in self._data:
@@ -148,28 +173,92 @@ class BaseBuilder(Generic[BuilderT]):
148
173
  title=self._data.get("title", ""),
149
174
  )
150
175
 
176
+ # Validate track_id requirement for features
177
+ node_type = self._data.get("type", self.node_type)
178
+ if node_type == "feature" and not self._data.get("track_id"):
179
+ # Get available tracks for helpful error message
180
+ try:
181
+ tracks = self._sdk.tracks.all()
182
+ track_options = "\n".join(
183
+ [f" - {track.id}: {track.title}" for track in tracks[:10]]
184
+ )
185
+ if len(tracks) > 10:
186
+ track_options += f"\n ... and {len(tracks) - 10} more tracks"
187
+
188
+ error_msg = (
189
+ f"Feature '{self._data.get('title', 'Unknown')}' requires a track linkage.\n\n"
190
+ f"Use: .set_track('track_id') to link to a track before saving.\n\n"
191
+ f"Available tracks:\n{track_options or ' (no tracks found)'}\n\n"
192
+ f"Create a track first: sdk.tracks.create('Track Title')"
193
+ )
194
+ except Exception:
195
+ # Fallback error message if we can't fetch tracks
196
+ error_msg = (
197
+ f"Feature '{self._data.get('title', 'Unknown')}' requires a track linkage.\n"
198
+ f"Use: .set_track('track_id') to link to a track before saving."
199
+ )
200
+
201
+ raise ValueError(error_msg)
202
+
151
203
  # Import Node here to avoid circular imports
152
204
  from htmlgraph.models import Node
153
- from htmlgraph.graph import HtmlGraph
154
205
 
155
206
  node = Node(**self._data)
156
207
 
157
- # Save to the correct collection directory based on node type
158
- # Use the collection's graph, not SDK._graph (which is features-only)
208
+ # Save to the collection's shared graph (not a new instance)
209
+ # This ensures the node is visible via collection.get() immediately
159
210
  collection_name = self._data.get("type", self.node_type) + "s"
160
- graph_path = self._sdk._directory / collection_name
161
- graph = HtmlGraph(graph_path, auto_load=False)
162
- graph.add(node)
211
+ collection = getattr(self._sdk, collection_name, None)
212
+
213
+ if collection is not None:
214
+ # Use the collection's shared graph
215
+ graph = collection._ensure_graph()
216
+ graph.add(node)
217
+ else:
218
+ # Fallback: create new graph (for collections not yet on SDK)
219
+ from htmlgraph.graph import HtmlGraph
220
+
221
+ graph_path = self._sdk._directory / collection_name
222
+ graph = HtmlGraph(graph_path, auto_load=False)
223
+ graph.add(node)
224
+
225
+ # Log creation event to SQLite for dashboard observability
226
+ try:
227
+ action_type = self._data.get("type", self.node_type)
228
+ self._sdk._log_event(
229
+ event_type="tool_call",
230
+ tool_name="SDK.create",
231
+ input_summary=f"Create {action_type}: {self._data.get('title', 'Untitled')}",
232
+ output_summary=f"Created {collection_name}/{node.id}",
233
+ context={
234
+ "collection": collection_name,
235
+ "node_id": node.id,
236
+ "node_type": action_type,
237
+ "title": node.title,
238
+ "status": self._data.get("status", "todo"),
239
+ "priority": self._data.get("priority", "medium"),
240
+ },
241
+ cost_tokens=50,
242
+ )
243
+ except Exception as e:
244
+ # Never break save because of logging
245
+ import logging
246
+
247
+ logging.debug(f"Event logging failed: {e}")
163
248
 
164
- # Log creation event if SessionManager is available and agent is set
165
- if hasattr(self._sdk, 'session_manager') and self._sdk.agent:
249
+ # Also log via SessionManager for backward compatibility
250
+ if hasattr(self._sdk, "session_manager") and self._sdk.agent:
166
251
  try:
167
252
  self._sdk.session_manager._maybe_log_work_item_action(
168
253
  agent=self._sdk.agent,
169
254
  tool="FeatureCreate",
170
255
  summary=f"Created: {collection_name}/{node.id}",
171
256
  feature_id=node.id,
172
- payload={"collection": collection_name, "action": "create", "title": node.title},
257
+ payload={
258
+ "collection": collection_name,
259
+ "action": "create",
260
+ "title": node.title,
261
+ },
173
262
  )
174
263
  except Exception:
175
264
  # Never break save because of logging
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Bug builder for creating bug report nodes.
5
+
6
+ Extends BaseBuilder with bug-specific methods like
7
+ severity and reproduction steps.
8
+ """
9
+
10
+
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ if TYPE_CHECKING:
14
+ from htmlgraph.sdk import SDK
15
+
16
+ from htmlgraph.builders.base import BaseBuilder
17
+
18
+
19
+ class BugBuilder(BaseBuilder["BugBuilder"]):
20
+ """
21
+ Fluent builder for creating bugs.
22
+
23
+ Inherits common builder methods from BaseBuilder and adds
24
+ bug-specific methods for issue tracking:
25
+ - severity: Bug severity level
26
+ - repro_steps: Steps to reproduce
27
+ - expected/actual: Expected vs actual behavior
28
+ - affected_version: Version where bug occurs
29
+
30
+ Example:
31
+ >>> sdk = SDK(agent="claude")
32
+ >>> bug = sdk.bugs.create("Login button unresponsive") \\
33
+ ... .set_priority("critical") \\
34
+ ... .set_severity("high") \\
35
+ ... .set_repro_steps(["Go to login", "Click button", "Nothing happens"]) \\
36
+ ... .save()
37
+ """
38
+
39
+ node_type = "bug"
40
+
41
+ def __init__(self, sdk: SDK, title: str, **kwargs: Any):
42
+ """Initialize bug builder with agent attribution."""
43
+ super().__init__(sdk, title, **kwargs)
44
+ # Auto-assign agent from SDK for work tracking
45
+ if sdk._agent_id:
46
+ self._data["agent_assigned"] = sdk._agent_id
47
+ elif "agent_assigned" not in self._data:
48
+ # Log warning if agent not assigned (defensive check)
49
+ import logging
50
+
51
+ logging.warning(
52
+ f"Creating bug '{self._data.get('title', 'Unknown')}' without agent attribution. "
53
+ "Pass agent='name' to SDK() initialization."
54
+ )
55
+
56
+ def set_severity(self, severity: str) -> BugBuilder:
57
+ """
58
+ Set bug severity level.
59
+
60
+ Args:
61
+ severity: Severity level (low, medium, high, critical)
62
+
63
+ Returns:
64
+ Self for method chaining
65
+
66
+ Example:
67
+ >>> bug.set_severity("critical")
68
+ """
69
+ self._data["severity"] = severity
70
+ return self
71
+
72
+ def set_repro_steps(self, steps: list[str]) -> BugBuilder:
73
+ """
74
+ Set steps to reproduce the bug.
75
+
76
+ Args:
77
+ steps: List of reproduction steps
78
+
79
+ Returns:
80
+ Self for method chaining
81
+
82
+ Example:
83
+ >>> bug.set_repro_steps(["Open app", "Click login", "Enter credentials"])
84
+ """
85
+ self._data["repro_steps"] = steps
86
+ return self
87
+
88
+ def set_expected_behavior(self, expected: str) -> BugBuilder:
89
+ """
90
+ Set expected behavior.
91
+
92
+ Args:
93
+ expected: What should happen
94
+
95
+ Returns:
96
+ Self for method chaining
97
+
98
+ Example:
99
+ >>> bug.set_expected_behavior("User should be logged in")
100
+ """
101
+ self._data["expected_behavior"] = expected
102
+ return self
103
+
104
+ def set_actual_behavior(self, actual: str) -> BugBuilder:
105
+ """
106
+ Set actual (buggy) behavior.
107
+
108
+ Args:
109
+ actual: What actually happens
110
+
111
+ Returns:
112
+ Self for method chaining
113
+
114
+ Example:
115
+ >>> bug.set_actual_behavior("Button does nothing")
116
+ """
117
+ self._data["actual_behavior"] = actual
118
+ return self
119
+
120
+ def set_affected_version(self, version: str) -> BugBuilder:
121
+ """
122
+ Set the affected version.
123
+
124
+ Args:
125
+ version: Version string where bug occurs
126
+
127
+ Returns:
128
+ Self for method chaining
129
+
130
+ Example:
131
+ >>> bug.set_affected_version("1.2.3")
132
+ """
133
+ self._data["affected_version"] = version
134
+ return self
135
+
136
+ def set_environment(self, environment: str) -> BugBuilder:
137
+ """
138
+ Set the environment where bug occurs.
139
+
140
+ Args:
141
+ environment: Environment description (e.g., "Chrome 120, macOS")
142
+
143
+ Returns:
144
+ Self for method chaining
145
+
146
+ Example:
147
+ >>> bug.set_environment("Chrome 120, macOS Sonoma")
148
+ """
149
+ self._data["environment"] = environment
150
+ return self
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Chore builder for creating maintenance task nodes.
5
+
6
+ Extends BaseBuilder with chore-specific methods like
7
+ chore type and recurrence.
8
+ """
9
+
10
+
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ if TYPE_CHECKING:
14
+ from htmlgraph.sdk import SDK
15
+
16
+ from htmlgraph.builders.base import BaseBuilder
17
+
18
+
19
+ class ChoreBuilder(BaseBuilder["ChoreBuilder"]):
20
+ """
21
+ Fluent builder for creating chores.
22
+
23
+ Inherits common builder methods from BaseBuilder and adds
24
+ chore-specific methods for maintenance work:
25
+ - chore_type: Classification of chore
26
+ - is_recurring: Whether this repeats
27
+ - recurrence_interval: How often it recurs
28
+
29
+ Example:
30
+ >>> sdk = SDK(agent="claude")
31
+ >>> chore = sdk.chores.create("Update dependencies") \\
32
+ ... .set_priority("low") \\
33
+ ... .set_chore_type("maintenance") \\
34
+ ... .set_recurring(interval_days=30) \\
35
+ ... .save()
36
+ """
37
+
38
+ node_type = "chore"
39
+
40
+ def __init__(self, sdk: SDK, title: str, **kwargs: Any):
41
+ """Initialize chore builder with agent attribution."""
42
+ super().__init__(sdk, title, **kwargs)
43
+ # Auto-assign agent from SDK for work tracking
44
+ if sdk._agent_id:
45
+ self._data["agent_assigned"] = sdk._agent_id
46
+ elif "agent_assigned" not in self._data:
47
+ # Log warning if agent not assigned (defensive check)
48
+ import logging
49
+
50
+ logging.warning(
51
+ f"Creating chore '{self._data.get('title', 'Unknown')}' without agent attribution. "
52
+ "Pass agent='name' to SDK() initialization."
53
+ )
54
+
55
+ def set_chore_type(self, chore_type: str) -> ChoreBuilder:
56
+ """
57
+ Set the type of chore.
58
+
59
+ Args:
60
+ chore_type: Type (maintenance, refactor, cleanup, upgrade, documentation)
61
+
62
+ Returns:
63
+ Self for method chaining
64
+
65
+ Example:
66
+ >>> chore.set_chore_type("maintenance")
67
+ """
68
+ self._data["chore_type"] = chore_type
69
+ return self
70
+
71
+ def set_recurring(self, interval_days: int | None = None) -> ChoreBuilder:
72
+ """
73
+ Mark chore as recurring with optional interval.
74
+
75
+ Args:
76
+ interval_days: Days between occurrences (optional)
77
+
78
+ Returns:
79
+ Self for method chaining
80
+
81
+ Example:
82
+ >>> chore.set_recurring(interval_days=7) # Weekly
83
+ """
84
+ self._data["is_recurring"] = True
85
+ if interval_days:
86
+ self._data["recurrence_interval_days"] = interval_days
87
+ return self
88
+
89
+ def set_scope(self, scope: str) -> ChoreBuilder:
90
+ """
91
+ Set the scope of the chore.
92
+
93
+ Args:
94
+ scope: Scope description (e.g., "entire codebase", "auth module")
95
+
96
+ Returns:
97
+ Self for method chaining
98
+
99
+ Example:
100
+ >>> chore.set_scope("authentication module")
101
+ """
102
+ self._data["scope"] = scope
103
+ return self
104
+
105
+ def set_estimated_effort(self, hours: float) -> ChoreBuilder:
106
+ """
107
+ Set estimated effort in hours.
108
+
109
+ Args:
110
+ hours: Estimated hours to complete
111
+
112
+ Returns:
113
+ Self for method chaining
114
+
115
+ Example:
116
+ >>> chore.set_estimated_effort(2.5)
117
+ """
118
+ self._data["estimated_effort_hours"] = hours
119
+ return self
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Epic builder for creating large body of work nodes.
5
+
6
+ Extends BaseBuilder with epic-specific methods like
7
+ child features and milestones.
8
+ """
9
+
10
+
11
+ from datetime import date
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ if TYPE_CHECKING:
15
+ from htmlgraph.sdk import SDK
16
+
17
+ from htmlgraph.builders.base import BaseBuilder
18
+
19
+
20
+ class EpicBuilder(BaseBuilder["EpicBuilder"]):
21
+ """
22
+ Fluent builder for creating epics.
23
+
24
+ Inherits common builder methods from BaseBuilder and adds
25
+ epic-specific methods for large initiatives:
26
+ - child_features: Features contained in this epic
27
+ - target_date: Target completion date
28
+ - success_criteria: How success is measured
29
+
30
+ Example:
31
+ >>> sdk = SDK(agent="claude")
32
+ >>> epic = sdk.epics.create("v2.0 Release") \\
33
+ ... .set_priority("high") \\
34
+ ... .set_target_date(date(2025, 3, 1)) \\
35
+ ... .add_child_feature("feat-001") \\
36
+ ... .add_child_feature("feat-002") \\
37
+ ... .save()
38
+ """
39
+
40
+ node_type = "epic"
41
+
42
+ def __init__(self, sdk: SDK, title: str, **kwargs: Any):
43
+ """Initialize epic builder with agent attribution."""
44
+ super().__init__(sdk, title, **kwargs)
45
+ # Auto-assign agent from SDK for work tracking
46
+ if sdk._agent_id:
47
+ self._data["agent_assigned"] = sdk._agent_id
48
+ elif "agent_assigned" not in self._data:
49
+ # Log warning if agent not assigned (defensive check)
50
+ import logging
51
+
52
+ logging.warning(
53
+ f"Creating epic '{self._data.get('title', 'Unknown')}' without agent attribution. "
54
+ "Pass agent='name' to SDK() initialization."
55
+ )
56
+
57
+ def add_child_feature(self, feature_id: str) -> EpicBuilder:
58
+ """
59
+ Add a feature as a child of this epic.
60
+
61
+ Args:
62
+ feature_id: ID of the child feature
63
+
64
+ Returns:
65
+ Self for method chaining
66
+
67
+ Example:
68
+ >>> epic.add_child_feature("feat-abc123")
69
+ """
70
+ return self._add_edge("contains", feature_id)
71
+
72
+ def add_child_features(self, feature_ids: list[str]) -> EpicBuilder:
73
+ """
74
+ Add multiple features as children of this epic.
75
+
76
+ Args:
77
+ feature_ids: List of child feature IDs
78
+
79
+ Returns:
80
+ Self for method chaining
81
+
82
+ Example:
83
+ >>> epic.add_child_features(["feat-001", "feat-002", "feat-003"])
84
+ """
85
+ for fid in feature_ids:
86
+ self.add_child_feature(fid)
87
+ return self
88
+
89
+ def set_target_date(self, target: date) -> EpicBuilder:
90
+ """
91
+ Set target completion date.
92
+
93
+ Args:
94
+ target: Target date for epic completion
95
+
96
+ Returns:
97
+ Self for method chaining
98
+
99
+ Example:
100
+ >>> epic.set_target_date(date(2025, 6, 1))
101
+ """
102
+ return self._set_date("target_date", target)
103
+
104
+ def set_success_criteria(self, criteria: list[str]) -> EpicBuilder:
105
+ """
106
+ Set success criteria for the epic.
107
+
108
+ Args:
109
+ criteria: List of success criteria
110
+
111
+ Returns:
112
+ Self for method chaining
113
+
114
+ Example:
115
+ >>> epic.set_success_criteria(["100% test coverage", "Zero critical bugs"])
116
+ """
117
+ self._data["success_criteria"] = criteria
118
+ return self
119
+
120
+ def set_business_value(self, value: str) -> EpicBuilder:
121
+ """
122
+ Set the business value statement.
123
+
124
+ Args:
125
+ value: Business value description
126
+
127
+ Returns:
128
+ Self for method chaining
129
+
130
+ Example:
131
+ >>> epic.set_business_value("Increase user retention by 20%")
132
+ """
133
+ self._data["business_value"] = value
134
+ return self
135
+
136
+ def set_stakeholder(self, stakeholder: str) -> EpicBuilder:
137
+ """
138
+ Set the primary stakeholder.
139
+
140
+ Args:
141
+ stakeholder: Stakeholder name or role
142
+
143
+ Returns:
144
+ Self for method chaining
145
+
146
+ Example:
147
+ >>> epic.set_stakeholder("Product Team")
148
+ """
149
+ self._data["stakeholder"] = stakeholder
150
+ return self