htmlgraph 0.26.25__py3-none-any.whl → 0.27.0__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 (147) 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/cross_session.py +13 -9
  7. htmlgraph/analytics/dependency.py +10 -6
  8. htmlgraph/analytics/work_type.py +15 -11
  9. htmlgraph/analytics_index.py +2 -1
  10. htmlgraph/api/main.py +58 -28
  11. htmlgraph/attribute_index.py +2 -1
  12. htmlgraph/builders/base.py +2 -1
  13. htmlgraph/builders/bug.py +2 -1
  14. htmlgraph/builders/chore.py +2 -1
  15. htmlgraph/builders/epic.py +2 -1
  16. htmlgraph/builders/feature.py +2 -1
  17. htmlgraph/builders/insight.py +2 -1
  18. htmlgraph/builders/metric.py +2 -1
  19. htmlgraph/builders/pattern.py +2 -1
  20. htmlgraph/builders/phase.py +2 -1
  21. htmlgraph/builders/spike.py +2 -1
  22. htmlgraph/builders/track.py +2 -1
  23. htmlgraph/cli/analytics.py +2 -1
  24. htmlgraph/cli/base.py +2 -1
  25. htmlgraph/cli/core.py +2 -1
  26. htmlgraph/cli/main.py +2 -1
  27. htmlgraph/cli/models.py +2 -1
  28. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  29. htmlgraph/cli/work/__init__.py +2 -1
  30. htmlgraph/cli/work/browse.py +2 -1
  31. htmlgraph/cli/work/features.py +2 -1
  32. htmlgraph/cli/work/orchestration.py +2 -1
  33. htmlgraph/cli/work/report.py +2 -1
  34. htmlgraph/cli/work/sessions.py +2 -1
  35. htmlgraph/cli/work/snapshot.py +2 -1
  36. htmlgraph/cli/work/tracks.py +2 -1
  37. htmlgraph/collections/base.py +10 -5
  38. htmlgraph/collections/bug.py +2 -1
  39. htmlgraph/collections/chore.py +2 -1
  40. htmlgraph/collections/epic.py +2 -1
  41. htmlgraph/collections/feature.py +2 -1
  42. htmlgraph/collections/insight.py +2 -1
  43. htmlgraph/collections/metric.py +2 -1
  44. htmlgraph/collections/pattern.py +2 -1
  45. htmlgraph/collections/phase.py +2 -1
  46. htmlgraph/collections/session.py +12 -7
  47. htmlgraph/collections/spike.py +6 -1
  48. htmlgraph/collections/task_delegation.py +7 -2
  49. htmlgraph/collections/todo.py +2 -1
  50. htmlgraph/collections/traces.py +15 -10
  51. htmlgraph/context_analytics.py +2 -1
  52. htmlgraph/dependency_models.py +2 -1
  53. htmlgraph/edge_index.py +2 -1
  54. htmlgraph/event_log.py +81 -66
  55. htmlgraph/event_migration.py +2 -1
  56. htmlgraph/file_watcher.py +12 -8
  57. htmlgraph/find_api.py +2 -1
  58. htmlgraph/git_events.py +6 -2
  59. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  60. htmlgraph/hooks/drift_handler.py +3 -3
  61. htmlgraph/hooks/event_tracker.py +40 -61
  62. htmlgraph/hooks/installer.py +5 -1
  63. htmlgraph/hooks/orchestrator.py +4 -0
  64. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  65. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  66. htmlgraph/hooks/posttooluse.py +4 -0
  67. htmlgraph/hooks/prompt_analyzer.py +5 -5
  68. htmlgraph/hooks/session_handler.py +2 -1
  69. htmlgraph/hooks/session_summary.py +6 -2
  70. htmlgraph/hooks/validator.py +8 -4
  71. htmlgraph/ids.py +2 -1
  72. htmlgraph/learning.py +2 -1
  73. htmlgraph/mcp_server.py +2 -1
  74. htmlgraph/operations/analytics.py +2 -1
  75. htmlgraph/operations/bootstrap.py +2 -1
  76. htmlgraph/operations/events.py +2 -1
  77. htmlgraph/operations/fastapi_server.py +2 -1
  78. htmlgraph/operations/hooks.py +2 -1
  79. htmlgraph/operations/initialization.py +2 -1
  80. htmlgraph/operations/server.py +2 -1
  81. htmlgraph/orchestration/claude_launcher.py +23 -20
  82. htmlgraph/orchestration/command_builder.py +2 -1
  83. htmlgraph/orchestration/headless_spawner.py +6 -2
  84. htmlgraph/orchestration/model_selection.py +7 -3
  85. htmlgraph/orchestration/plugin_manager.py +24 -19
  86. htmlgraph/orchestration/spawners/claude.py +5 -2
  87. htmlgraph/orchestration/spawners/codex.py +12 -19
  88. htmlgraph/orchestration/spawners/copilot.py +13 -18
  89. htmlgraph/orchestration/spawners/gemini.py +12 -19
  90. htmlgraph/orchestration/subprocess_runner.py +6 -3
  91. htmlgraph/orchestration/task_coordination.py +16 -8
  92. htmlgraph/orchestrator.py +2 -1
  93. htmlgraph/parallel.py +2 -1
  94. htmlgraph/query_builder.py +2 -1
  95. htmlgraph/reflection.py +2 -1
  96. htmlgraph/refs.py +2 -1
  97. htmlgraph/repo_hash.py +2 -1
  98. htmlgraph/sdk/__init__.py +398 -0
  99. htmlgraph/sdk/__init__.pyi +14 -0
  100. htmlgraph/sdk/analytics/__init__.py +19 -0
  101. htmlgraph/sdk/analytics/engine.py +155 -0
  102. htmlgraph/sdk/analytics/helpers.py +178 -0
  103. htmlgraph/sdk/analytics/registry.py +109 -0
  104. htmlgraph/sdk/base.py +484 -0
  105. htmlgraph/sdk/constants.py +216 -0
  106. htmlgraph/sdk/core.pyi +308 -0
  107. htmlgraph/sdk/discovery.py +120 -0
  108. htmlgraph/sdk/help/__init__.py +12 -0
  109. htmlgraph/sdk/help/mixin.py +699 -0
  110. htmlgraph/sdk/mixins/__init__.py +15 -0
  111. htmlgraph/sdk/mixins/attribution.py +113 -0
  112. htmlgraph/sdk/mixins/mixin.py +410 -0
  113. htmlgraph/sdk/operations/__init__.py +12 -0
  114. htmlgraph/sdk/operations/mixin.py +427 -0
  115. htmlgraph/sdk/orchestration/__init__.py +17 -0
  116. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  117. htmlgraph/sdk/orchestration/spawner.py +204 -0
  118. htmlgraph/sdk/planning/__init__.py +19 -0
  119. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  120. htmlgraph/sdk/planning/mixin.py +211 -0
  121. htmlgraph/sdk/planning/parallel.py +186 -0
  122. htmlgraph/sdk/planning/queue.py +210 -0
  123. htmlgraph/sdk/planning/recommendations.py +87 -0
  124. htmlgraph/sdk/planning/smart_planning.py +319 -0
  125. htmlgraph/sdk/session/__init__.py +19 -0
  126. htmlgraph/sdk/session/continuity.py +57 -0
  127. htmlgraph/sdk/session/handoff.py +110 -0
  128. htmlgraph/sdk/session/info.py +309 -0
  129. htmlgraph/sdk/session/manager.py +103 -0
  130. htmlgraph/server.py +21 -17
  131. htmlgraph/session_warning.py +2 -1
  132. htmlgraph/sessions/handoff.py +4 -3
  133. htmlgraph/system_prompts.py +2 -1
  134. htmlgraph/track_builder.py +2 -1
  135. htmlgraph/transcript.py +2 -1
  136. htmlgraph/watch.py +2 -1
  137. htmlgraph/work_type_utils.py +2 -1
  138. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/METADATA +15 -1
  139. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/RECORD +146 -114
  140. htmlgraph/sdk.py +0 -3500
  141. {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/dashboard.html +0 -0
  142. {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/styles.css +0 -0
  143. {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  144. {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  145. {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  146. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/WHEEL +0 -0
  147. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/entry_points.txt +0 -0
@@ -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()
@@ -1,9 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  """Command builder for Claude Code CLI invocations.
2
4
 
3
5
  Provides fluent interface for constructing Claude CLI commands.
4
6
  """
5
7
 
6
- from __future__ import annotations
7
8
 
8
9
  from typing import TYPE_CHECKING
9
10
 
@@ -1,5 +1,9 @@
1
1
  """Headless AI spawner for multi-AI orchestration.
2
2
 
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
3
7
  This module provides backward compatibility by delegating to modular spawner implementations.
4
8
  """
5
9
 
@@ -263,8 +267,8 @@ class HeadlessSpawner:
263
267
  >>> spawner = HeadlessSpawner()
264
268
  >>> result = spawner.spawn_claude("What is 2+2?")
265
269
  >>> if result.success:
266
- ... print(result.response) # "4"
267
- ... print(f"Cost: ${result.raw_output['total_cost_usd']}")
270
+ ... logger.info("%s", result.response) # "4"
271
+ ... logger.info(f"Cost: ${result.raw_output['total_cost_usd']}")
268
272
  """
269
273
  return self._claude_spawner.spawn(
270
274
  prompt=prompt,
@@ -1,5 +1,9 @@
1
1
  """Intelligent model selection for task routing.
2
2
 
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
3
7
  This module provides functionality to select the best AI model for a given task
4
8
  based on task type, complexity, and budget constraints.
5
9
 
@@ -179,7 +183,7 @@ class ModelSelection:
179
183
 
180
184
  Example:
181
185
  >>> model = ModelSelection.select_model("implementation", "high", "balanced")
182
- >>> print(model)
186
+ >>> logger.info("%s", model)
183
187
  'claude-opus'
184
188
  """
185
189
  # Normalize inputs
@@ -218,7 +222,7 @@ class ModelSelection:
218
222
 
219
223
  Example:
220
224
  >>> fallbacks = ModelSelection.get_fallback_chain("gemini")
221
- >>> print(fallbacks)
225
+ >>> logger.info("%s", fallbacks)
222
226
  ['claude-haiku', 'claude-sonnet', 'claude-opus']
223
227
  """
224
228
  return ModelSelection.FALLBACK_CHAINS.get(primary_model, ["claude-sonnet"])
@@ -305,7 +309,7 @@ def select_model(
305
309
 
306
310
  Example:
307
311
  >>> model = select_model("implementation", "high")
308
- >>> print(model)
312
+ >>> logger.info("%s", model)
309
313
  """
310
314
  return ModelSelection.select_model(task_type, complexity, budget)
311
315
 
@@ -1,15 +1,18 @@
1
+ from __future__ import annotations
2
+
1
3
  """Plugin management for HtmlGraph Claude Code integration.
2
4
 
3
5
  Centralizes plugin installation, directory management, and validation.
4
6
  """
5
7
 
6
- from __future__ import annotations
7
-
8
+ import logging
8
9
  import subprocess
9
10
  import sys
10
11
  from pathlib import Path
11
12
  from typing import TYPE_CHECKING
12
13
 
14
+ logger = logging.getLogger(__name__)
15
+
13
16
  if TYPE_CHECKING:
14
17
  pass
15
18
 
@@ -40,12 +43,12 @@ class PluginManager:
40
43
  verbose: Whether to show progress messages
41
44
  """
42
45
  if verbose:
43
- print("\n📦 Installing/upgrading HtmlGraph plugin...\n")
46
+ logger.info("\n📦 Installing/upgrading HtmlGraph plugin...\n")
44
47
 
45
48
  # Step 1: Update marketplace
46
49
  try:
47
50
  if verbose:
48
- print(" Updating marketplace...")
51
+ logger.info(" Updating marketplace...")
49
52
  result = subprocess.run(
50
53
  ["claude", "plugin", "marketplace", "update", "htmlgraph"],
51
54
  capture_output=True,
@@ -54,7 +57,7 @@ class PluginManager:
54
57
  )
55
58
  if result.returncode == 0:
56
59
  if verbose:
57
- print(" ✓ Marketplace updated")
60
+ logger.info(" ✓ Marketplace updated")
58
61
  else:
59
62
  # Non-blocking errors
60
63
  if (
@@ -62,20 +65,20 @@ class PluginManager:
62
65
  or "no marketplace" in result.stderr.lower()
63
66
  ):
64
67
  if verbose:
65
- print(" ℹ Marketplace not configured (OK, continuing)")
68
+ logger.info(" ℹ Marketplace not configured (OK, continuing)")
66
69
  elif verbose:
67
- print(f" ⚠ Marketplace update: {result.stderr.strip()}")
70
+ logger.info(f" ⚠ Marketplace update: {result.stderr.strip()}")
68
71
  except FileNotFoundError:
69
72
  if verbose:
70
- print(" ⚠ 'claude' command not found")
73
+ logger.info(" ⚠ 'claude' command not found")
71
74
  except Exception as e:
72
75
  if verbose:
73
- print(f" ⚠ Error updating marketplace: {e}")
76
+ logger.info(f" ⚠ Error updating marketplace: {e}")
74
77
 
75
78
  # Step 2: Try update, fallback to install
76
79
  try:
77
80
  if verbose:
78
- print(" Updating plugin to latest version...")
81
+ logger.info(" Updating plugin to latest version...")
79
82
  result = subprocess.run(
80
83
  ["claude", "plugin", "update", "htmlgraph"],
81
84
  capture_output=True,
@@ -84,7 +87,7 @@ class PluginManager:
84
87
  )
85
88
  if result.returncode == 0:
86
89
  if verbose:
87
- print(" ✓ Plugin updated successfully")
90
+ logger.info(" ✓ Plugin updated successfully")
88
91
  else:
89
92
  # Fallback to install
90
93
  if (
@@ -92,7 +95,7 @@ class PluginManager:
92
95
  or "not found" in result.stderr.lower()
93
96
  ):
94
97
  if verbose:
95
- print(" ℹ Plugin not yet installed, installing...")
98
+ logger.info(" ℹ Plugin not yet installed, installing...")
96
99
  install_result = subprocess.run(
97
100
  ["claude", "plugin", "install", "htmlgraph"],
98
101
  capture_output=True,
@@ -101,20 +104,22 @@ class PluginManager:
101
104
  )
102
105
  if install_result.returncode == 0:
103
106
  if verbose:
104
- print(" ✓ Plugin installed successfully")
107
+ logger.info(" ✓ Plugin installed successfully")
105
108
  elif verbose:
106
- print(f" ⚠ Plugin install: {install_result.stderr.strip()}")
109
+ logger.info(
110
+ f" ⚠ Plugin install: {install_result.stderr.strip()}"
111
+ )
107
112
  elif verbose:
108
- print(f" ⚠ Plugin update: {result.stderr.strip()}")
113
+ logger.info(f" ⚠ Plugin update: {result.stderr.strip()}")
109
114
  except FileNotFoundError:
110
115
  if verbose:
111
- print(" ⚠ 'claude' command not found")
116
+ logger.info(" ⚠ 'claude' command not found")
112
117
  except Exception as e:
113
118
  if verbose:
114
- print(f" ⚠ Error updating plugin: {e}")
119
+ logger.info(f" ⚠ Error updating plugin: {e}")
115
120
 
116
121
  if verbose:
117
- print("\n✓ Plugin installation complete\n")
122
+ logger.info("\n✓ Plugin installation complete\n")
118
123
 
119
124
  @staticmethod
120
125
  def validate_plugin_dir(plugin_dir: Path) -> None:
@@ -127,7 +132,7 @@ class PluginManager:
127
132
  SystemExit: If plugin directory doesn't exist
128
133
  """
129
134
  if not plugin_dir.exists():
130
- print(f"Error: Plugin directory not found: {plugin_dir}", file=sys.stderr)
135
+ logger.warning(f"Error: Plugin directory not found: {plugin_dir}")
131
136
  print(
132
137
  "Expected location: packages/claude-plugin/.claude-plugin",
133
138
  file=sys.stderr,
@@ -1,11 +1,14 @@
1
1
  """Claude spawner implementation."""
2
2
 
3
3
  import json
4
+ import logging
4
5
  import subprocess
5
6
  from typing import TYPE_CHECKING
6
7
 
7
8
  from .base import AIResult, BaseSpawner
8
9
 
10
+ logger = logging.getLogger(__name__)
11
+
9
12
  if TYPE_CHECKING:
10
13
  pass
11
14
 
@@ -64,8 +67,8 @@ class ClaudeSpawner(BaseSpawner):
64
67
  >>> spawner = ClaudeSpawner()
65
68
  >>> result = spawner.spawn("What is 2+2?")
66
69
  >>> if result.success:
67
- ... print(result.response) # "4"
68
- ... print(f"Cost: ${result.raw_output['total_cost_usd']}")
70
+ ... logger.info("%s", result.response) # "4"
71
+ ... logger.info(f"Cost: ${result.raw_output['total_cost_usd']}")
69
72
  """
70
73
  cmd = ["claude", "-p"]
71
74
 
@@ -1,13 +1,15 @@
1
1
  """Codex spawner implementation."""
2
2
 
3
3
  import json
4
+ import logging
4
5
  import subprocess
5
- import sys
6
6
  import time
7
7
  from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from .base import AIResult, BaseSpawner
10
10
 
11
+ logger = logging.getLogger(__name__)
12
+
11
13
  if TYPE_CHECKING:
12
14
  from htmlgraph.sdk import SDK
13
15
 
@@ -232,15 +234,11 @@ class CodexSpawner(BaseSpawner):
232
234
 
233
235
  # Record subprocess invocation if tracker is available
234
236
  subprocess_event_id = None
235
- print(
236
- f"DEBUG: tracker={tracker is not None}, parent_event_id={parent_event_id}",
237
- file=sys.stderr,
237
+ logger.warning(
238
+ f"DEBUG: tracker={tracker is not None}, parent_event_id={parent_event_id}"
238
239
  )
239
240
  if tracker and parent_event_id:
240
- print(
241
- "DEBUG: Recording subprocess invocation for Codex...",
242
- file=sys.stderr,
243
- )
241
+ logger.debug("Recording subprocess invocation for Codex...")
244
242
  try:
245
243
  subprocess_event = tracker.record_tool_call(
246
244
  tool_name="subprocess.codex",
@@ -250,23 +248,18 @@ class CodexSpawner(BaseSpawner):
250
248
  )
251
249
  if subprocess_event:
252
250
  subprocess_event_id = subprocess_event.get("event_id")
253
- print(
254
- f"DEBUG: Subprocess event created for Codex: {subprocess_event_id}",
255
- file=sys.stderr,
251
+ logger.warning(
252
+ f"DEBUG: Subprocess event created for Codex: {subprocess_event_id}"
256
253
  )
257
254
  else:
258
- print("DEBUG: subprocess_event was None", file=sys.stderr)
255
+ logger.debug("subprocess_event was None")
259
256
  except Exception as e:
260
257
  # Tracking failure should not break execution
261
- print(
262
- f"DEBUG: Exception recording Codex subprocess: {e}",
263
- file=sys.stderr,
264
- )
258
+ logger.warning(f"DEBUG: Exception recording Codex subprocess: {e}")
265
259
  pass
266
260
  else:
267
- print(
268
- f"DEBUG: Skipping Codex subprocess tracking - tracker={tracker is not None}, parent_event_id={parent_event_id}",
269
- file=sys.stderr,
261
+ logger.warning(
262
+ f"DEBUG: Skipping Codex subprocess tracking - tracker={tracker is not None}, parent_event_id={parent_event_id}"
270
263
  )
271
264
 
272
265
  result = subprocess.run(