htmlgraph 0.26.25__py3-none-any.whl → 0.27.1__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 (175) hide show
  1. htmlgraph/__init__.py +23 -1
  2. htmlgraph/__init__.pyi +123 -0
  3. htmlgraph/agent_registry.py +2 -1
  4. htmlgraph/analytics/cli.py +3 -3
  5. htmlgraph/analytics/cost_analyzer.py +5 -1
  6. htmlgraph/analytics/cost_monitor.py +664 -0
  7. htmlgraph/analytics/cross_session.py +13 -9
  8. htmlgraph/analytics/dependency.py +10 -6
  9. htmlgraph/analytics/strategic/__init__.py +80 -0
  10. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  11. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  12. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  13. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  14. htmlgraph/analytics/work_type.py +15 -11
  15. htmlgraph/analytics_index.py +2 -1
  16. htmlgraph/api/cost_alerts_websocket.py +416 -0
  17. htmlgraph/api/main.py +167 -62
  18. htmlgraph/api/websocket.py +538 -0
  19. htmlgraph/attribute_index.py +2 -1
  20. htmlgraph/builders/base.py +2 -1
  21. htmlgraph/builders/bug.py +2 -1
  22. htmlgraph/builders/chore.py +2 -1
  23. htmlgraph/builders/epic.py +2 -1
  24. htmlgraph/builders/feature.py +2 -1
  25. htmlgraph/builders/insight.py +2 -1
  26. htmlgraph/builders/metric.py +2 -1
  27. htmlgraph/builders/pattern.py +2 -1
  28. htmlgraph/builders/phase.py +2 -1
  29. htmlgraph/builders/spike.py +2 -1
  30. htmlgraph/builders/track.py +2 -1
  31. htmlgraph/cli/analytics.py +2 -1
  32. htmlgraph/cli/base.py +2 -1
  33. htmlgraph/cli/core.py +2 -1
  34. htmlgraph/cli/main.py +2 -1
  35. htmlgraph/cli/models.py +2 -1
  36. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  37. htmlgraph/cli/work/__init__.py +2 -1
  38. htmlgraph/cli/work/browse.py +2 -1
  39. htmlgraph/cli/work/features.py +2 -1
  40. htmlgraph/cli/work/orchestration.py +2 -1
  41. htmlgraph/cli/work/report.py +2 -1
  42. htmlgraph/cli/work/sessions.py +2 -1
  43. htmlgraph/cli/work/snapshot.py +2 -1
  44. htmlgraph/cli/work/tracks.py +2 -1
  45. htmlgraph/collections/base.py +10 -5
  46. htmlgraph/collections/bug.py +2 -1
  47. htmlgraph/collections/chore.py +2 -1
  48. htmlgraph/collections/epic.py +2 -1
  49. htmlgraph/collections/feature.py +2 -1
  50. htmlgraph/collections/insight.py +2 -1
  51. htmlgraph/collections/metric.py +2 -1
  52. htmlgraph/collections/pattern.py +2 -1
  53. htmlgraph/collections/phase.py +2 -1
  54. htmlgraph/collections/session.py +12 -7
  55. htmlgraph/collections/spike.py +6 -1
  56. htmlgraph/collections/task_delegation.py +7 -2
  57. htmlgraph/collections/todo.py +2 -1
  58. htmlgraph/collections/traces.py +15 -10
  59. htmlgraph/config/cost_models.json +56 -0
  60. htmlgraph/context_analytics.py +2 -1
  61. htmlgraph/db/schema.py +67 -6
  62. htmlgraph/dependency_models.py +2 -1
  63. htmlgraph/edge_index.py +2 -1
  64. htmlgraph/event_log.py +83 -64
  65. htmlgraph/event_migration.py +2 -1
  66. htmlgraph/file_watcher.py +12 -8
  67. htmlgraph/find_api.py +2 -1
  68. htmlgraph/git_events.py +6 -2
  69. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  70. htmlgraph/hooks/drift_handler.py +3 -3
  71. htmlgraph/hooks/event_tracker.py +40 -61
  72. htmlgraph/hooks/installer.py +5 -1
  73. htmlgraph/hooks/orchestrator.py +4 -0
  74. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  75. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  76. htmlgraph/hooks/posttooluse.py +4 -0
  77. htmlgraph/hooks/prompt_analyzer.py +5 -5
  78. htmlgraph/hooks/session_handler.py +2 -1
  79. htmlgraph/hooks/session_summary.py +6 -2
  80. htmlgraph/hooks/validator.py +8 -4
  81. htmlgraph/ids.py +2 -1
  82. htmlgraph/learning.py +2 -1
  83. htmlgraph/mcp_server.py +2 -1
  84. htmlgraph/operations/analytics.py +2 -1
  85. htmlgraph/operations/bootstrap.py +2 -1
  86. htmlgraph/operations/events.py +2 -1
  87. htmlgraph/operations/fastapi_server.py +2 -1
  88. htmlgraph/operations/hooks.py +2 -1
  89. htmlgraph/operations/initialization.py +2 -1
  90. htmlgraph/operations/server.py +2 -1
  91. htmlgraph/orchestration/claude_launcher.py +23 -20
  92. htmlgraph/orchestration/command_builder.py +2 -1
  93. htmlgraph/orchestration/headless_spawner.py +6 -2
  94. htmlgraph/orchestration/model_selection.py +7 -3
  95. htmlgraph/orchestration/plugin_manager.py +24 -19
  96. htmlgraph/orchestration/spawners/claude.py +5 -2
  97. htmlgraph/orchestration/spawners/codex.py +12 -19
  98. htmlgraph/orchestration/spawners/copilot.py +13 -18
  99. htmlgraph/orchestration/spawners/gemini.py +12 -19
  100. htmlgraph/orchestration/subprocess_runner.py +6 -3
  101. htmlgraph/orchestration/task_coordination.py +16 -8
  102. htmlgraph/orchestrator.py +2 -1
  103. htmlgraph/parallel.py +2 -1
  104. htmlgraph/query_builder.py +2 -1
  105. htmlgraph/reflection.py +2 -1
  106. htmlgraph/refs.py +2 -1
  107. htmlgraph/repo_hash.py +2 -1
  108. htmlgraph/repositories/__init__.py +292 -0
  109. htmlgraph/repositories/analytics_repository.py +455 -0
  110. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  111. htmlgraph/repositories/feature_repository.py +581 -0
  112. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  113. htmlgraph/repositories/feature_repository_memory.py +607 -0
  114. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  115. htmlgraph/repositories/filter_service.py +620 -0
  116. htmlgraph/repositories/filter_service_standard.py +445 -0
  117. htmlgraph/repositories/shared_cache.py +621 -0
  118. htmlgraph/repositories/shared_cache_memory.py +395 -0
  119. htmlgraph/repositories/track_repository.py +552 -0
  120. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  121. htmlgraph/repositories/track_repository_memory.py +508 -0
  122. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  123. htmlgraph/sdk/__init__.py +398 -0
  124. htmlgraph/sdk/__init__.pyi +14 -0
  125. htmlgraph/sdk/analytics/__init__.py +19 -0
  126. htmlgraph/sdk/analytics/engine.py +155 -0
  127. htmlgraph/sdk/analytics/helpers.py +178 -0
  128. htmlgraph/sdk/analytics/registry.py +109 -0
  129. htmlgraph/sdk/base.py +484 -0
  130. htmlgraph/sdk/constants.py +216 -0
  131. htmlgraph/sdk/core.pyi +308 -0
  132. htmlgraph/sdk/discovery.py +120 -0
  133. htmlgraph/sdk/help/__init__.py +12 -0
  134. htmlgraph/sdk/help/mixin.py +699 -0
  135. htmlgraph/sdk/mixins/__init__.py +15 -0
  136. htmlgraph/sdk/mixins/attribution.py +113 -0
  137. htmlgraph/sdk/mixins/mixin.py +410 -0
  138. htmlgraph/sdk/operations/__init__.py +12 -0
  139. htmlgraph/sdk/operations/mixin.py +427 -0
  140. htmlgraph/sdk/orchestration/__init__.py +17 -0
  141. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  142. htmlgraph/sdk/orchestration/spawner.py +204 -0
  143. htmlgraph/sdk/planning/__init__.py +19 -0
  144. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  145. htmlgraph/sdk/planning/mixin.py +211 -0
  146. htmlgraph/sdk/planning/parallel.py +186 -0
  147. htmlgraph/sdk/planning/queue.py +210 -0
  148. htmlgraph/sdk/planning/recommendations.py +87 -0
  149. htmlgraph/sdk/planning/smart_planning.py +319 -0
  150. htmlgraph/sdk/session/__init__.py +19 -0
  151. htmlgraph/sdk/session/continuity.py +57 -0
  152. htmlgraph/sdk/session/handoff.py +110 -0
  153. htmlgraph/sdk/session/info.py +309 -0
  154. htmlgraph/sdk/session/manager.py +103 -0
  155. htmlgraph/sdk/strategic/__init__.py +26 -0
  156. htmlgraph/sdk/strategic/mixin.py +563 -0
  157. htmlgraph/server.py +21 -17
  158. htmlgraph/session_warning.py +2 -1
  159. htmlgraph/sessions/handoff.py +4 -3
  160. htmlgraph/system_prompts.py +2 -1
  161. htmlgraph/track_builder.py +2 -1
  162. htmlgraph/transcript.py +2 -1
  163. htmlgraph/watch.py +2 -1
  164. htmlgraph/work_type_utils.py +2 -1
  165. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/METADATA +1 -1
  166. htmlgraph-0.27.1.dist-info/RECORD +332 -0
  167. htmlgraph/sdk.py +0 -3500
  168. htmlgraph-0.26.25.dist-info/RECORD +0 -274
  169. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/dashboard.html +0 -0
  170. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/styles.css +0 -0
  171. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  172. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  173. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  174. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/WHEEL +0 -0
  175. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  HtmlGraph Event Tracker Module
