htmlgraph 0.26.24__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 (155) 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 +114 -51
  11. htmlgraph/api/templates/dashboard-redesign.html +3 -3
  12. htmlgraph/api/templates/dashboard.html +3 -3
  13. htmlgraph/api/templates/partials/work-items.html +613 -0
  14. htmlgraph/attribute_index.py +2 -1
  15. htmlgraph/builders/base.py +2 -1
  16. htmlgraph/builders/bug.py +2 -1
  17. htmlgraph/builders/chore.py +2 -1
  18. htmlgraph/builders/epic.py +2 -1
  19. htmlgraph/builders/feature.py +2 -1
  20. htmlgraph/builders/insight.py +2 -1
  21. htmlgraph/builders/metric.py +2 -1
  22. htmlgraph/builders/pattern.py +2 -1
  23. htmlgraph/builders/phase.py +2 -1
  24. htmlgraph/builders/spike.py +2 -1
  25. htmlgraph/builders/track.py +28 -1
  26. htmlgraph/cli/analytics.py +2 -1
  27. htmlgraph/cli/base.py +33 -8
  28. htmlgraph/cli/core.py +2 -1
  29. htmlgraph/cli/main.py +2 -1
  30. htmlgraph/cli/models.py +2 -1
  31. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  32. htmlgraph/cli/work/__init__.py +76 -1
  33. htmlgraph/cli/work/browse.py +115 -0
  34. htmlgraph/cli/work/features.py +2 -1
  35. htmlgraph/cli/work/orchestration.py +2 -1
  36. htmlgraph/cli/work/report.py +2 -1
  37. htmlgraph/cli/work/sessions.py +2 -1
  38. htmlgraph/cli/work/snapshot.py +559 -0
  39. htmlgraph/cli/work/tracks.py +2 -1
  40. htmlgraph/collections/base.py +43 -4
  41. htmlgraph/collections/bug.py +2 -1
  42. htmlgraph/collections/chore.py +2 -1
  43. htmlgraph/collections/epic.py +2 -1
  44. htmlgraph/collections/feature.py +2 -1
  45. htmlgraph/collections/insight.py +2 -1
  46. htmlgraph/collections/metric.py +2 -1
  47. htmlgraph/collections/pattern.py +2 -1
  48. htmlgraph/collections/phase.py +2 -1
  49. htmlgraph/collections/session.py +12 -7
  50. htmlgraph/collections/spike.py +6 -1
  51. htmlgraph/collections/task_delegation.py +7 -2
  52. htmlgraph/collections/todo.py +14 -1
  53. htmlgraph/collections/traces.py +15 -10
  54. htmlgraph/context_analytics.py +2 -1
  55. htmlgraph/converter.py +11 -0
  56. htmlgraph/dependency_models.py +2 -1
  57. htmlgraph/edge_index.py +2 -1
  58. htmlgraph/event_log.py +81 -66
  59. htmlgraph/event_migration.py +2 -1
  60. htmlgraph/file_watcher.py +12 -8
  61. htmlgraph/find_api.py +2 -1
  62. htmlgraph/git_events.py +6 -2
  63. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  64. htmlgraph/hooks/drift_handler.py +3 -3
  65. htmlgraph/hooks/event_tracker.py +40 -61
  66. htmlgraph/hooks/installer.py +5 -1
  67. htmlgraph/hooks/orchestrator.py +92 -14
  68. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  69. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  70. htmlgraph/hooks/posttooluse.py +4 -0
  71. htmlgraph/hooks/prompt_analyzer.py +5 -5
  72. htmlgraph/hooks/session_handler.py +5 -2
  73. htmlgraph/hooks/session_summary.py +6 -2
  74. htmlgraph/hooks/validator.py +8 -4
  75. htmlgraph/ids.py +2 -1
  76. htmlgraph/learning.py +2 -1
  77. htmlgraph/mcp_server.py +2 -1
  78. htmlgraph/models.py +18 -1
  79. htmlgraph/operations/analytics.py +2 -1
  80. htmlgraph/operations/bootstrap.py +2 -1
  81. htmlgraph/operations/events.py +2 -1
  82. htmlgraph/operations/fastapi_server.py +2 -1
  83. htmlgraph/operations/hooks.py +2 -1
  84. htmlgraph/operations/initialization.py +2 -1
  85. htmlgraph/operations/server.py +2 -1
  86. htmlgraph/orchestration/__init__.py +4 -0
  87. htmlgraph/orchestration/claude_launcher.py +23 -20
  88. htmlgraph/orchestration/command_builder.py +2 -1
  89. htmlgraph/orchestration/headless_spawner.py +6 -2
  90. htmlgraph/orchestration/model_selection.py +7 -3
  91. htmlgraph/orchestration/plugin_manager.py +25 -21
  92. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  93. htmlgraph/orchestration/spawners/claude.py +5 -2
  94. htmlgraph/orchestration/spawners/codex.py +12 -19
  95. htmlgraph/orchestration/spawners/copilot.py +13 -18
  96. htmlgraph/orchestration/spawners/gemini.py +12 -19
  97. htmlgraph/orchestration/subprocess_runner.py +6 -3
  98. htmlgraph/orchestration/task_coordination.py +16 -8
  99. htmlgraph/orchestrator.py +2 -1
  100. htmlgraph/parallel.py +2 -1
  101. htmlgraph/query_builder.py +2 -1
  102. htmlgraph/reflection.py +2 -1
  103. htmlgraph/refs.py +344 -0
  104. htmlgraph/repo_hash.py +2 -1
  105. htmlgraph/sdk/__init__.py +398 -0
  106. htmlgraph/sdk/__init__.pyi +14 -0
  107. htmlgraph/sdk/analytics/__init__.py +19 -0
  108. htmlgraph/sdk/analytics/engine.py +155 -0
  109. htmlgraph/sdk/analytics/helpers.py +178 -0
  110. htmlgraph/sdk/analytics/registry.py +109 -0
  111. htmlgraph/sdk/base.py +484 -0
  112. htmlgraph/sdk/constants.py +216 -0
  113. htmlgraph/sdk/core.pyi +308 -0
  114. htmlgraph/sdk/discovery.py +120 -0
  115. htmlgraph/sdk/help/__init__.py +12 -0
  116. htmlgraph/sdk/help/mixin.py +699 -0
  117. htmlgraph/sdk/mixins/__init__.py +15 -0
  118. htmlgraph/sdk/mixins/attribution.py +113 -0
  119. htmlgraph/sdk/mixins/mixin.py +410 -0
  120. htmlgraph/sdk/operations/__init__.py +12 -0
  121. htmlgraph/sdk/operations/mixin.py +427 -0
  122. htmlgraph/sdk/orchestration/__init__.py +17 -0
  123. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  124. htmlgraph/sdk/orchestration/spawner.py +204 -0
  125. htmlgraph/sdk/planning/__init__.py +19 -0
  126. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  127. htmlgraph/sdk/planning/mixin.py +211 -0
  128. htmlgraph/sdk/planning/parallel.py +186 -0
  129. htmlgraph/sdk/planning/queue.py +210 -0
  130. htmlgraph/sdk/planning/recommendations.py +87 -0
  131. htmlgraph/sdk/planning/smart_planning.py +319 -0
  132. htmlgraph/sdk/session/__init__.py +19 -0
  133. htmlgraph/sdk/session/continuity.py +57 -0
  134. htmlgraph/sdk/session/handoff.py +110 -0
  135. htmlgraph/sdk/session/info.py +309 -0
  136. htmlgraph/sdk/session/manager.py +103 -0
  137. htmlgraph/server.py +21 -17
  138. htmlgraph/session_manager.py +1 -7
  139. htmlgraph/session_warning.py +2 -1
  140. htmlgraph/sessions/handoff.py +10 -3
  141. htmlgraph/system_prompts.py +2 -1
  142. htmlgraph/track_builder.py +14 -1
  143. htmlgraph/transcript.py +2 -1
  144. htmlgraph/watch.py +2 -1
  145. htmlgraph/work_type_utils.py +2 -1
  146. {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/METADATA +15 -1
  147. {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/RECORD +154 -117
  148. htmlgraph/sdk.py +0 -3430
  149. {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/dashboard.html +0 -0
  150. {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/styles.css +0 -0
  151. {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  152. {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  153. {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  154. {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/WHEEL +0 -0
  155. {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/entry_points.txt +0 -0
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
htmlgraph/models.py CHANGED
@@ -1385,7 +1385,12 @@ class Session(BaseModel):
1385
1385
 
1386
1386
  # Build handoff HTML
1387
1387
  handoff_html = ""
1388
- if self.handoff_notes or self.recommended_next or self.blockers:
1388
+ if (
1389
+ self.handoff_notes
1390
+ or self.recommended_next
1391
+ or self.blockers
1392
+ or self.recommended_context
1393
+ ):
1389
1394
  handoff_section = """
1390
1395
  <section data-handoff>
1391
1396
  <h3>Handoff Context</h3>"""
@@ -1408,6 +1413,18 @@ class Session(BaseModel):
1408
1413
  </ul>
1409
1414
  </div>"""
1410
1415
 
1416
+ if self.recommended_context:
1417
+ context_items = "\n ".join(
1418
+ f"<li>{file_path}</li>" for file_path in self.recommended_context
1419
+ )
1420
+ handoff_section += f"""
1421
+ <div data-recommended-context>
1422
+ <strong>Recommended Context:</strong>
1423
+ <ul>
1424
+ {context_items}
1425
+ </ul>
1426
+ </div>"""
1427
+
1411
1428
  handoff_section += "\n </section>"
1412
1429
  handoff_html = handoff_section
1413
1430
 
@@ -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
@@ -9,6 +9,7 @@ from .model_selection import (
9
9
  get_fallback_chain,
10
10
  select_model,
11
11
  )
12
+ from .spawner_event_tracker import SpawnerEventTracker, create_tracker_from_env
12
13
 
13
14
  # Export modular spawners for advanced usage
14
15
  from .spawners import (
@@ -37,6 +38,9 @@ __all__ = [
37
38
  "CodexSpawner",
38
39
  "CopilotSpawner",
39
40
  "ClaudeSpawner",
41
+ # Spawner event tracking
42
+ "SpawnerEventTracker",
43
+ "create_tracker_from_env",
40
44
  # Model selection
41
45
  "ModelSelection",
42
46
  "TaskType",
@@ -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
 
@@ -22,7 +25,7 @@ class PluginManager:
22
25
  """Get the plugin directory path.
23
26
 
24
27
  Returns:
25
- Path to packages/claude-plugin/.claude-plugin
28
+ Path to packages/claude-plugin (the plugin root, not .claude-plugin)
26
29
  """
27
30
  # Path(__file__) = .../src/python/htmlgraph/orchestration/plugin_manager.py
28
31
  # Need to go up 5 levels to reach project root
@@ -30,7 +33,6 @@ class PluginManager:
30
33
  Path(__file__).parent.parent.parent.parent.parent
31
34
  / "packages"
32
35
  / "claude-plugin"
33
- / ".claude-plugin"
34
36
  )
35
37
 
36
38
  @staticmethod
@@ -41,12 +43,12 @@ class PluginManager:
41
43
  verbose: Whether to show progress messages
42
44
  """
43
45
  if verbose:
44
- print("\nšŸ“¦ Installing/upgrading HtmlGraph plugin...\n")
46
+ logger.info("\nšŸ“¦ Installing/upgrading HtmlGraph plugin...\n")
45
47
 
46
48
  # Step 1: Update marketplace
47
49
  try:
48
50
  if verbose:
49
- print(" Updating marketplace...")
51
+ logger.info(" Updating marketplace...")
50
52
  result = subprocess.run(
51
53
  ["claude", "plugin", "marketplace", "update", "htmlgraph"],
52
54
  capture_output=True,
@@ -55,7 +57,7 @@ class PluginManager:
55
57
  )
56
58
  if result.returncode == 0:
57
59
  if verbose:
58
- print(" āœ“ Marketplace updated")
60
+ logger.info(" āœ“ Marketplace updated")
59
61
  else:
60
62
  # Non-blocking errors
61
63
  if (
@@ -63,20 +65,20 @@ class PluginManager:
63
65
  or "no marketplace" in result.stderr.lower()
64
66
  ):
65
67
  if verbose:
66
- print(" ℹ Marketplace not configured (OK, continuing)")
68
+ logger.info(" ℹ Marketplace not configured (OK, continuing)")
67
69
  elif verbose:
68
- print(f" ⚠ Marketplace update: {result.stderr.strip()}")
70
+ logger.info(f" ⚠ Marketplace update: {result.stderr.strip()}")
69
71
  except FileNotFoundError:
70
72
  if verbose:
71
- print(" ⚠ 'claude' command not found")
73
+ logger.info(" ⚠ 'claude' command not found")
72
74
  except Exception as e:
73
75
  if verbose:
74
- print(f" ⚠ Error updating marketplace: {e}")
76
+ logger.info(f" ⚠ Error updating marketplace: {e}")
75
77
 
76
78
  # Step 2: Try update, fallback to install
77
79
  try:
78
80
  if verbose:
79
- print(" Updating plugin to latest version...")
81
+ logger.info(" Updating plugin to latest version...")
80
82
  result = subprocess.run(
81
83
  ["claude", "plugin", "update", "htmlgraph"],
82
84
  capture_output=True,
@@ -85,7 +87,7 @@ class PluginManager:
85
87
  )
86
88
  if result.returncode == 0:
87
89
  if verbose:
88
- print(" āœ“ Plugin updated successfully")
90
+ logger.info(" āœ“ Plugin updated successfully")
89
91
  else:
90
92
  # Fallback to install
91
93
  if (
@@ -93,7 +95,7 @@ class PluginManager:
93
95
  or "not found" in result.stderr.lower()
94
96
  ):
95
97
  if verbose:
96
- print(" ℹ Plugin not yet installed, installing...")
98
+ logger.info(" ℹ Plugin not yet installed, installing...")
97
99
  install_result = subprocess.run(
98
100
  ["claude", "plugin", "install", "htmlgraph"],
99
101
  capture_output=True,
@@ -102,20 +104,22 @@ class PluginManager:
102
104
  )
103
105
  if install_result.returncode == 0:
104
106
  if verbose:
105
- print(" āœ“ Plugin installed successfully")
107
+ logger.info(" āœ“ Plugin installed successfully")
106
108
  elif verbose:
107
- print(f" ⚠ Plugin install: {install_result.stderr.strip()}")
109
+ logger.info(
110
+ f" ⚠ Plugin install: {install_result.stderr.strip()}"
111
+ )
108
112
  elif verbose:
109
- print(f" ⚠ Plugin update: {result.stderr.strip()}")
113
+ logger.info(f" ⚠ Plugin update: {result.stderr.strip()}")
110
114
  except FileNotFoundError:
111
115
  if verbose:
112
- print(" ⚠ 'claude' command not found")
116
+ logger.info(" ⚠ 'claude' command not found")
113
117
  except Exception as e:
114
118
  if verbose:
115
- print(f" ⚠ Error updating plugin: {e}")
119
+ logger.info(f" ⚠ Error updating plugin: {e}")
116
120
 
117
121
  if verbose:
118
- print("\nāœ“ Plugin installation complete\n")
122
+ logger.info("\nāœ“ Plugin installation complete\n")
119
123
 
120
124
  @staticmethod
121
125
  def validate_plugin_dir(plugin_dir: Path) -> None:
@@ -128,7 +132,7 @@ class PluginManager:
128
132
  SystemExit: If plugin directory doesn't exist
129
133
  """
130
134
  if not plugin_dir.exists():
131
- print(f"Error: Plugin directory not found: {plugin_dir}", file=sys.stderr)
135
+ logger.warning(f"Error: Plugin directory not found: {plugin_dir}")
132
136
  print(
133
137
  "Expected location: packages/claude-plugin/.claude-plugin",
134
138
  file=sys.stderr,