htmlgraph 0.20.1__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 +51 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/__init__.py +8 -1
- htmlgraph/analytics/cli.py +86 -20
- 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 +10 -6
- 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 +67 -27
- htmlgraph/analytics_index.py +53 -20
- 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 +2 -1
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/base.py +57 -2
- htmlgraph/builders/bug.py +19 -3
- htmlgraph/builders/chore.py +19 -3
- htmlgraph/builders/epic.py +19 -3
- htmlgraph/builders/feature.py +27 -3
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +19 -3
- htmlgraph/builders/spike.py +29 -3
- htmlgraph/builders/track.py +42 -1
- 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 +2 -0
- htmlgraph/collections/base.py +197 -14
- 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 +194 -0
- htmlgraph/collections/spike.py +13 -2
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +116 -7
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2246 -248
- 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 +2 -1
- htmlgraph/deploy.py +26 -27
- 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 +2 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +86 -37
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +67 -9
- 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 +8 -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 +790 -99
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +327 -76
- htmlgraph/hooks/orchestrator_reflector.py +31 -4
- htmlgraph/hooks/post_tool_use_failure.py +32 -7
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +92 -19
- htmlgraph/hooks/pretooluse.py +527 -7
- 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 +99 -4
- htmlgraph/hooks/validator.py +212 -91
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +125 -100
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +217 -18
- 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.py → orchestration/task_coordination.py} +16 -8
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +115 -4
- htmlgraph/parallel.py +2 -1
- htmlgraph/parser.py +86 -6
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +2 -1
- 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/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 +295 -107
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +285 -3
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +33 -1
- htmlgraph/track_manager.py +38 -0
- htmlgraph/transcript.py +18 -5
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -4839
- htmlgraph/sdk.py +0 -2359
- htmlgraph-0.20.1.dist-info/RECORD +0 -118
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Claude Code launcher with multiple integration modes.
|
|
4
|
+
|
|
5
|
+
Coordinates launching Claude Code with various HtmlGraph integration options.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import logging
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from htmlgraph.orchestration.command_builder import ClaudeCommandBuilder
|
|
14
|
+
from htmlgraph.orchestration.plugin_manager import PluginManager
|
|
15
|
+
from htmlgraph.orchestration.prompts import get_orchestrator_prompt
|
|
16
|
+
from htmlgraph.orchestration.subprocess_runner import SubprocessRunner
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClaudeLauncher:
|
|
22
|
+
"""Launch Claude Code with various HtmlGraph integration modes.
|
|
23
|
+
|
|
24
|
+
Supports four launch scenarios:
|
|
25
|
+
1. --init: Orchestrator mode with plugin installation
|
|
26
|
+
2. --continue: Resume last session with orchestrator rules
|
|
27
|
+
3. --dev: Development mode with local plugin
|
|
28
|
+
4. default: Minimal orchestrator rules
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, args: argparse.Namespace) -> None:
|
|
32
|
+
"""Initialize launcher with parsed arguments.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
args: Parsed command-line arguments
|
|
36
|
+
"""
|
|
37
|
+
self.args = args
|
|
38
|
+
self.interactive = not (args.quiet or args.format == "json")
|
|
39
|
+
|
|
40
|
+
def launch(self) -> None:
|
|
41
|
+
"""Main entry point - routes to appropriate scenario.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
SystemExit: On error during launch
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
if self.args.init:
|
|
48
|
+
self._launch_orchestrator_mode()
|
|
49
|
+
elif self.args.continue_session:
|
|
50
|
+
self._launch_resume_mode()
|
|
51
|
+
elif self.args.dev:
|
|
52
|
+
self._launch_dev_mode()
|
|
53
|
+
else:
|
|
54
|
+
self._launch_default_mode()
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.warning(f"Error: Failed to start Claude Code: {e}")
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
def _launch_orchestrator_mode(self) -> None:
|
|
60
|
+
"""Launch with orchestrator prompt (--init).
|
|
61
|
+
|
|
62
|
+
Installs plugin, loads orchestrator system prompt, and starts Claude Code
|
|
63
|
+
in orchestrator mode with multi-AI delegation rules.
|
|
64
|
+
"""
|
|
65
|
+
# Install plugin
|
|
66
|
+
PluginManager.install_or_update(verbose=self.interactive)
|
|
67
|
+
|
|
68
|
+
# Load prompt
|
|
69
|
+
prompt = get_orchestrator_prompt(include_dev_mode=False)
|
|
70
|
+
|
|
71
|
+
# Show banner
|
|
72
|
+
if self.interactive:
|
|
73
|
+
self._print_orchestrator_banner()
|
|
74
|
+
|
|
75
|
+
# Build command
|
|
76
|
+
cmd = ClaudeCommandBuilder().with_system_prompt(prompt).build()
|
|
77
|
+
|
|
78
|
+
# Execute
|
|
79
|
+
SubprocessRunner.run_claude_command(cmd)
|
|
80
|
+
|
|
81
|
+
def _launch_resume_mode(self) -> None:
|
|
82
|
+
"""Resume last session with orchestrator rules (--continue).
|
|
83
|
+
|
|
84
|
+
Installs/updates marketplace plugin and resumes the last Claude Code
|
|
85
|
+
session with orchestrator system prompt. Uses marketplace plugin, NOT
|
|
86
|
+
local source (only --dev uses local source).
|
|
87
|
+
"""
|
|
88
|
+
# Install/update marketplace plugin
|
|
89
|
+
PluginManager.install_or_update(verbose=self.interactive)
|
|
90
|
+
|
|
91
|
+
# Load prompt
|
|
92
|
+
prompt = get_orchestrator_prompt(include_dev_mode=False)
|
|
93
|
+
|
|
94
|
+
# Show status
|
|
95
|
+
if self.interactive:
|
|
96
|
+
logger.info("Resuming last Claude Code session...")
|
|
97
|
+
logger.info(" ✓ Multi-AI delegation rules injected")
|
|
98
|
+
logger.info(" ✓ Using marketplace plugin")
|
|
99
|
+
|
|
100
|
+
# Build command (no --plugin-dir, uses marketplace)
|
|
101
|
+
cmd = ClaudeCommandBuilder().with_resume().with_system_prompt(prompt).build()
|
|
102
|
+
|
|
103
|
+
# Execute
|
|
104
|
+
SubprocessRunner.run_claude_command(cmd)
|
|
105
|
+
|
|
106
|
+
def _launch_dev_mode(self) -> None:
|
|
107
|
+
"""Launch with local plugin for development (--dev).
|
|
108
|
+
|
|
109
|
+
Loads plugin from local source directory for development/testing.
|
|
110
|
+
Changes to plugin files take effect after restart.
|
|
111
|
+
"""
|
|
112
|
+
# Get and validate plugin directory
|
|
113
|
+
plugin_dir = PluginManager.get_plugin_dir()
|
|
114
|
+
PluginManager.validate_plugin_dir(plugin_dir)
|
|
115
|
+
|
|
116
|
+
# Load prompt with dev mode
|
|
117
|
+
prompt = get_orchestrator_prompt(include_dev_mode=True)
|
|
118
|
+
|
|
119
|
+
# Show banner
|
|
120
|
+
if self.interactive:
|
|
121
|
+
self._print_dev_mode_banner(plugin_dir)
|
|
122
|
+
|
|
123
|
+
# Build command
|
|
124
|
+
cmd = (
|
|
125
|
+
ClaudeCommandBuilder()
|
|
126
|
+
.with_plugin_dir(str(plugin_dir))
|
|
127
|
+
.with_system_prompt(prompt)
|
|
128
|
+
.build()
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Execute
|
|
132
|
+
SubprocessRunner.run_claude_command(cmd)
|
|
133
|
+
|
|
134
|
+
def _launch_default_mode(self) -> None:
|
|
135
|
+
"""Launch with minimal orchestrator rules (default).
|
|
136
|
+
|
|
137
|
+
Starts Claude Code with basic multi-AI delegation rules but no plugin.
|
|
138
|
+
"""
|
|
139
|
+
# Load prompt
|
|
140
|
+
prompt = get_orchestrator_prompt(include_dev_mode=False)
|
|
141
|
+
|
|
142
|
+
# Show status
|
|
143
|
+
if self.interactive:
|
|
144
|
+
logger.info("Starting Claude Code with multi-AI delegation rules...")
|
|
145
|
+
|
|
146
|
+
# Build command
|
|
147
|
+
cmd = ClaudeCommandBuilder().with_system_prompt(prompt).build()
|
|
148
|
+
|
|
149
|
+
# Execute
|
|
150
|
+
SubprocessRunner.run_claude_command(cmd)
|
|
151
|
+
|
|
152
|
+
def _print_orchestrator_banner(self) -> None:
|
|
153
|
+
"""Print orchestrator mode banner."""
|
|
154
|
+
print("=" * 60)
|
|
155
|
+
logger.info("🤖 HtmlGraph Orchestrator Mode")
|
|
156
|
+
print("=" * 60)
|
|
157
|
+
logger.info("\nStarting Claude Code with orchestrator system prompt...")
|
|
158
|
+
logger.info("Key directives:")
|
|
159
|
+
logger.info(" ✓ Delegate to Gemini (FREE), Codex, Copilot first")
|
|
160
|
+
logger.info(" ✓ Use Task() only as fallback")
|
|
161
|
+
logger.info(" ✓ Create work items before delegating")
|
|
162
|
+
logger.info(" ✓ Track all work in .htmlgraph/")
|
|
163
|
+
print()
|
|
164
|
+
|
|
165
|
+
def _print_dev_mode_banner(self, plugin_dir: Path) -> None:
|
|
166
|
+
"""Print development mode banner.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
plugin_dir: Path to local plugin directory
|
|
170
|
+
"""
|
|
171
|
+
print("=" * 60)
|
|
172
|
+
logger.info("🔧 HtmlGraph Development Mode")
|
|
173
|
+
print("=" * 60)
|
|
174
|
+
logger.info(f"\nLoading plugin from: {plugin_dir}")
|
|
175
|
+
logger.info(" ✓ Skills, agents, and hooks will be loaded from local files")
|
|
176
|
+
logger.info(" ✓ Orchestrator system prompt will be appended")
|
|
177
|
+
logger.info(" ✓ Multi-AI delegation rules will be injected")
|
|
178
|
+
logger.info(" ✓ Changes to plugin files will take effect after restart")
|
|
179
|
+
print()
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Command builder for Claude Code CLI invocations.
|
|
4
|
+
|
|
5
|
+
Provides fluent interface for constructing Claude CLI commands.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ClaudeCommandBuilder:
|
|
16
|
+
"""Fluent builder for Claude Code CLI commands.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> builder = ClaudeCommandBuilder()
|
|
20
|
+
>>> cmd = builder.with_resume() \\
|
|
21
|
+
... .with_plugin_dir("/path/to/plugin") \\
|
|
22
|
+
... .with_system_prompt("System prompt text") \\
|
|
23
|
+
... .build()
|
|
24
|
+
>>> # Result: ["claude", "--resume", "--plugin-dir", "/path/to/plugin",
|
|
25
|
+
... # "--append-system-prompt", "System prompt text"]
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize with base command."""
|
|
30
|
+
self._cmd: list[str] = ["claude"]
|
|
31
|
+
|
|
32
|
+
def with_resume(self) -> ClaudeCommandBuilder:
|
|
33
|
+
"""Add --resume flag to resume last session.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Self for method chaining
|
|
37
|
+
"""
|
|
38
|
+
self._cmd.append("--resume")
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
def with_plugin_dir(self, plugin_dir: str | Path) -> ClaudeCommandBuilder:
|
|
42
|
+
"""Add --plugin-dir flag.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
plugin_dir: Path to plugin directory
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Self for method chaining
|
|
49
|
+
"""
|
|
50
|
+
self._cmd.extend(["--plugin-dir", str(plugin_dir)])
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def with_system_prompt(self, prompt: str) -> ClaudeCommandBuilder:
|
|
54
|
+
"""Add --append-system-prompt flag.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
prompt: System prompt text to append
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Self for method chaining
|
|
61
|
+
"""
|
|
62
|
+
if prompt: # Only add if prompt is not empty
|
|
63
|
+
self._cmd.extend(["--append-system-prompt", prompt])
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def build(self) -> list[str]:
|
|
67
|
+
"""Build the final command list.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of command arguments ready for subprocess.run()
|
|
71
|
+
"""
|
|
72
|
+
return self._cmd
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Headless AI spawner for multi-AI orchestration.
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
This module provides backward compatibility by delegating to modular spawner implementations.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .spawners import (
|
|
13
|
+
AIResult,
|
|
14
|
+
ClaudeSpawner,
|
|
15
|
+
CodexSpawner,
|
|
16
|
+
CopilotSpawner,
|
|
17
|
+
GeminiSpawner,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Re-export AIResult for backward compatibility
|
|
21
|
+
__all__ = ["HeadlessSpawner", "AIResult"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HeadlessSpawner:
|
|
25
|
+
"""
|
|
26
|
+
Spawn AI agents in headless CLI mode.
|
|
27
|
+
|
|
28
|
+
Supports multiple AI CLIs:
|
|
29
|
+
- spawn_gemini(): Google Gemini (free tier)
|
|
30
|
+
- spawn_codex(): OpenAI Codex (ChatGPT Plus+)
|
|
31
|
+
- spawn_copilot(): GitHub Copilot (GitHub subscription)
|
|
32
|
+
- spawn_claude(): Claude Code (same login as Task tool)
|
|
33
|
+
|
|
34
|
+
spawn_claude() vs Task() Tool:
|
|
35
|
+
--------------------------------
|
|
36
|
+
Both use the same Claude Code authentication and billing, but:
|
|
37
|
+
|
|
38
|
+
spawn_claude():
|
|
39
|
+
- Isolated execution (no context sharing)
|
|
40
|
+
- Fresh session each call
|
|
41
|
+
- Best for: independent tasks, external scripts, parallel processing
|
|
42
|
+
- Cache miss on each call (higher token usage)
|
|
43
|
+
|
|
44
|
+
Task():
|
|
45
|
+
- Shared conversation context
|
|
46
|
+
- Builds on previous work
|
|
47
|
+
- Best for: orchestration, related sequential work
|
|
48
|
+
- Cache hits in session (5x cheaper for related work)
|
|
49
|
+
|
|
50
|
+
Example - When to use spawn_claude():
|
|
51
|
+
# Independent tasks in external script
|
|
52
|
+
spawner = HeadlessSpawner()
|
|
53
|
+
for file in files:
|
|
54
|
+
result = spawner.spawn_claude(f"Analyze {file} independently")
|
|
55
|
+
save_result(file, result)
|
|
56
|
+
|
|
57
|
+
Example - When to use Task() instead:
|
|
58
|
+
# Related tasks in orchestration workflow
|
|
59
|
+
Task(prompt="Analyze all files and compare them")
|
|
60
|
+
# Better: shares context, uses caching
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self) -> None:
|
|
64
|
+
"""Initialize spawner with modular implementations."""
|
|
65
|
+
self._gemini_spawner = GeminiSpawner()
|
|
66
|
+
self._codex_spawner = CodexSpawner()
|
|
67
|
+
self._copilot_spawner = CopilotSpawner()
|
|
68
|
+
self._claude_spawner = ClaudeSpawner()
|
|
69
|
+
|
|
70
|
+
# Expose internal methods for backward compatibility with tests
|
|
71
|
+
def _parse_and_track_gemini_events(self, jsonl_output: str, sdk: Any) -> list[dict]:
|
|
72
|
+
"""Parse and track Gemini events (delegates to GeminiSpawner)."""
|
|
73
|
+
return self._gemini_spawner._parse_and_track_events(jsonl_output, sdk)
|
|
74
|
+
|
|
75
|
+
def _parse_and_track_codex_events(self, jsonl_output: str, sdk: Any) -> list[dict]:
|
|
76
|
+
"""Parse and track Codex events (delegates to CodexSpawner)."""
|
|
77
|
+
return self._codex_spawner._parse_and_track_events(jsonl_output, sdk)
|
|
78
|
+
|
|
79
|
+
def _parse_and_track_copilot_events(
|
|
80
|
+
self, prompt: str, response: str, sdk: Any
|
|
81
|
+
) -> list[dict]:
|
|
82
|
+
"""Parse and track Copilot events (delegates to CopilotSpawner)."""
|
|
83
|
+
return self._copilot_spawner._parse_and_track_events(prompt, response, sdk)
|
|
84
|
+
|
|
85
|
+
def _get_sdk(self) -> Any:
|
|
86
|
+
"""Get SDK instance (delegates to base spawner implementation)."""
|
|
87
|
+
return self._gemini_spawner._get_sdk()
|
|
88
|
+
|
|
89
|
+
def spawn_gemini(
|
|
90
|
+
self,
|
|
91
|
+
prompt: str,
|
|
92
|
+
output_format: str = "stream-json",
|
|
93
|
+
model: str | None = None,
|
|
94
|
+
include_directories: list[str] | None = None,
|
|
95
|
+
track_in_htmlgraph: bool = True,
|
|
96
|
+
timeout: int = 120,
|
|
97
|
+
tracker: Any = None,
|
|
98
|
+
parent_event_id: str | None = None,
|
|
99
|
+
) -> AIResult:
|
|
100
|
+
"""
|
|
101
|
+
Spawn Gemini in headless mode.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
prompt: Task description for Gemini
|
|
105
|
+
output_format: "json" or "stream-json" (enables real-time tracking)
|
|
106
|
+
model: Model selection. Default: None (recommended - lets CLI choose
|
|
107
|
+
thinking-compatible models). Older models may fail.
|
|
108
|
+
include_directories: Directories to include for context. Default: None
|
|
109
|
+
track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
|
|
110
|
+
timeout: Max seconds to wait
|
|
111
|
+
tracker: Optional SpawnerEventTracker for recording subprocess invocation
|
|
112
|
+
parent_event_id: Optional parent event ID for event hierarchy
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
AIResult with response, error, and tracked events if tracking enabled
|
|
116
|
+
"""
|
|
117
|
+
return self._gemini_spawner.spawn(
|
|
118
|
+
prompt=prompt,
|
|
119
|
+
output_format=output_format,
|
|
120
|
+
model=model,
|
|
121
|
+
include_directories=include_directories,
|
|
122
|
+
track_in_htmlgraph=track_in_htmlgraph,
|
|
123
|
+
timeout=timeout,
|
|
124
|
+
tracker=tracker,
|
|
125
|
+
parent_event_id=parent_event_id,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def spawn_codex(
|
|
129
|
+
self,
|
|
130
|
+
prompt: str,
|
|
131
|
+
output_json: bool = True,
|
|
132
|
+
model: str | None = None,
|
|
133
|
+
sandbox: str | None = None,
|
|
134
|
+
full_auto: bool = True,
|
|
135
|
+
images: list[str] | None = None,
|
|
136
|
+
output_last_message: str | None = None,
|
|
137
|
+
output_schema: str | None = None,
|
|
138
|
+
skip_git_check: bool = False,
|
|
139
|
+
working_directory: str | None = None,
|
|
140
|
+
use_oss: bool = False,
|
|
141
|
+
bypass_approvals: bool = False,
|
|
142
|
+
track_in_htmlgraph: bool = True,
|
|
143
|
+
timeout: int = 120,
|
|
144
|
+
tracker: Any = None,
|
|
145
|
+
parent_event_id: str | None = None,
|
|
146
|
+
) -> AIResult:
|
|
147
|
+
"""
|
|
148
|
+
Spawn Codex in headless mode.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
prompt: Task description for Codex
|
|
152
|
+
output_json: JSONL output flag (enables real-time tracking)
|
|
153
|
+
model: Model selection (e.g., "gpt-4-turbo"). Default: None
|
|
154
|
+
sandbox: Sandbox mode ("read-only", "workspace-write", or full)
|
|
155
|
+
full_auto: Enable full auto mode. Default: True (required headless)
|
|
156
|
+
images: List of image paths (--image). Default: None
|
|
157
|
+
output_last_message: Write last message to file. Default: None
|
|
158
|
+
output_schema: JSON schema for validation. Default: None
|
|
159
|
+
skip_git_check: Skip git repo check. Default: False
|
|
160
|
+
working_directory: Workspace directory (--cd). Default: None
|
|
161
|
+
use_oss: Use local Ollama provider (--oss). Default: False
|
|
162
|
+
bypass_approvals: Bypass approval checks. Default: False
|
|
163
|
+
track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
|
|
164
|
+
timeout: Max seconds to wait
|
|
165
|
+
tracker: Optional SpawnerEventTracker for recording subprocess invocation
|
|
166
|
+
parent_event_id: Optional parent event ID for event hierarchy
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
AIResult with response, error, and tracked events if tracking enabled
|
|
170
|
+
"""
|
|
171
|
+
return self._codex_spawner.spawn(
|
|
172
|
+
prompt=prompt,
|
|
173
|
+
output_json=output_json,
|
|
174
|
+
model=model,
|
|
175
|
+
sandbox=sandbox,
|
|
176
|
+
full_auto=full_auto,
|
|
177
|
+
images=images,
|
|
178
|
+
output_last_message=output_last_message,
|
|
179
|
+
output_schema=output_schema,
|
|
180
|
+
skip_git_check=skip_git_check,
|
|
181
|
+
working_directory=working_directory,
|
|
182
|
+
use_oss=use_oss,
|
|
183
|
+
bypass_approvals=bypass_approvals,
|
|
184
|
+
track_in_htmlgraph=track_in_htmlgraph,
|
|
185
|
+
timeout=timeout,
|
|
186
|
+
tracker=tracker,
|
|
187
|
+
parent_event_id=parent_event_id,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
def spawn_copilot(
|
|
191
|
+
self,
|
|
192
|
+
prompt: str,
|
|
193
|
+
allow_tools: list[str] | None = None,
|
|
194
|
+
allow_all_tools: bool = False,
|
|
195
|
+
deny_tools: list[str] | None = None,
|
|
196
|
+
track_in_htmlgraph: bool = True,
|
|
197
|
+
timeout: int = 120,
|
|
198
|
+
tracker: Any = None,
|
|
199
|
+
parent_event_id: str | None = None,
|
|
200
|
+
) -> AIResult:
|
|
201
|
+
"""
|
|
202
|
+
Spawn GitHub Copilot in headless mode.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
prompt: Task description for Copilot
|
|
206
|
+
allow_tools: Tools to auto-approve (e.g., ["shell(git)"])
|
|
207
|
+
allow_all_tools: Auto-approve all tools. Default: False
|
|
208
|
+
deny_tools: Tools to deny (--deny-tool). Default: None
|
|
209
|
+
track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
|
|
210
|
+
timeout: Max seconds to wait
|
|
211
|
+
tracker: Optional SpawnerEventTracker for recording subprocess invocation
|
|
212
|
+
parent_event_id: Optional parent event ID for event hierarchy
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
AIResult with response, error, and tracked events if tracking enabled
|
|
216
|
+
"""
|
|
217
|
+
return self._copilot_spawner.spawn(
|
|
218
|
+
prompt=prompt,
|
|
219
|
+
allow_tools=allow_tools,
|
|
220
|
+
allow_all_tools=allow_all_tools,
|
|
221
|
+
deny_tools=deny_tools,
|
|
222
|
+
track_in_htmlgraph=track_in_htmlgraph,
|
|
223
|
+
timeout=timeout,
|
|
224
|
+
tracker=tracker,
|
|
225
|
+
parent_event_id=parent_event_id,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def spawn_claude(
|
|
229
|
+
self,
|
|
230
|
+
prompt: str,
|
|
231
|
+
output_format: str = "json",
|
|
232
|
+
permission_mode: str = "bypassPermissions",
|
|
233
|
+
resume: str | None = None,
|
|
234
|
+
verbose: bool = False,
|
|
235
|
+
timeout: int = 300,
|
|
236
|
+
extra_args: list[str] | None = None,
|
|
237
|
+
) -> AIResult:
|
|
238
|
+
"""
|
|
239
|
+
Spawn Claude in headless mode.
|
|
240
|
+
|
|
241
|
+
NOTE: Uses same Claude Code authentication as Task() tool, but provides
|
|
242
|
+
isolated execution context. Each call creates a new session without shared
|
|
243
|
+
context. Best for independent tasks or external scripts.
|
|
244
|
+
|
|
245
|
+
For orchestration workflows with shared context, prefer Task() tool which
|
|
246
|
+
leverages prompt caching (5x cheaper for related work).
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
prompt: Task description for Claude
|
|
250
|
+
output_format: "text" or "json" (stream-json requires --verbose)
|
|
251
|
+
permission_mode: Permission handling mode:
|
|
252
|
+
- "bypassPermissions": Auto-approve all (default)
|
|
253
|
+
- "acceptEdits": Auto-approve edits only
|
|
254
|
+
- "dontAsk": Fail on permission prompts
|
|
255
|
+
- "default": Normal interactive prompts
|
|
256
|
+
- "plan": Plan mode (no execution)
|
|
257
|
+
- "delegate": Delegation mode
|
|
258
|
+
resume: Resume from previous session (--resume). Default: None
|
|
259
|
+
verbose: Enable verbose output (--verbose). Default: False
|
|
260
|
+
timeout: Max seconds (default: 300, Claude can be slow with initialization)
|
|
261
|
+
extra_args: Additional arguments to pass to Claude CLI
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
AIResult with response or error
|
|
265
|
+
|
|
266
|
+
Example:
|
|
267
|
+
>>> spawner = HeadlessSpawner()
|
|
268
|
+
>>> result = spawner.spawn_claude("What is 2+2?")
|
|
269
|
+
>>> if result.success:
|
|
270
|
+
... logger.info("%s", result.response) # "4"
|
|
271
|
+
... logger.info(f"Cost: ${result.raw_output['total_cost_usd']}")
|
|
272
|
+
"""
|
|
273
|
+
return self._claude_spawner.spawn(
|
|
274
|
+
prompt=prompt,
|
|
275
|
+
output_format=output_format,
|
|
276
|
+
permission_mode=permission_mode,
|
|
277
|
+
resume=resume,
|
|
278
|
+
verbose=verbose,
|
|
279
|
+
timeout=timeout,
|
|
280
|
+
extra_args=extra_args,
|
|
281
|
+
)
|