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.
- htmlgraph/__init__.py +23 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/cli.py +3 -3
- htmlgraph/analytics/cost_analyzer.py +5 -1
- htmlgraph/analytics/cross_session.py +13 -9
- htmlgraph/analytics/dependency.py +10 -6
- htmlgraph/analytics/work_type.py +15 -11
- htmlgraph/analytics_index.py +2 -1
- htmlgraph/api/main.py +58 -28
- htmlgraph/attribute_index.py +2 -1
- htmlgraph/builders/base.py +2 -1
- htmlgraph/builders/bug.py +2 -1
- htmlgraph/builders/chore.py +2 -1
- htmlgraph/builders/epic.py +2 -1
- htmlgraph/builders/feature.py +2 -1
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +2 -1
- htmlgraph/builders/spike.py +2 -1
- htmlgraph/builders/track.py +2 -1
- htmlgraph/cli/analytics.py +2 -1
- htmlgraph/cli/base.py +2 -1
- htmlgraph/cli/core.py +2 -1
- htmlgraph/cli/main.py +2 -1
- htmlgraph/cli/models.py +2 -1
- htmlgraph/cli/templates/cost_dashboard.py +2 -1
- htmlgraph/cli/work/__init__.py +2 -1
- htmlgraph/cli/work/browse.py +2 -1
- htmlgraph/cli/work/features.py +2 -1
- htmlgraph/cli/work/orchestration.py +2 -1
- htmlgraph/cli/work/report.py +2 -1
- htmlgraph/cli/work/sessions.py +2 -1
- htmlgraph/cli/work/snapshot.py +2 -1
- htmlgraph/cli/work/tracks.py +2 -1
- htmlgraph/collections/base.py +10 -5
- htmlgraph/collections/bug.py +2 -1
- htmlgraph/collections/chore.py +2 -1
- htmlgraph/collections/epic.py +2 -1
- htmlgraph/collections/feature.py +2 -1
- htmlgraph/collections/insight.py +2 -1
- htmlgraph/collections/metric.py +2 -1
- htmlgraph/collections/pattern.py +2 -1
- htmlgraph/collections/phase.py +2 -1
- htmlgraph/collections/session.py +12 -7
- htmlgraph/collections/spike.py +6 -1
- htmlgraph/collections/task_delegation.py +7 -2
- htmlgraph/collections/todo.py +2 -1
- htmlgraph/collections/traces.py +15 -10
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/dependency_models.py +2 -1
- htmlgraph/edge_index.py +2 -1
- htmlgraph/event_log.py +81 -66
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +6 -2
- htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
- htmlgraph/hooks/drift_handler.py +3 -3
- htmlgraph/hooks/event_tracker.py +40 -61
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +4 -0
- htmlgraph/hooks/orchestrator_reflector.py +4 -0
- htmlgraph/hooks/post_tool_use_failure.py +7 -3
- htmlgraph/hooks/posttooluse.py +4 -0
- htmlgraph/hooks/prompt_analyzer.py +5 -5
- htmlgraph/hooks/session_handler.py +2 -1
- htmlgraph/hooks/session_summary.py +6 -2
- htmlgraph/hooks/validator.py +8 -4
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +2 -1
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/operations/analytics.py +2 -1
- htmlgraph/operations/bootstrap.py +2 -1
- htmlgraph/operations/events.py +2 -1
- htmlgraph/operations/fastapi_server.py +2 -1
- htmlgraph/operations/hooks.py +2 -1
- htmlgraph/operations/initialization.py +2 -1
- htmlgraph/operations/server.py +2 -1
- htmlgraph/orchestration/claude_launcher.py +23 -20
- htmlgraph/orchestration/command_builder.py +2 -1
- htmlgraph/orchestration/headless_spawner.py +6 -2
- htmlgraph/orchestration/model_selection.py +7 -3
- htmlgraph/orchestration/plugin_manager.py +24 -19
- htmlgraph/orchestration/spawners/claude.py +5 -2
- htmlgraph/orchestration/spawners/codex.py +12 -19
- htmlgraph/orchestration/spawners/copilot.py +13 -18
- htmlgraph/orchestration/spawners/gemini.py +12 -19
- htmlgraph/orchestration/subprocess_runner.py +6 -3
- htmlgraph/orchestration/task_coordination.py +16 -8
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/parallel.py +2 -1
- htmlgraph/query_builder.py +2 -1
- htmlgraph/reflection.py +2 -1
- htmlgraph/refs.py +2 -1
- htmlgraph/repo_hash.py +2 -1
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/server.py +21 -17
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/handoff.py +4 -3
- htmlgraph/system_prompts.py +2 -1
- htmlgraph/track_builder.py +2 -1
- htmlgraph/transcript.py +2 -1
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/METADATA +15 -1
- {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/RECORD +146 -114
- htmlgraph/sdk.py +0 -3500
- {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.26.25.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.0.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
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
|
-
|
|
245
|
+
logger.warning(f"Created debug spike: {spike.id}")
|
|
242
246
|
|
|
243
247
|
except Exception as e:
|
|
244
|
-
|
|
248
|
+
logger.warning(f"Failed to create debug spike: {e}")
|
|
245
249
|
|
|
246
250
|
|
|
247
251
|
def main() -> None:
|
htmlgraph/hooks/posttooluse.py
CHANGED
|
@@ -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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
547
|
+
... logger.info(f"Created event: {event_id}")
|
|
548
548
|
"""
|
|
549
549
|
try:
|
|
550
550
|
session_id = context.session_id
|
|
@@ -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
|
-
|
|
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
|
-
|
|
394
|
+
logger.warning(f"Warning: Could not generate CIGS summary: {e}")
|
|
391
395
|
print(json.dumps({"continue": True}))
|
htmlgraph/hooks/validator.py
CHANGED
|
@@ -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
|
-
|
|
29
|
+
logger.debug("Validation reason: %s", result["reason"])
|
|
26
30
|
elif "guidance" in result:
|
|
27
|
-
|
|
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
|
-
|
|
455
|
+
logger.debug("Validation reason: %s", result["reason"])
|
|
452
456
|
elif "guidance" in result:
|
|
453
|
-
|
|
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
|
"""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
|
htmlgraph/operations/events.py
CHANGED
htmlgraph/operations/hooks.py
CHANGED
|
@@ -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
|
htmlgraph/operations/server.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
164
|
+
logger.info("🤖 HtmlGraph Orchestrator Mode")
|
|
162
165
|
print("=" * 60)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
181
|
+
logger.info("🔧 HtmlGraph Development Mode")
|
|
179
182
|
print("=" * 60)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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,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
|
-
...
|
|
267
|
-
...
|
|
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
|
-
>>>
|
|
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
|
-
>>>
|
|
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
|
-
>>>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
+
logger.info(" ℹ Marketplace not configured (OK, continuing)")
|
|
66
69
|
elif verbose:
|
|
67
|
-
|
|
70
|
+
logger.info(f" ⚠ Marketplace update: {result.stderr.strip()}")
|
|
68
71
|
except FileNotFoundError:
|
|
69
72
|
if verbose:
|
|
70
|
-
|
|
73
|
+
logger.info(" ⚠ 'claude' command not found")
|
|
71
74
|
except Exception as e:
|
|
72
75
|
if verbose:
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
+
logger.info(" ✓ Plugin installed successfully")
|
|
105
108
|
elif verbose:
|
|
106
|
-
|
|
109
|
+
logger.info(
|
|
110
|
+
f" ⚠ Plugin install: {install_result.stderr.strip()}"
|
|
111
|
+
)
|
|
107
112
|
elif verbose:
|
|
108
|
-
|
|
113
|
+
logger.info(f" ⚠ Plugin update: {result.stderr.strip()}")
|
|
109
114
|
except FileNotFoundError:
|
|
110
115
|
if verbose:
|
|
111
|
-
|
|
116
|
+
logger.info(" ⚠ 'claude' command not found")
|
|
112
117
|
except Exception as e:
|
|
113
118
|
if verbose:
|
|
114
|
-
|
|
119
|
+
logger.info(f" ⚠ Error updating plugin: {e}")
|
|
115
120
|
|
|
116
121
|
if verbose:
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
68
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
255
|
+
logger.debug("subprocess_event was None")
|
|
259
256
|
except Exception as e:
|
|
260
257
|
# Tracking failure should not break execution
|
|
261
|
-
|
|
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
|
-
|
|
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(
|