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
@@ -0,0 +1,113 @@
1
+ """
2
+ Task attribution mixin for SDK - subagent work tracking.
3
+
4
+ Provides methods for tracking which subagent did what work.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+
12
+ class TaskAttributionMixin:
13
+ """
14
+ Mixin providing task attribution capabilities to SDK.
15
+
16
+ Tracks which subagent executed what work for observability.
17
+ """
18
+
19
+ def get_task_attribution(self, task_id: str) -> dict[str, Any]:
20
+ """
21
+ Get attribution - which subagent did what work in this task?
22
+
23
+ Queries the database to find all events associated with a Claude Code task,
24
+ showing which subagent executed each tool call.
25
+
26
+ Args:
27
+ task_id: Claude Code's internal task ID (available from Task() response)
28
+
29
+ Returns:
30
+ Dictionary with task_id, by_subagent mapping, and total_events count
31
+
32
+ Example:
33
+ >>> sdk = SDK(agent="claude")
34
+ >>> result = sdk.get_task_attribution("task-abc123-xyz789")
35
+ >>> for subagent, events in result['by_subagent'].items():
36
+ ... logger.info(f"{subagent}:")
37
+ ... for event in events:
38
+ ... logger.info(f" - {event['tool']}: {event['summary']}")
39
+ >>> logger.info(f"Total events: {result['total_events']}")
40
+
41
+ See also:
42
+ get_subagent_work: Get all work grouped by subagent in a session
43
+ """
44
+ from htmlgraph.config import get_database_path
45
+ from htmlgraph.db.schema import HtmlGraphDB
46
+
47
+ try:
48
+ db = HtmlGraphDB(str(get_database_path()))
49
+ events = db.get_events_for_task(task_id)
50
+
51
+ # Group by subagent_type
52
+ by_subagent: dict[str, list[dict[str, Any]]] = {}
53
+ for event in events:
54
+ agent = event.get("subagent_type", "orchestrator")
55
+ if agent not in by_subagent:
56
+ by_subagent[agent] = []
57
+ by_subagent[agent].append(
58
+ {
59
+ "tool": event.get("tool_name"),
60
+ "summary": event.get("input_summary"),
61
+ "timestamp": event.get("created_at"),
62
+ "event_id": event.get("event_id"),
63
+ "success": not event.get("is_error", False),
64
+ }
65
+ )
66
+
67
+ return {
68
+ "task_id": task_id,
69
+ "by_subagent": by_subagent,
70
+ "total_events": len(events),
71
+ }
72
+ except Exception as e:
73
+ return {
74
+ "task_id": task_id,
75
+ "by_subagent": {},
76
+ "total_events": 0,
77
+ "error": str(e),
78
+ }
79
+
80
+ def get_subagent_work(self, session_id: str) -> dict[str, list[dict[str, Any]]]:
81
+ """
82
+ Get all work grouped by which subagent did it in a session.
83
+
84
+ Shows which subagent (researcher, general-purpose, etc.) executed each
85
+ tool call within a session.
86
+
87
+ Args:
88
+ session_id: Session ID to analyze
89
+
90
+ Returns:
91
+ Dictionary mapping subagent_type to list of events they executed.
92
+ Each event includes: tool_name, input_summary, output_summary, created_at, event_id
93
+
94
+ Example:
95
+ >>> sdk = SDK(agent="claude")
96
+ >>> work = sdk.get_subagent_work("sess-123")
97
+ >>> for subagent, events in work.items():
98
+ ... logger.info(f"{subagent} ({len(events)} events):")
99
+ ... for event in events:
100
+ ... logger.info(f" - {event['tool_name']}: {event['input_summary']}")
101
+
102
+ See also:
103
+ get_task_attribution: Get work for a specific Claude Code task
104
+ analyze_session: Get session metrics and analytics
105
+ """
106
+ from htmlgraph.config import get_database_path
107
+ from htmlgraph.db.schema import HtmlGraphDB
108
+
109
+ try:
110
+ db = HtmlGraphDB(str(get_database_path()))
111
+ return db.get_subagent_work(session_id)
112
+ except Exception:
113
+ return {}
@@ -0,0 +1,410 @@
1
+ """
2
+ Core mixin for SDK - database, refs, export, and utility methods.
3
+
4
+ Provides essential SDK functionality that doesn't belong in specialized mixins.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import os
11
+ from typing import TYPE_CHECKING, Any, cast
12
+
13
+ if TYPE_CHECKING:
14
+ from pathlib import Path
15
+
16
+ from htmlgraph.agents import AgentInterface
17
+ from htmlgraph.db.schema import HtmlGraphDB
18
+ from htmlgraph.models import Node
19
+ from htmlgraph.refs import RefManager
20
+ from htmlgraph.session_manager import SessionManager
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class CoreMixin:
26
+ """
27
+ Mixin providing core SDK functionality.
28
+
29
+ Includes:
30
+ - Database access (db, query, execute_query_builder)
31
+ - Ref resolution (ref)
32
+ - Export functionality (export_to_html)
33
+ - Event logging (_log_event)
34
+ - Utility methods (reload, summary, my_work, next_task, get_status, dedupe_sessions)
35
+ """
36
+
37
+ _directory: Path
38
+ _db: HtmlGraphDB
39
+ _agent_id: str | None
40
+ _parent_session: str | None
41
+ _agent_interface: AgentInterface
42
+ _graph: Any
43
+ session_manager: SessionManager
44
+ refs: RefManager
45
+
46
+ # Collection references (for ref resolution)
47
+ features: Any
48
+ tracks: Any
49
+ bugs: Any
50
+ spikes: Any
51
+ chores: Any
52
+ epics: Any
53
+ todos: Any
54
+ phases: Any
55
+
56
+ def ref(self, short_ref: str) -> Node | None:
57
+ """
58
+ Resolve a short ref to a Node object.
59
+
60
+ Short refs are stable identifiers like @f1, @t2, @b5 that map to
61
+ full node IDs. This method resolves the short ref and fetches the
62
+ corresponding node from the appropriate collection.
63
+
64
+ Args:
65
+ short_ref: Short ref like "@f1", "@t2", "@b5", etc.
66
+
67
+ Returns:
68
+ Node object or None if not found
69
+
70
+ Example:
71
+ >>> sdk = SDK(agent="claude")
72
+ >>> feature = sdk.ref("@f1")
73
+ >>> if feature:
74
+ ... logger.info("%s", feature.title)
75
+ ... feature.status = "done"
76
+ ... sdk.features.update(feature)
77
+ """
78
+ # Resolve short ref to full ID
79
+ full_id = self.refs.resolve_ref(short_ref)
80
+ if not full_id:
81
+ return None
82
+
83
+ # Determine type from ref prefix and fetch from appropriate collection
84
+ if len(short_ref) < 2:
85
+ return None
86
+
87
+ prefix = short_ref[1] # Get letter after @
88
+
89
+ # Map prefix to collection
90
+ collection_map = {
91
+ "f": self.features,
92
+ "t": self.tracks,
93
+ "b": self.bugs,
94
+ "s": self.spikes,
95
+ "c": self.chores,
96
+ "e": self.epics,
97
+ "d": self.todos,
98
+ "p": self.phases,
99
+ }
100
+
101
+ collection = collection_map.get(prefix)
102
+ if not collection:
103
+ return None
104
+
105
+ # Get node from collection
106
+ if hasattr(collection, "get"):
107
+ return cast("Node | None", collection.get(full_id))
108
+
109
+ return None
110
+
111
+ # =========================================================================
112
+ # SQLite Database Integration
113
+ # =========================================================================
114
+
115
+ def db(self) -> HtmlGraphDB:
116
+ """
117
+ Get the SQLite database instance.
118
+
119
+ Returns:
120
+ HtmlGraphDB instance for executing queries
121
+
122
+ Example:
123
+ >>> sdk = SDK(agent="claude")
124
+ >>> db = sdk.db()
125
+ >>> events = db.get_session_events("sess-123")
126
+ >>> features = db.get_features_by_status("todo")
127
+ """
128
+ return self._db
129
+
130
+ def query(self, sql: str, params: tuple[Any, ...] = ()) -> list[dict[str, Any]]:
131
+ """
132
+ Execute a raw SQL query on the SQLite database.
133
+
134
+ Args:
135
+ sql: SQL query string
136
+ params: Query parameters (for safe parameterized queries)
137
+
138
+ Returns:
139
+ List of result dictionaries
140
+
141
+ Example:
142
+ >>> sdk = SDK(agent="claude")
143
+ >>> results = sdk.query(
144
+ ... "SELECT * FROM features WHERE status = ? AND priority = ?",
145
+ ... ("todo", "high")
146
+ ... )
147
+ >>> for row in results:
148
+ ... print(row["title"])
149
+ """
150
+ if not self._db.connection:
151
+ self._db.connect()
152
+
153
+ cursor = self._db.connection.cursor() # type: ignore[union-attr]
154
+ cursor.execute(sql, params)
155
+ rows = cursor.fetchall()
156
+ return [dict(row) for row in rows]
157
+
158
+ def execute_query_builder(
159
+ self, sql: str, params: tuple[Any, ...] = ()
160
+ ) -> list[dict[str, Any]]:
161
+ """
162
+ Execute a query using the Queries builder.
163
+
164
+ Args:
165
+ sql: SQL query from Queries builder
166
+ params: Parameters from Queries builder
167
+
168
+ Returns:
169
+ List of result dictionaries
170
+
171
+ Example:
172
+ >>> sdk = SDK(agent="claude")
173
+ >>> sql, params = Queries.get_features_by_status("todo", limit=5)
174
+ >>> results = sdk.execute_query_builder(sql, params)
175
+ """
176
+ return self.query(sql, params)
177
+
178
+ def export_to_html(
179
+ self,
180
+ output_dir: str | None = None,
181
+ include_features: bool = True,
182
+ include_sessions: bool = True,
183
+ include_events: bool = False,
184
+ ) -> dict[str, int]:
185
+ """
186
+ Export SQLite data to HTML files for backward compatibility.
187
+
188
+ Args:
189
+ output_dir: Directory to export to (defaults to .htmlgraph)
190
+ include_features: Export features
191
+ include_sessions: Export sessions
192
+ include_events: Export events (detailed, use with care)
193
+
194
+ Returns:
195
+ Dict with export counts: {"features": int, "sessions": int, "events": int}
196
+
197
+ Example:
198
+ >>> sdk = SDK(agent="claude")
199
+ >>> result = sdk.export_to_html()
200
+ >>> logger.info(f"Exported {result['features']} features")
201
+ """
202
+ from pathlib import Path
203
+
204
+ if output_dir is None:
205
+ output_dir = str(self._directory)
206
+
207
+ output_path = Path(output_dir)
208
+ counts: dict[str, int] = {"features": 0, "sessions": 0, "events": 0}
209
+
210
+ if include_features:
211
+ # Export all features from SQLite to HTML
212
+ features_dir = output_path / "features"
213
+ features_dir.mkdir(parents=True, exist_ok=True)
214
+
215
+ try:
216
+ cursor = self._db.connection.cursor() # type: ignore[union-attr]
217
+ cursor.execute("SELECT * FROM features")
218
+ rows = cursor.fetchall()
219
+
220
+ for row in rows:
221
+ feature_dict = dict(row)
222
+ feature_id = feature_dict["id"]
223
+ # Write HTML file (simplified export)
224
+ html_file = features_dir / f"{feature_id}.html"
225
+ html_file.write_text(
226
+ f"<h1>{feature_dict['title']}</h1>"
227
+ f"<p>Status: {feature_dict['status']}</p>"
228
+ f"<p>Type: {feature_dict['type']}</p>"
229
+ )
230
+ counts["features"] += 1
231
+ except Exception as e:
232
+ logger.error(f"Error exporting features: {e}")
233
+
234
+ if include_sessions:
235
+ # Export all sessions from SQLite to HTML
236
+ sessions_dir = output_path / "sessions"
237
+ sessions_dir.mkdir(parents=True, exist_ok=True)
238
+
239
+ try:
240
+ cursor = self._db.connection.cursor() # type: ignore[union-attr]
241
+ cursor.execute("SELECT * FROM sessions")
242
+ rows = cursor.fetchall()
243
+
244
+ for row in rows:
245
+ session_dict = dict(row)
246
+ session_id = session_dict["session_id"]
247
+ # Write HTML file (simplified export)
248
+ html_file = sessions_dir / f"{session_id}.html"
249
+ html_file.write_text(
250
+ f"<h1>Session {session_id}</h1>"
251
+ f"<p>Agent: {session_dict['agent_assigned']}</p>"
252
+ f"<p>Status: {session_dict['status']}</p>"
253
+ )
254
+ counts["sessions"] += 1
255
+ except Exception as e:
256
+ logger.error(f"Error exporting sessions: {e}")
257
+
258
+ return counts
259
+
260
+ def _log_event(
261
+ self,
262
+ event_type: str,
263
+ tool_name: str | None = None,
264
+ input_summary: str | None = None,
265
+ output_summary: str | None = None,
266
+ context: dict[str, Any] | None = None,
267
+ cost_tokens: int = 0,
268
+ ) -> bool:
269
+ """
270
+ Log an event to the SQLite database with parent-child linking.
271
+
272
+ Internal method used by collections to track operations.
273
+ Automatically creates a session if one doesn't exist.
274
+ Reads parent event ID from HTMLGRAPH_PARENT_ACTIVITY env var for hierarchical tracking.
275
+
276
+ Args:
277
+ event_type: Type of event (tool_call, completion, error, etc.)
278
+ tool_name: Tool that was called
279
+ input_summary: Summary of input
280
+ output_summary: Summary of output
281
+ context: Additional context metadata
282
+ cost_tokens: Token cost estimate
283
+
284
+ Returns:
285
+ True if logged successfully, False otherwise
286
+
287
+ Example (internal use):
288
+ >>> sdk._log_event(
289
+ ... event_type="tool_call",
290
+ ... tool_name="Edit",
291
+ ... input_summary="Edit file.py",
292
+ ... cost_tokens=100
293
+ ... )
294
+ """
295
+ from uuid import uuid4
296
+
297
+ event_id = f"evt-{uuid4().hex[:12]}"
298
+ session_id = self._parent_session or "cli-session"
299
+
300
+ # Read parent event ID from environment variable for hierarchical linking
301
+ parent_event_id = os.getenv("HTMLGRAPH_PARENT_ACTIVITY")
302
+
303
+ # Ensure session exists before logging event
304
+ try:
305
+ self._ensure_session_exists(session_id, parent_event_id=parent_event_id) # type: ignore[attr-defined]
306
+ except Exception as e:
307
+ logger.debug(f"Failed to ensure session exists: {e}")
308
+ # Continue anyway - session creation failure shouldn't block event logging
309
+
310
+ # Ensure agent_id is set for event logging
311
+ agent_id = self._agent_id or "unknown"
312
+
313
+ return self._db.insert_event(
314
+ event_id=event_id,
315
+ agent_id=agent_id,
316
+ event_type=event_type,
317
+ session_id=session_id,
318
+ tool_name=tool_name,
319
+ input_summary=input_summary,
320
+ output_summary=output_summary,
321
+ context=context,
322
+ parent_event_id=parent_event_id,
323
+ cost_tokens=cost_tokens,
324
+ )
325
+
326
+ def reload(self) -> None:
327
+ """Reload all data from disk."""
328
+ self._graph.reload()
329
+ self._agent_interface.reload()
330
+ # SessionManager reloads implicitly on access via its converters/graphs
331
+
332
+ def summary(self, max_items: int = 10) -> str:
333
+ """
334
+ Get project summary.
335
+
336
+ Returns:
337
+ Compact overview for AI agent orientation
338
+ """
339
+ return self._agent_interface.get_summary(max_items)
340
+
341
+ def my_work(self) -> dict[str, Any]:
342
+ """
343
+ Get current agent's workload.
344
+
345
+ Returns:
346
+ Dict with in_progress, completed counts
347
+ """
348
+ if not self._agent_id:
349
+ raise ValueError("No agent ID set")
350
+ return self._agent_interface.get_workload(self._agent_id)
351
+
352
+ def next_task(
353
+ self, priority: str | None = None, auto_claim: bool = True
354
+ ) -> Node | None:
355
+ """
356
+ Get next available task for this agent.
357
+
358
+ Args:
359
+ priority: Optional priority filter
360
+ auto_claim: Automatically claim the task
361
+
362
+ Returns:
363
+ Next available Node or None
364
+ """
365
+ return self._agent_interface.get_next_task(
366
+ agent_id=self._agent_id,
367
+ priority=priority,
368
+ node_type="feature",
369
+ auto_claim=auto_claim,
370
+ )
371
+
372
+ def get_status(self) -> dict[str, Any]:
373
+ """
374
+ Get project status.
375
+
376
+ Returns:
377
+ Dict with status metrics (WIP, counts, etc.)
378
+ """
379
+ return self.session_manager.get_status()
380
+
381
+ def dedupe_sessions(
382
+ self,
383
+ max_events: int = 1,
384
+ move_dir_name: str = "_orphans",
385
+ dry_run: bool = False,
386
+ stale_extra_active: bool = True,
387
+ ) -> dict[str, int]:
388
+ """
389
+ Move low-signal sessions (e.g. SessionStart-only) out of the main sessions dir.
390
+
391
+ Args:
392
+ max_events: Maximum events threshold (sessions with <= this many events are moved)
393
+ move_dir_name: Directory name to move orphaned sessions to
394
+ dry_run: If True, only report what would be done without actually moving files
395
+ stale_extra_active: If True, also mark extra active sessions as stale
396
+
397
+ Returns:
398
+ Dict with counts: {"scanned": int, "moved": int, "missing": int, "staled_active": int, "kept_active": int}
399
+
400
+ Example:
401
+ >>> sdk = SDK(agent="claude")
402
+ >>> result = sdk.dedupe_sessions(max_events=1, dry_run=False)
403
+ >>> logger.info(f"Scanned: {result['scanned']}, Moved: {result['moved']}")
404
+ """
405
+ return self.session_manager.dedupe_orphan_sessions(
406
+ max_events=max_events,
407
+ move_dir_name=move_dir_name,
408
+ dry_run=dry_run,
409
+ stale_extra_active=stale_extra_active,
410
+ )
@@ -0,0 +1,12 @@
1
+ """
2
+ Operations module for SDK - server, hooks, events management.
3
+
4
+ Provides:
5
+ - OperationsMixin: Server, hooks, and events operations
6
+ """
7
+
8
+ from htmlgraph.sdk.operations.mixin import OperationsMixin
9
+
10
+ __all__ = [
11
+ "OperationsMixin",
12
+ ]