3
7
 
@@ -22,7 +26,6 @@ import json
22
26
  import os
23
27
  import re
24
28
  import subprocess
25
- import sys
26
29
  from datetime import datetime, timedelta, timezone
27
30
  from pathlib import Path
28
31
  from typing import Any, cast # noqa: F401
@@ -149,10 +152,7 @@ def get_parent_user_query(db: HtmlGraphDB, session_id: str) -> str | None:
149
152
  return str(row[0])
150
153
  return None
151
154
  except Exception as e:
152
- print(
153
- f"Debug: Database query for UserQuery failed: {e}",
154
- file=sys.stderr,
155
- )
155
+ logger.warning(f"Debug: Database query for UserQuery failed: {e}")
156
156
  return None
157
157
 
158
158
 
@@ -194,9 +194,8 @@ def load_drift_queue(graph_dir: Path, max_age_hours: int = 48) -> dict[str, Any]
194
194
  queue["activities"] = fresh_activities
195
195
  save_drift_queue(graph_dir, queue)
196
196
  removed = original_count - len(fresh_activities)
197
- print(
198
- f"Cleaned {removed} stale drift queue entries (older than {max_age_hours}h)",
199
- file=sys.stderr,
197
+ logger.warning(
198
+ f"Cleaned {removed} stale drift queue entries (older than {max_age_hours}h)"
200
199
  )
201
200
 
202
201
  return cast(dict[Any, Any], queue)
@@ -212,7 +211,7 @@ def save_drift_queue(graph_dir: Path, queue: dict[str, Any]) -> None:
212
211
  with open(queue_path, "w") as f:
213
212
  json.dump(queue, f, indent=2, default=str)
214
213
  except Exception as e:
215
- print(f"Warning: Could not save drift queue: {e}", file=sys.stderr)
214
+ logger.warning(f"Warning: Could not save drift queue: {e}")
216
215
 
217
216
 
218
217
  def clear_drift_queue_activities(graph_dir: Path) -> None:
@@ -236,7 +235,7 @@ def clear_drift_queue_activities(graph_dir: Path) -> None:
236
235
  with open(queue_path, "w") as f:
237
236
  json.dump(queue, f, indent=2)
238
237
  except Exception as e:
239
- print(f"Warning: Could not clear drift queue: {e}", file=sys.stderr)
238
+ logger.warning(f"Warning: Could not clear drift queue: {e}")
240
239
 
241
240
 
242
241
  def add_to_drift_queue(
@@ -624,7 +623,7 @@ def record_event_to_sqlite(
624
623
  return None
625
624
 
626
625
  except Exception as e:
627
- print(f"Warning: Could not record event to SQLite: {e}", file=sys.stderr)
626
+ logger.warning(f"Warning: Could not record event to SQLite: {e}")
628
627
  return None
629
628
 
630
629
 
@@ -676,7 +675,7 @@ def record_delegation_to_sqlite(
676
675
  return None
677
676
 
678
677
  except Exception as e:
679
- print(f"Warning: Could not record delegation to SQLite: {e}", file=sys.stderr)
678
+ logger.warning(f"Warning: Could not record delegation to SQLite: {e}")
680
679
  return None
681
680
 
682
681
 
@@ -702,7 +701,7 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
702
701
  try:
703
702
  manager = SessionManager(graph_dir)
704
703
  except Exception as e:
705
- print(f"Warning: Could not initialize SessionManager: {e}", file=sys.stderr)
704
+ logger.warning(f"Warning: Could not initialize SessionManager: {e}")
706
705
  return {"continue": True}
707
706
 
708
707
  # Initialize SQLite database for event recording
@@ -713,7 +712,7 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
713
712
 
714
713
  db = HtmlGraphDB(str(get_database_path()))
715
714
  except Exception as e:
716
- print(f"Warning: Could not initialize SQLite database: {e}", file=sys.stderr)
715
+ logger.warning(f"Warning: Could not initialize SQLite database: {e}")
717
716
  # Continue without SQLite (graceful degradation)
718
717
 
719
718
  # Detect agent and model from environment
@@ -804,36 +803,28 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
804
803
  task_row = cursor.fetchone()
805
804
  if task_row:
806
805
  task_event_id_from_db = task_row[0]
807
- print(
808
- f"DEBUG Method 1 fallback: Found task_delegation={task_event_id_from_db} for {subagent_type}",
809
- file=sys.stderr,
806
+ logger.warning(
807
+ f"DEBUG Method 1 fallback: Found task_delegation={task_event_id_from_db} for {subagent_type}"
810
808
  )
811
809
  else:
812
- print(
813
- f"DEBUG Method 1: No task_delegation found for subagent_type={subagent_type}",
814
- file=sys.stderr,
810
+ logger.warning(
811
+ f"DEBUG Method 1: No task_delegation found for subagent_type={subagent_type}"
815
812
  )
816
813
  else:
817
- print(
818
- f"DEBUG Method 1: Found task_delegation={task_event_id_from_db} for subagent {subagent_type}",
819
- file=sys.stderr,
814
+ logger.warning(
815
+ f"DEBUG Method 1: Found task_delegation={task_event_id_from_db} for subagent {subagent_type}"
820
816
  )
821
817
  except Exception as e:
822
- print(
823
- f"DEBUG: Error finding task_delegation for Method 1: {e}",
824
- file=sys.stderr,
818
+ logger.warning(
819
+ f"DEBUG: Error finding task_delegation for Method 1: {e}"
825
820
  )
826
821
 
827
- print(
822
+ logger.debug(
828
823
  f"DEBUG subagent persistence: Found current session as subagent in sessions table: "
829
824
  f"type={subagent_type}, parent_session={parent_session_id}, task_event={task_event_id_from_db}",
830
- file=sys.stderr,
831
825
  )
832
826
  except Exception as e:
833
- print(
834
- f"DEBUG: Error checking sessions table for subagent: {e}",
835
- file=sys.stderr,
836
- )
827
+ logger.warning(f"DEBUG: Error checking sessions table for subagent: {e}")
837
828
 
838
829
  # Method 2: Environment variables (for first tool call before session table is populated)
839
830
  if not subagent_type:
@@ -878,17 +869,13 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
878
869
  task_event_id_from_db = (
879
870
  task_event_id # Store for later use as parent_event_id
880
871
  )
881
- print(
872
+ logger.debug(
882
873
  f"DEBUG subagent detection (database): Detected active task_delegation "
883
874
  f"type={subagent_type}, parent_session={parent_session_id}, "
884
- f"parent_event={task_event_id}",
885
- file=sys.stderr,
875
+ f"parent_event={task_event_id}"
886
876
  )
887
877
  except Exception as e:
888
- print(
889
- f"DEBUG: Error detecting subagent from database: {e}",
890
- file=sys.stderr,
891
- )
878
+ logger.warning(f"DEBUG: Error detecting subagent from database: {e}")
892
879
 
893
880
  if subagent_type and parent_session_id:
894
881
  # We're in a subagent - create or get subagent session
@@ -899,9 +886,8 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
899
886
  existing = manager.session_converter.load(subagent_session_id)
900
887
  if existing:
901
888
  active_session = existing
902
- print(
903
- f"Debug: Using existing subagent session: {subagent_session_id}",
904
- file=sys.stderr,
889
+ logger.warning(
890
+ f"Debug: Using existing subagent session: {subagent_session_id}"
905
891
  )
906
892
  else:
907
893
  # Create new subagent session with parent link
@@ -913,16 +899,12 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
913
899
  parent_session_id=parent_session_id,
914
900
  title=f"{subagent_type.capitalize()} Subagent",
915
901
  )
916
- print(
902
+ logger.debug(
917
903
  f"Debug: Created subagent session: {subagent_session_id} "
918
- f"(parent: {parent_session_id})",
919
- file=sys.stderr,
904
+ f"(parent: {parent_session_id})"
920
905
  )
921
906
  except Exception as e:
922
- print(
923
- f"Warning: Could not create subagent session: {e}",
924
- file=sys.stderr,
925
- )
907
+ logger.warning(f"Warning: Could not create subagent session: {e}")
926
908
  return {"continue": True}
927
909
 
928
910
  # Override detected agent for subagent context
@@ -1004,9 +986,8 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
1004
986
  )
1005
987
  except Exception as e:
1006
988
  # Session may already exist, that's OK - continue
1007
- print(
1008
- f"Debug: Could not insert session to SQLite (may already exist): {e}",
1009
- file=sys.stderr,
989
+ logger.warning(
990
+ f"Debug: Could not insert session to SQLite (may already exist): {e}"
1010
991
  )
1011
992
 
1012
993
  # Handle different hook types
@@ -1031,7 +1012,7 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
1031
1012
  feature_id=result.feature_id if result else None,
1032
1013
  )
1033
1014
  except Exception as e:
1034
- print(f"Warning: Could not track stop: {e}", file=sys.stderr)
1015
+ logger.warning(f"Warning: Could not track stop: {e}")
1035
1016
  return {"continue": True}
1036
1017
 
1037
1018
  elif hook_type == "UserPromptSubmit":
@@ -1063,7 +1044,7 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
1063
1044
  )
1064
1045
 
1065
1046
  except Exception as e:
1066
- print(f"Warning: Could not track query: {e}", file=sys.stderr)
1047
+ logger.warning(f"Warning: Could not track query: {e}")
1067
1048
  return {"continue": True}
1068
1049
 
1069
1050
  elif hook_type == "PostToolUse":
@@ -1160,14 +1141,12 @@ def track_event(hook_type: str, hook_input: dict[str, Any]) -> dict[str, Any]:
1160
1141
  task_row = cursor.fetchone()
1161
1142
  if task_row:
1162
1143
  parent_activity_id = task_row[0]
1163
- print(
1164
- f"DEBUG: Found active task_delegation={parent_activity_id} in parent_activity_id fallback",
1165
- file=sys.stderr,
1144
+ logger.warning(
1145
+ f"DEBUG: Found active task_delegation={parent_activity_id} in parent_activity_id fallback"
1166
1146
  )
1167
1147
  except Exception as e:
1168
- print(
1169
- f"DEBUG: Error finding task_delegation in parent_activity_id: {e}",
1170
- file=sys.stderr,
1148
+ logger.warning(
1149
+ f"DEBUG: Error finding task_delegation in parent_activity_id: {e}"
1171
1150
  )
1172
1151
 
1173
1152
  # Only if no active task found, fall back to UserQuery
@@ -1320,7 +1299,7 @@ Or manually create a work item in .htmlgraph/ (bug, feature, spike, or chore).""
1320
1299
  nudge = f"Drift detected ({drift_score:.2f}): Activity may not align with {feature_id}. Consider refocusing or updating the feature."
1321
1300
 
1322
1301
  except Exception as e:
1323
- print(f"Warning: Could not track activity: {e}", file=sys.stderr)
1302
+ logger.warning(f"Warning: Could not track activity: {e}")
1324
1303
 
1325
1304
  # Build response
1326
1305
  response: dict[str, Any] = {"continue": True}
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Git hooks installation and configuration management.
3
7
  """
@@ -52,7 +56,7 @@ class HookConfig:
52
56
  user_config = json.load(f)
53
57
  self.config.update(user_config)
54
58
  except Exception as e:
55
- print(f"Warning: Failed to load hook config: {e}")
59
+ logger.info(f"Warning: Failed to load hook config: {e}")
56
60
 
57
61
  def save(self) -> None:
58
62
  """Save configuration to file."""
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Orchestrator Enforcement Module
3
7
 
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Orchestrator Reflection Module
3
7
 
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env python3
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+
2
6
  """
3
7
  PostToolUseFailure Hook - Automatic Error Tracking and Debug Spike Creation
4
8
 
@@ -109,7 +113,7 @@ def run(hook_input: dict[str, Any]) -> dict[str, Any]:
109
113
 
110
114
  except Exception as e:
111
115
  # Never raise - log and continue
112
- print(f"PostToolUseFailure hook error: {e}", file=sys.stderr)
116
+ logger.warning(f"PostToolUseFailure hook error: {e}")
113
117
  return {"continue": True}
114
118
 
115
119
 
@@ -238,10 +242,10 @@ def create_debug_spike(tool: str, error: str, log_path: Path) -> None:
238
242
  with open(spike_marker, "w") as f:
239
243
  json.dump(existing_spikes, f, indent=2)
240
244
 
241
- print(f"Created debug spike: {spike.id}", file=sys.stderr)
245
+ logger.warning(f"Created debug spike: {spike.id}")
242
246
 
243
247
  except Exception as e:
244
- print(f"Failed to create debug spike: {e}", file=sys.stderr)
248
+ logger.warning(f"Failed to create debug spike: {e}")
245
249
 
246
250
 
247
251
  def main() -> None:
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Unified PostToolUse Hook - Parallel Execution of Multiple Tasks
3
7
 
@@ -259,7 +259,7 @@ def get_session_violation_count(context: HookContext) -> tuple[int, int]:
259
259
  Example:
260
260
  >>> violation_count, waste_tokens = get_session_violation_count(context)
261
261
  >>> if violation_count > 0:
262
- ... print(f"Violations this session: {violation_count}")
262
+ ... logger.info(f"Violations this session: {violation_count}")
263
263
  """
264
264
  try:
265
265
  from htmlgraph.cigs import ViolationTracker
@@ -289,7 +289,7 @@ def get_active_work_item(context: HookContext) -> dict[str, Any] | None:
289
289
  Example:
290
290
  >>> active = get_active_work_item(context)
291
291
  >>> if active and active['type'] == 'feature':
292
- ... print(f"Active feature: {active['title']}")
292
+ ... logger.info(f"Active feature: {active['title']}")
293
293
  """
294
294
  try:
295
295
  from htmlgraph import SDK
@@ -326,7 +326,7 @@ def generate_guidance(
326
326
  >>> classification = classify_prompt("Implement new API endpoint")
327
327
  >>> guidance = generate_guidance(classification, None, prompt)
328
328
  >>> if guidance:
329
- ... print(guidance)
329
+ ... logger.info("%s", guidance)
330
330
  """
331
331
 
332
332
  # If continuing and has active work, no guidance needed
@@ -467,7 +467,7 @@ def generate_cigs_guidance(
467
467
  >>> cigs = classify_cigs_intent("Search for all error handling")
468
468
  >>> guidance = generate_cigs_guidance(cigs, 0, 0)
469
469
  >>> if guidance:
470
- ... print(guidance)
470
+ ... logger.info("%s", guidance)
471
471
  """
472
472
  imperatives = []
473
473
 
@@ -544,7 +544,7 @@ def create_user_query_event(context: HookContext, prompt: str) -> str | None:
544
544
  >>> context = HookContext.from_input(hook_input)
545
545
  >>> event_id = create_user_query_event(context, "Implement feature X")
546
546
  >>> if event_id:
547
- ... print(f"Created event: {event_id}")
547
+ ... logger.info(f"Created event: {event_id}")
548
548
  """
549
549
  try:
550
550
  session_id = context.session_id
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  HtmlGraph Session Handler Module
3
5
 
@@ -24,7 +26,6 @@ Public API:
24
26
  Check if HtmlGraph has updates available
25
27
  """
26
28
 
27
- from __future__ import annotations
28
29
 
29
30
  import json
30
31
  import logging
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Session Summary Module - CIGS Integration
3
7
 
@@ -353,7 +357,7 @@ class CIGSSessionSummarizer:
353
357
  with open(summary_file, "w") as f:
354
358
  json.dump(summary_data, f, indent=2, default=str)
355
359
  except Exception as e:
356
- print(f"Warning: Failed to persist summary: {e}", file=sys.stderr)
360
+ logger.warning(f"Warning: Failed to persist summary: {e}")
357
361
 
358
362
 
359
363
  def main() -> None:
@@ -387,5 +391,5 @@ def main() -> None:
387
391
  result = summarizer.summarize(session_id)
388
392
  print(json.dumps(result))
389
393
  except Exception as e:
390
- print(f"Warning: Could not generate CIGS summary: {e}", file=sys.stderr)
394
+ logger.warning(f"Warning: Could not generate CIGS summary: {e}")
391
395
  print(json.dumps({"continue": True}))
@@ -1,3 +1,7 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger(__name__)
4
+
1
5
  """
2
6
  Work Validation Module for HtmlGraph Hooks
3
7
 
@@ -22,9 +26,9 @@ Example:
22
26
  result = validate_tool_call("Edit", {"file_path": "test.py"}, config, history)
23
27
 
24
28
  if result["decision"] == "block":
25
- print(result["reason"])
29
+ logger.debug("Validation reason: %s", result["reason"])
26
30
  elif "guidance" in result:
27
- print(result["guidance"])
31
+ logger.debug("Validation guidance: %s", result["guidance"])
28
32
  """
29
33
 
30
34
  import json
@@ -448,9 +452,9 @@ def validate_tool_call(
448
452
  history = load_tool_history(session_id)
449
453
  result = validate_tool_call("Edit", {"file_path": "test.py"}, config, history)
450
454
  if result["decision"] == "block":
451
- print(result["reason"])
455
+ logger.debug("Validation reason: %s", result["reason"])
452
456
  elif "guidance" in result:
453
- print(result["guidance"])
457
+ logger.debug("Validation guidance: %s", result["guidance"])
454
458
  """
455
459
  # Check if this is a subagent context - subagents have unrestricted tool access
456
460
  if is_subagent_context():
htmlgraph/ids.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Hash-based ID generation for HtmlGraph.
3
5
 
@@ -15,7 +17,6 @@ collision probability is effectively zero even with thousands
15
17
  of concurrent agents creating tasks simultaneously.
16
18
  """
17
19
 
18
- from __future__ import annotations
19
20
 
20
21
  import hashlib
21
22
  import os
htmlgraph/learning.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Active Learning Persistence Module.
3
5
 
@@ -5,7 +7,6 @@ Bridges TranscriptAnalytics to the HtmlGraph for persistent learning.
5
7
  Analyzes sessions and persists patterns, insights, and metrics to the graph.
6
8
  """
7
9
 
8
- from __future__ import annotations
9
10
 
10
11
  from collections import Counter
11
12
  from datetime import datetime
htmlgraph/mcp_server.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """
2
4
  Minimal MCP (Model Context Protocol) server for HtmlGraph.
3
5
 
@@ -24,7 +26,6 @@ Example SDK usage:
24
26
  f.status = "done"
25
27
  """
26
28
 
27
- from __future__ import annotations
28
29
 
29
30
  import json
30
31
  import os
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  """Analytics operations for HtmlGraph."""
2
4
 
3
- from __future__ import annotations
4
5
 
5
6
  from dataclasses import dataclass
6
7
  from pathlib import Path
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """HtmlGraph bootstrap operations.
2
4
 
3
5
  One-command setup to go from installation to first value in under 60 seconds.
@@ -13,7 +15,6 @@ The bootstrap process includes:
13
15
  This is designed for simplicity and speed - the minimal viable setup.
14
16
  """
15
17
 
16
- from __future__ import annotations
17
18
 
18
19
  import json
19
20
  import subprocess
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  """Event and analytics index operations for HtmlGraph."""
2
4
 
3
- from __future__ import annotations
4
5
 
5
6
  from dataclasses import dataclass
6
7
  from pathlib import Path
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  """FastAPI-based server for HtmlGraph dashboard with real-time observability."""
2
4
 
3
- from __future__ import annotations
4
5
 
5
6
  import logging
6
7
  from dataclasses import dataclass
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  """Git hook operations for HtmlGraph."""
2
4
 
3
- from __future__ import annotations
4
5
 
5
6
  import json
6
7
  import shutil
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  """HtmlGraph initialization operations.
2
4
 
3
5
  This module provides functions for initializing the .htmlgraph directory structure,
@@ -13,7 +15,6 @@ The initialization process includes:
13
15
  Extracted from monolithic cmd_init() implementation for better maintainability.
14
16
  """
15
17
 
16
- from __future__ import annotations
17
18
 
18
19
  import json
19
20
  import sqlite3
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  """Server operations for HtmlGraph."""
2
4
 
3
- from __future__ import annotations
4
5
 
5
6
  from dataclasses import dataclass
6
7
  from pathlib import Path
@@ -1,11 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  """Claude Code launcher with multiple integration modes.
2
4
 
3
5
  Coordinates launching Claude Code with various HtmlGraph integration options.
4
6
  """
5
7
 
6
- from __future__ import annotations
7
-
8
8
  import argparse
9
+ import logging
9
10
  import sys
10
11
  from pathlib import Path
11
12
 
@@ -14,6 +15,8 @@ from htmlgraph.orchestration.plugin_manager import PluginManager
14
15
  from htmlgraph.orchestration.prompts import get_orchestrator_prompt
15
16
  from htmlgraph.orchestration.subprocess_runner import SubprocessRunner
16
17
 
18
+ logger = logging.getLogger(__name__)
19
+
17
20
 
18
21
  class ClaudeLauncher:
19
22
  """Launch Claude Code with various HtmlGraph integration modes.
@@ -50,7 +53,7 @@ class ClaudeLauncher:
50
53
  else:
51
54
  self._launch_default_mode()
52
55
  except Exception as e:
53
- print(f"Error: Failed to start Claude Code: {e}", file=sys.stderr)
56
+ logger.warning(f"Error: Failed to start Claude Code: {e}")
54
57
  sys.exit(1)
55
58
 
56
59
  def _launch_orchestrator_mode(self) -> None:
@@ -92,8 +95,8 @@ class ClaudeLauncher:
92
95
 
93
96
  # Show status
94
97
  if self.interactive:
95
- print("Resuming last Claude Code session...")
96
- print(" ✓ Multi-AI delegation rules injected")
98
+ logger.info("Resuming last Claude Code session...")
99
+ logger.info(" ✓ Multi-AI delegation rules injected")
97
100
 
98
101
  # Build command
99
102
  builder = ClaudeCommandBuilder().with_resume().with_system_prompt(prompt)
@@ -102,7 +105,7 @@ class ClaudeLauncher:
102
105
  if plugin_dir.exists():
103
106
  builder.with_plugin_dir(str(plugin_dir))
104
107
  if self.interactive:
105
- print(f" ✓ Loading plugin from: {plugin_dir}")
108
+ logger.info(f" ✓ Loading plugin from: {plugin_dir}")
106
109
 
107
110
  cmd = builder.build()
108
111
 
@@ -147,7 +150,7 @@ class ClaudeLauncher:
147
150
 
148
151
  # Show status
149
152
  if self.interactive:
150
- print("Starting Claude Code with multi-AI delegation rules...")
153
+ logger.info("Starting Claude Code with multi-AI delegation rules...")
151
154
 
152
155
  # Build command
153
156
  cmd = ClaudeCommandBuilder().with_system_prompt(prompt).build()
@@ -158,14 +161,14 @@ class ClaudeLauncher:
158
161
  def _print_orchestrator_banner(self) -> None:
159
162
  """Print orchestrator mode banner."""
160
163
  print("=" * 60)
161
- print("🤖 HtmlGraph Orchestrator Mode")
164
+ logger.info("🤖 HtmlGraph Orchestrator Mode")
162
165
  print("=" * 60)
163
- print("\nStarting Claude Code with orchestrator system prompt...")
164
- print("Key directives:")
165
- print(" ✓ Delegate to Gemini (FREE), Codex, Copilot first")
166
- print(" ✓ Use Task() only as fallback")
167
- print(" ✓ Create work items before delegating")
168
- print(" ✓ Track all work in .htmlgraph/")
166
+ logger.info("\nStarting Claude Code with orchestrator system prompt...")
167
+ logger.info("Key directives:")
168
+ logger.info(" ✓ Delegate to Gemini (FREE), Codex, Copilot first")
169
+ logger.info(" ✓ Use Task() only as fallback")
170
+ logger.info(" ✓ Create work items before delegating")
171
+ logger.info(" ✓ Track all work in .htmlgraph/")
169
172
  print()
170
173
 
171
174
  def _print_dev_mode_banner(self, plugin_dir: Path) -> None:
@@ -175,11 +178,11 @@ class ClaudeLauncher:
175
178
  plugin_dir: Path to local plugin directory
176
179
  """
177
180
  print("=" * 60)
178
- print("🔧 HtmlGraph Development Mode")
181
+ logger.info("🔧 HtmlGraph Development Mode")
179
182
  print("=" * 60)
180
- print(f"\nLoading plugin from: {plugin_dir}")
181
- print(" ✓ Skills, agents, and hooks will be loaded from local files")
182
- print(" ✓ Orchestrator system prompt will be appended")
183
- print(" ✓ Multi-AI delegation rules will be injected")
184
- print(" ✓ Changes to plugin files will take effect after restart")
183
+ logger.info(f"\nLoading plugin from: {plugin_dir}")
184
+ logger.info(" ✓ Skills, agents, and hooks will be loaded from local files")
185
+ logger.info(" ✓ Orchestrator system prompt will be appended")
186
+ logger.info(" ✓ Multi-AI delegation rules will be injected")
187
+ logger.info(" ✓ Changes to plugin files will take effect after restart")
185
188
  print()