htmlgraph 0.9.3__py3-none-any.whl → 0.27.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +192 -100
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +190 -14
- htmlgraph/analytics_index.py +135 -51
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +45 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/orchestration/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- 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/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Plugin management for HtmlGraph Claude Code integration.
|
|
4
|
+
|
|
5
|
+
Centralizes plugin installation, directory management, and validation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PluginManager:
|
|
21
|
+
"""Manage HtmlGraph Claude plugin installation and directories."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_plugin_dir() -> Path:
|
|
25
|
+
"""Get the plugin directory path.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Path to packages/claude-plugin (the plugin root, not .claude-plugin)
|
|
29
|
+
"""
|
|
30
|
+
# Path(__file__) = .../src/python/htmlgraph/orchestration/plugin_manager.py
|
|
31
|
+
# Need to go up 5 levels to reach project root
|
|
32
|
+
return (
|
|
33
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
34
|
+
/ "packages"
|
|
35
|
+
/ "claude-plugin"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def install_or_update(verbose: bool = True) -> None:
|
|
40
|
+
"""Install or update HtmlGraph plugin.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
verbose: Whether to show progress messages
|
|
44
|
+
"""
|
|
45
|
+
if verbose:
|
|
46
|
+
logger.info("\n📦 Installing/upgrading HtmlGraph plugin...\n")
|
|
47
|
+
|
|
48
|
+
# Step 1: Update marketplace
|
|
49
|
+
try:
|
|
50
|
+
if verbose:
|
|
51
|
+
logger.info(" Updating marketplace...")
|
|
52
|
+
result = subprocess.run(
|
|
53
|
+
["claude", "plugin", "marketplace", "update", "htmlgraph"],
|
|
54
|
+
capture_output=True,
|
|
55
|
+
text=True,
|
|
56
|
+
check=False,
|
|
57
|
+
)
|
|
58
|
+
if result.returncode == 0:
|
|
59
|
+
if verbose:
|
|
60
|
+
logger.info(" ✓ Marketplace updated")
|
|
61
|
+
else:
|
|
62
|
+
# Non-blocking errors
|
|
63
|
+
if (
|
|
64
|
+
"not found" in result.stderr.lower()
|
|
65
|
+
or "no marketplace" in result.stderr.lower()
|
|
66
|
+
):
|
|
67
|
+
if verbose:
|
|
68
|
+
logger.info(" ℹ Marketplace not configured (OK, continuing)")
|
|
69
|
+
elif verbose:
|
|
70
|
+
logger.info(f" ⚠ Marketplace update: {result.stderr.strip()}")
|
|
71
|
+
except FileNotFoundError:
|
|
72
|
+
if verbose:
|
|
73
|
+
logger.info(" ⚠ 'claude' command not found")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
if verbose:
|
|
76
|
+
logger.info(f" ⚠ Error updating marketplace: {e}")
|
|
77
|
+
|
|
78
|
+
# Step 2: Try update, fallback to install
|
|
79
|
+
try:
|
|
80
|
+
if verbose:
|
|
81
|
+
logger.info(" Updating plugin to latest version...")
|
|
82
|
+
result = subprocess.run(
|
|
83
|
+
["claude", "plugin", "update", "htmlgraph"],
|
|
84
|
+
capture_output=True,
|
|
85
|
+
text=True,
|
|
86
|
+
check=False,
|
|
87
|
+
)
|
|
88
|
+
if result.returncode == 0:
|
|
89
|
+
if verbose:
|
|
90
|
+
logger.info(" ✓ Plugin updated successfully")
|
|
91
|
+
else:
|
|
92
|
+
# Fallback to install
|
|
93
|
+
if (
|
|
94
|
+
"not installed" in result.stderr.lower()
|
|
95
|
+
or "not found" in result.stderr.lower()
|
|
96
|
+
):
|
|
97
|
+
if verbose:
|
|
98
|
+
logger.info(" ℹ Plugin not yet installed, installing...")
|
|
99
|
+
install_result = subprocess.run(
|
|
100
|
+
["claude", "plugin", "install", "htmlgraph"],
|
|
101
|
+
capture_output=True,
|
|
102
|
+
text=True,
|
|
103
|
+
check=False,
|
|
104
|
+
)
|
|
105
|
+
if install_result.returncode == 0:
|
|
106
|
+
if verbose:
|
|
107
|
+
logger.info(" ✓ Plugin installed successfully")
|
|
108
|
+
elif verbose:
|
|
109
|
+
logger.info(
|
|
110
|
+
f" ⚠ Plugin install: {install_result.stderr.strip()}"
|
|
111
|
+
)
|
|
112
|
+
elif verbose:
|
|
113
|
+
logger.info(f" ⚠ Plugin update: {result.stderr.strip()}")
|
|
114
|
+
except FileNotFoundError:
|
|
115
|
+
if verbose:
|
|
116
|
+
logger.info(" ⚠ 'claude' command not found")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
if verbose:
|
|
119
|
+
logger.info(f" ⚠ Error updating plugin: {e}")
|
|
120
|
+
|
|
121
|
+
if verbose:
|
|
122
|
+
logger.info("\n✓ Plugin installation complete\n")
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def validate_plugin_dir(plugin_dir: Path) -> None:
|
|
126
|
+
"""Validate that plugin directory exists, exit if not.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
plugin_dir: Path to plugin directory
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
SystemExit: If plugin directory doesn't exist
|
|
133
|
+
"""
|
|
134
|
+
if not plugin_dir.exists():
|
|
135
|
+
logger.warning(f"Error: Plugin directory not found: {plugin_dir}")
|
|
136
|
+
print(
|
|
137
|
+
"Expected location: packages/claude-plugin/.claude-plugin",
|
|
138
|
+
file=sys.stderr,
|
|
139
|
+
)
|
|
140
|
+
sys.exit(1)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Orchestrator system prompt loading and management.
|
|
3
|
+
|
|
4
|
+
Centralizes logic for loading and combining orchestrator system prompts,
|
|
5
|
+
keeping CLI code clean and focused on invocation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import textwrap
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_orchestrator_prompt(include_dev_mode: bool = False) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Load and combine orchestrator system prompts.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
include_dev_mode: If True, append development mode guidance
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Combined system prompt text ready for --append-system-prompt
|
|
21
|
+
"""
|
|
22
|
+
package_dir = Path(__file__).parent.parent
|
|
23
|
+
|
|
24
|
+
# Load base orchestrator prompt
|
|
25
|
+
prompt_file = package_dir / "orchestrator-system-prompt-optimized.txt"
|
|
26
|
+
if prompt_file.exists():
|
|
27
|
+
base_prompt = prompt_file.read_text(encoding="utf-8")
|
|
28
|
+
else:
|
|
29
|
+
# Fallback: minimal orchestrator guidance
|
|
30
|
+
base_prompt = textwrap.dedent(
|
|
31
|
+
"""
|
|
32
|
+
You are an AI orchestrator for HtmlGraph project development.
|
|
33
|
+
|
|
34
|
+
CRITICAL DIRECTIVES:
|
|
35
|
+
1. DELEGATE to spawner skills - do not implement directly
|
|
36
|
+
2. CREATE work items before delegating (features, bugs, spikes)
|
|
37
|
+
3. USE SDK for tracking - all work must be tracked in .htmlgraph/
|
|
38
|
+
4. RESPECT dependencies - check blockers before starting
|
|
39
|
+
|
|
40
|
+
Key Rules:
|
|
41
|
+
- Exploration/Research → Skill(skill=".claude-plugin:gemini")
|
|
42
|
+
- Code implementation → Skill(skill=".claude-plugin:codex")
|
|
43
|
+
- Git/GitHub ops → Skill(skill=".claude-plugin:copilot")
|
|
44
|
+
- Strategic planning → Task() with Claude subagent
|
|
45
|
+
|
|
46
|
+
Always use:
|
|
47
|
+
from htmlgraph import SDK
|
|
48
|
+
sdk = SDK(agent='orchestrator')
|
|
49
|
+
|
|
50
|
+
See CLAUDE.md for complete orchestrator directives.
|
|
51
|
+
"""
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Load orchestration rules
|
|
55
|
+
rules_file = package_dir / "orchestration.md"
|
|
56
|
+
orchestration_rules = ""
|
|
57
|
+
if rules_file.exists():
|
|
58
|
+
orchestration_rules = rules_file.read_text(encoding="utf-8")
|
|
59
|
+
|
|
60
|
+
# Combine prompts
|
|
61
|
+
combined_prompt = base_prompt
|
|
62
|
+
if orchestration_rules:
|
|
63
|
+
combined_prompt = f"{base_prompt}\n\n---\n\n{orchestration_rules}"
|
|
64
|
+
|
|
65
|
+
# Add dev mode guidance if requested
|
|
66
|
+
if include_dev_mode:
|
|
67
|
+
dev_addendum = textwrap.dedent(
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
═══════════════════════════════════════════════════════════
|
|
71
|
+
🔧 DEVELOPMENT MODE - HtmlGraph Project
|
|
72
|
+
═══════════════════════════════════════════════════════════
|
|
73
|
+
|
|
74
|
+
CRITICAL: Hooks load htmlgraph from PyPI, NOT local source!
|
|
75
|
+
|
|
76
|
+
Development Workflow:
|
|
77
|
+
1. Make changes to src/python/htmlgraph/
|
|
78
|
+
2. Run tests: uv run pytest
|
|
79
|
+
3. Deploy to PyPI: ./scripts/deploy-all.sh X.Y.Z --no-confirm
|
|
80
|
+
4. Restart Claude Code (hooks auto-load new version from PyPI)
|
|
81
|
+
5. Verify changes work correctly
|
|
82
|
+
|
|
83
|
+
Why PyPI in Dev Mode?
|
|
84
|
+
- Hooks use: #!/usr/bin/env -S uv run --with htmlgraph
|
|
85
|
+
- Always pulls latest version from PyPI
|
|
86
|
+
- Tests in production-like environment
|
|
87
|
+
- No surprises when distributed to users
|
|
88
|
+
- Single source of truth (PyPI package)
|
|
89
|
+
|
|
90
|
+
Incremental Versioning:
|
|
91
|
+
- Use patch versions: 0.26.2 → 0.26.3 → 0.26.4
|
|
92
|
+
- No need to edit hook shebangs (always get latest)
|
|
93
|
+
- Deploy frequently for rapid iteration
|
|
94
|
+
|
|
95
|
+
Session ID Tracking (v0.26.3+):
|
|
96
|
+
- PostToolUse hooks query for most recent UserQuery session
|
|
97
|
+
- All events should share same session_id
|
|
98
|
+
- Verify: See "Development Mode" section in CLAUDE.md
|
|
99
|
+
|
|
100
|
+
Key References:
|
|
101
|
+
- Development workflow: CLAUDE.md "Development Mode" section
|
|
102
|
+
- Orchestrator patterns: /orchestrator-directives skill
|
|
103
|
+
- Code quality: /code-quality skill
|
|
104
|
+
- Deployment: /deployment-automation skill
|
|
105
|
+
|
|
106
|
+
Remember: You're dogfooding HtmlGraph!
|
|
107
|
+
- Use SDK to track your own work
|
|
108
|
+
- Delegate to spawner agents (Gemini, Codex, Copilot)
|
|
109
|
+
- Follow orchestration patterns
|
|
110
|
+
- Test in production-like environment
|
|
111
|
+
|
|
112
|
+
═══════════════════════════════════════════════════════════
|
|
113
|
+
"""
|
|
114
|
+
)
|
|
115
|
+
combined_prompt += dev_addendum
|
|
116
|
+
|
|
117
|
+
return combined_prompt
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_prompt_summary() -> dict[str, str]:
|
|
121
|
+
"""
|
|
122
|
+
Get summary of available prompt components.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Dictionary with component names and their status
|
|
126
|
+
"""
|
|
127
|
+
package_dir = Path(__file__).parent.parent
|
|
128
|
+
|
|
129
|
+
prompt_file = package_dir / "orchestrator-system-prompt-optimized.txt"
|
|
130
|
+
rules_file = package_dir / "orchestration.md"
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"base_prompt": "✓ Found" if prompt_file.exists() else "✗ Missing",
|
|
134
|
+
"orchestration_rules": "✓ Found" if rules_file.exists() else "✗ Missing",
|
|
135
|
+
"base_prompt_path": str(prompt_file),
|
|
136
|
+
"rules_path": str(rules_file),
|
|
137
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""Spawner event tracking helper for internal activity tracking in spawned sessions.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for tracking internal activities within spawner agents
|
|
4
|
+
and linking them to parent delegation events for observability in HtmlGraph.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from htmlgraph.orchestration.spawner_event_tracker import SpawnerEventTracker
|
|
8
|
+
|
|
9
|
+
tracker = SpawnerEventTracker(
|
|
10
|
+
delegation_event_id="event-abc123",
|
|
11
|
+
parent_agent="orchestrator",
|
|
12
|
+
spawner_type="gemini"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Track initialization phase
|
|
16
|
+
init_event = tracker.record_phase("Initializing Spawner", spawned_agent="gemini-2.0-flash")
|
|
17
|
+
|
|
18
|
+
# Track execution phase
|
|
19
|
+
exec_event = tracker.record_phase("Executing Gemini", tool_name="gemini-cli")
|
|
20
|
+
tracker.complete_phase(exec_event["event_id"], output_summary="Generated output...")
|
|
21
|
+
|
|
22
|
+
# Track completion
|
|
23
|
+
tracker.record_completion(success=True, response="Result here")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import time
|
|
28
|
+
import uuid
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SpawnerEventTracker:
|
|
33
|
+
"""Track internal activities in spawner agents with parent-child linking."""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
delegation_event_id: str | None = None,
|
|
38
|
+
parent_agent: str = "orchestrator",
|
|
39
|
+
spawner_type: str = "generic",
|
|
40
|
+
session_id: str | None = None,
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Initialize spawner event tracker.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
delegation_event_id: Parent delegation event ID to link to
|
|
47
|
+
parent_agent: Agent that initiated the spawning
|
|
48
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
49
|
+
session_id: Session ID for events
|
|
50
|
+
"""
|
|
51
|
+
self.delegation_event_id = delegation_event_id
|
|
52
|
+
self.parent_agent = parent_agent
|
|
53
|
+
self.spawner_type = spawner_type
|
|
54
|
+
self.session_id = session_id or f"session-{uuid.uuid4().hex[:8]}"
|
|
55
|
+
self.db = None
|
|
56
|
+
self.phase_events: dict[str, dict[str, Any]] = {}
|
|
57
|
+
self.start_time = time.time()
|
|
58
|
+
|
|
59
|
+
# Try to initialize database for event tracking
|
|
60
|
+
try:
|
|
61
|
+
from htmlgraph.config import get_database_path
|
|
62
|
+
from htmlgraph.db.schema import HtmlGraphDB
|
|
63
|
+
|
|
64
|
+
# Get correct database path from environment or project root
|
|
65
|
+
db_path = get_database_path()
|
|
66
|
+
|
|
67
|
+
if db_path.exists():
|
|
68
|
+
self.db = HtmlGraphDB(str(db_path))
|
|
69
|
+
except Exception:
|
|
70
|
+
# Tracking is optional, continue without it
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
def record_phase(
|
|
74
|
+
self,
|
|
75
|
+
phase_name: str,
|
|
76
|
+
spawned_agent: str | None = None,
|
|
77
|
+
tool_name: str | None = None,
|
|
78
|
+
input_summary: str | None = None,
|
|
79
|
+
status: str = "running",
|
|
80
|
+
parent_phase_event_id: str | None = None,
|
|
81
|
+
) -> dict[str, Any]:
|
|
82
|
+
"""
|
|
83
|
+
Record the start of a phase in spawner execution.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
phase_name: Human-readable phase name (e.g., "Initializing Spawner")
|
|
87
|
+
spawned_agent: Agent being spawned (optional)
|
|
88
|
+
tool_name: Tool being executed (optional)
|
|
89
|
+
input_summary: Summary of input (optional)
|
|
90
|
+
status: Current status (running, completed, failed)
|
|
91
|
+
parent_phase_event_id: Parent phase event ID for proper nesting (optional)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Event dictionary with event_id and metadata
|
|
95
|
+
"""
|
|
96
|
+
if not self.db:
|
|
97
|
+
return {}
|
|
98
|
+
|
|
99
|
+
event_id = f"event-{uuid.uuid4().hex[:8]}"
|
|
100
|
+
event_type = "tool_call"
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
context = {
|
|
104
|
+
"phase_name": phase_name,
|
|
105
|
+
"spawner_type": self.spawner_type,
|
|
106
|
+
"parent_delegation_event": self.delegation_event_id,
|
|
107
|
+
}
|
|
108
|
+
if spawned_agent:
|
|
109
|
+
context["spawned_agent"] = spawned_agent
|
|
110
|
+
if tool_name:
|
|
111
|
+
context["tool"] = tool_name
|
|
112
|
+
|
|
113
|
+
# Use parent_phase_event_id if provided, otherwise use delegation_event_id
|
|
114
|
+
actual_parent_event_id = parent_phase_event_id or self.delegation_event_id
|
|
115
|
+
|
|
116
|
+
self.db.insert_event(
|
|
117
|
+
event_id=event_id,
|
|
118
|
+
agent_id=spawned_agent or self.parent_agent,
|
|
119
|
+
event_type=event_type,
|
|
120
|
+
session_id=self.session_id,
|
|
121
|
+
tool_name=tool_name
|
|
122
|
+
or f"HeadlessSpawner.{phase_name.replace(' ', '_').lower()}",
|
|
123
|
+
input_summary=input_summary or phase_name,
|
|
124
|
+
context=context,
|
|
125
|
+
parent_event_id=actual_parent_event_id,
|
|
126
|
+
subagent_type=self.spawner_type,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
event = {
|
|
130
|
+
"event_id": event_id,
|
|
131
|
+
"phase_name": phase_name,
|
|
132
|
+
"spawned_agent": spawned_agent,
|
|
133
|
+
"tool_name": tool_name,
|
|
134
|
+
"status": status,
|
|
135
|
+
"start_time": time.time(),
|
|
136
|
+
}
|
|
137
|
+
self.phase_events[event_id] = event
|
|
138
|
+
return event
|
|
139
|
+
|
|
140
|
+
except Exception:
|
|
141
|
+
# Non-fatal - tracking is best-effort
|
|
142
|
+
return {}
|
|
143
|
+
|
|
144
|
+
def complete_phase(
|
|
145
|
+
self,
|
|
146
|
+
event_id: str,
|
|
147
|
+
output_summary: str | None = None,
|
|
148
|
+
status: str = "completed",
|
|
149
|
+
execution_duration: float | None = None,
|
|
150
|
+
) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Mark a phase as completed with results.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
event_id: Event ID from record_phase
|
|
156
|
+
output_summary: Summary of output/result
|
|
157
|
+
status: Final status (completed, failed)
|
|
158
|
+
execution_duration: Execution time in seconds (auto-calculated if not provided)
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
True if update successful, False otherwise
|
|
162
|
+
"""
|
|
163
|
+
if not self.db or not event_id:
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
if execution_duration is None and event_id in self.phase_events:
|
|
168
|
+
execution_duration = (
|
|
169
|
+
time.time() - self.phase_events[event_id]["start_time"]
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if self.db.connection is None:
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
cursor = self.db.connection.cursor()
|
|
176
|
+
cursor.execute(
|
|
177
|
+
"""
|
|
178
|
+
UPDATE agent_events
|
|
179
|
+
SET output_summary = ?, status = ?, execution_duration_seconds = ?,
|
|
180
|
+
updated_at = CURRENT_TIMESTAMP
|
|
181
|
+
WHERE event_id = ?
|
|
182
|
+
""",
|
|
183
|
+
(output_summary, status, execution_duration or 0.0, event_id),
|
|
184
|
+
)
|
|
185
|
+
self.db.connection.commit()
|
|
186
|
+
|
|
187
|
+
if event_id in self.phase_events:
|
|
188
|
+
self.phase_events[event_id]["status"] = status
|
|
189
|
+
self.phase_events[event_id]["output_summary"] = output_summary
|
|
190
|
+
|
|
191
|
+
return True
|
|
192
|
+
except Exception:
|
|
193
|
+
# Non-fatal
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
def record_completion(
|
|
197
|
+
self,
|
|
198
|
+
success: bool,
|
|
199
|
+
response: str | None = None,
|
|
200
|
+
error: str | None = None,
|
|
201
|
+
tokens_used: int = 0,
|
|
202
|
+
cost: float = 0.0,
|
|
203
|
+
) -> dict[str, Any]:
|
|
204
|
+
"""
|
|
205
|
+
Record final completion with overall results.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
success: Whether execution succeeded
|
|
209
|
+
response: Successful response/output
|
|
210
|
+
error: Error message if failed
|
|
211
|
+
tokens_used: Tokens consumed
|
|
212
|
+
cost: Execution cost
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Completion event dictionary
|
|
216
|
+
"""
|
|
217
|
+
total_duration = time.time() - self.start_time
|
|
218
|
+
|
|
219
|
+
completion_event: dict[str, Any] = {
|
|
220
|
+
"success": success,
|
|
221
|
+
"duration": total_duration,
|
|
222
|
+
"tokens": tokens_used,
|
|
223
|
+
"cost": cost,
|
|
224
|
+
"phase_count": len(self.phase_events),
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if success:
|
|
228
|
+
completion_event["response"] = response
|
|
229
|
+
else:
|
|
230
|
+
completion_event["error"] = error
|
|
231
|
+
|
|
232
|
+
return completion_event
|
|
233
|
+
|
|
234
|
+
def get_phase_events(self) -> dict[str, dict[str, Any]]:
|
|
235
|
+
"""Get all recorded phase events."""
|
|
236
|
+
return self.phase_events
|
|
237
|
+
|
|
238
|
+
def record_tool_call(
|
|
239
|
+
self,
|
|
240
|
+
tool_name: str,
|
|
241
|
+
tool_input: dict | None,
|
|
242
|
+
phase_event_id: str,
|
|
243
|
+
spawned_agent: str | None = None,
|
|
244
|
+
) -> dict[str, Any]:
|
|
245
|
+
"""
|
|
246
|
+
Record a tool call within a spawned execution phase.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
tool_name: Name of the tool (bash, read_file, write_file, etc.)
|
|
250
|
+
tool_input: Input parameters to the tool
|
|
251
|
+
phase_event_id: Parent phase event ID to link to
|
|
252
|
+
spawned_agent: Agent making the tool call (optional)
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Event dictionary with event_id and metadata
|
|
256
|
+
"""
|
|
257
|
+
if not self.db:
|
|
258
|
+
return {}
|
|
259
|
+
|
|
260
|
+
event_id = f"event-{uuid.uuid4().hex[:8]}"
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
context = {
|
|
264
|
+
"tool_name": tool_name,
|
|
265
|
+
"spawner_type": self.spawner_type,
|
|
266
|
+
"parent_phase_event": phase_event_id,
|
|
267
|
+
}
|
|
268
|
+
if spawned_agent:
|
|
269
|
+
context["spawned_agent"] = spawned_agent
|
|
270
|
+
|
|
271
|
+
self.db.insert_event(
|
|
272
|
+
event_id=event_id,
|
|
273
|
+
agent_id=spawned_agent or self.parent_agent,
|
|
274
|
+
event_type="tool_call",
|
|
275
|
+
session_id=self.session_id,
|
|
276
|
+
tool_name=tool_name,
|
|
277
|
+
input_summary=(
|
|
278
|
+
str(tool_input)[:200] if tool_input else f"Call to {tool_name}"
|
|
279
|
+
),
|
|
280
|
+
context=context,
|
|
281
|
+
parent_event_id=phase_event_id,
|
|
282
|
+
subagent_type=self.spawner_type,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
tool_event = {
|
|
286
|
+
"event_id": event_id,
|
|
287
|
+
"tool_name": tool_name,
|
|
288
|
+
"tool_input": tool_input,
|
|
289
|
+
"phase_event_id": phase_event_id,
|
|
290
|
+
"spawned_agent": spawned_agent,
|
|
291
|
+
"status": "running",
|
|
292
|
+
"start_time": time.time(),
|
|
293
|
+
}
|
|
294
|
+
return tool_event
|
|
295
|
+
|
|
296
|
+
except Exception:
|
|
297
|
+
# Non-fatal - tracking is best-effort
|
|
298
|
+
return {}
|
|
299
|
+
|
|
300
|
+
def complete_tool_call(
|
|
301
|
+
self,
|
|
302
|
+
event_id: str,
|
|
303
|
+
output_summary: str | None = None,
|
|
304
|
+
success: bool = True,
|
|
305
|
+
) -> bool:
|
|
306
|
+
"""
|
|
307
|
+
Mark a tool call as complete with results.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
event_id: Event ID from record_tool_call
|
|
311
|
+
output_summary: Summary of tool output/result
|
|
312
|
+
success: Whether the tool call succeeded
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
True if update successful, False otherwise
|
|
316
|
+
"""
|
|
317
|
+
if not self.db or not event_id:
|
|
318
|
+
return False
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
if self.db.connection is None:
|
|
322
|
+
return False
|
|
323
|
+
|
|
324
|
+
cursor = self.db.connection.cursor()
|
|
325
|
+
cursor.execute(
|
|
326
|
+
"""
|
|
327
|
+
UPDATE agent_events
|
|
328
|
+
SET output_summary = ?, status = ?, updated_at = CURRENT_TIMESTAMP
|
|
329
|
+
WHERE event_id = ?
|
|
330
|
+
""",
|
|
331
|
+
(
|
|
332
|
+
output_summary,
|
|
333
|
+
"completed" if success else "failed",
|
|
334
|
+
event_id,
|
|
335
|
+
),
|
|
336
|
+
)
|
|
337
|
+
self.db.connection.commit()
|
|
338
|
+
return True
|
|
339
|
+
except Exception:
|
|
340
|
+
# Non-fatal
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
def get_event_hierarchy(self) -> dict[str, Any]:
|
|
344
|
+
"""
|
|
345
|
+
Get the event hierarchy for this spawner execution.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Dictionary with delegation event as root and phases as children
|
|
349
|
+
"""
|
|
350
|
+
return {
|
|
351
|
+
"delegation_event_id": self.delegation_event_id,
|
|
352
|
+
"spawner_type": self.spawner_type,
|
|
353
|
+
"session_id": self.session_id,
|
|
354
|
+
"total_duration": time.time() - self.start_time,
|
|
355
|
+
"phase_events": self.phase_events,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def create_tracker_from_env(
|
|
360
|
+
spawner_type: str = "generic",
|
|
361
|
+
) -> SpawnerEventTracker:
|
|
362
|
+
"""
|
|
363
|
+
Create a SpawnerEventTracker from environment variables.
|
|
364
|
+
|
|
365
|
+
Reads HTMLGRAPH_PARENT_EVENT, HTMLGRAPH_PARENT_AGENT, HTMLGRAPH_PARENT_SESSION
|
|
366
|
+
from environment to link to parent context.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
spawner_type: Type of spawner (gemini, codex, copilot)
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Initialized SpawnerEventTracker
|
|
373
|
+
"""
|
|
374
|
+
delegation_event_id = os.getenv("HTMLGRAPH_PARENT_EVENT")
|
|
375
|
+
parent_agent = os.getenv("HTMLGRAPH_PARENT_AGENT", "orchestrator")
|
|
376
|
+
session_id = os.getenv("HTMLGRAPH_PARENT_SESSION")
|
|
377
|
+
|
|
378
|
+
return SpawnerEventTracker(
|
|
379
|
+
delegation_event_id=delegation_event_id,
|
|
380
|
+
parent_agent=parent_agent,
|
|
381
|
+
spawner_type=spawner_type,
|
|
382
|
+
session_id=session_id,
|
|
383
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""AI spawner implementations for multi-AI orchestration."""
|
|
2
|
+
|
|
3
|
+
from .base import AIResult, BaseSpawner
|
|
4
|
+
from .claude import ClaudeSpawner
|
|
5
|
+
from .codex import CodexSpawner
|
|
6
|
+
from .copilot import CopilotSpawner
|
|
7
|
+
from .gemini import GeminiSpawner
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AIResult",
|
|
11
|
+
"BaseSpawner",
|
|
12
|
+
"ClaudeSpawner",
|
|
13
|
+
"CodexSpawner",
|
|
14
|
+
"CopilotSpawner",
|
|
15
|
+
"GeminiSpawner",
|
|
16
|
+
]
|