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,399 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""HTML template for cost dashboard.
|
|
4
|
+
|
|
5
|
+
This module generates a beautiful, interactive HTML dashboard for cost analysis.
|
|
6
|
+
Separated from main CLI code for maintainability and testability.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from htmlgraph.cli.analytics import CostSummary
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_html(summary: CostSummary) -> str:
|
|
18
|
+
"""Generate HTML dashboard from cost summary.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
summary: Validated cost summary data
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Complete HTML document as string
|
|
25
|
+
"""
|
|
26
|
+
# Calculate derived metrics
|
|
27
|
+
total_cost = summary.total_cost_tokens
|
|
28
|
+
avg_cost = summary.avg_cost_per_event
|
|
29
|
+
delegation_pct = summary.delegation_percentage
|
|
30
|
+
cost_usd = summary.estimated_cost_usd
|
|
31
|
+
|
|
32
|
+
# Sort tools by cost
|
|
33
|
+
sorted_tools = sorted(
|
|
34
|
+
summary.tool_costs.items(),
|
|
35
|
+
key=lambda x: x[1].total_tokens,
|
|
36
|
+
reverse=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Sort sessions by cost
|
|
40
|
+
sorted_sessions = sorted(
|
|
41
|
+
summary.session_costs.items(),
|
|
42
|
+
key=lambda x: x[1].total_tokens,
|
|
43
|
+
reverse=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Build tool cost rows
|
|
47
|
+
tool_rows = _build_tool_rows(sorted_tools, total_cost)
|
|
48
|
+
|
|
49
|
+
# Build session cost rows
|
|
50
|
+
session_rows = _build_session_rows(sorted_sessions, total_cost)
|
|
51
|
+
|
|
52
|
+
# Build insights
|
|
53
|
+
insights = _build_insights_html(delegation_pct, sorted_tools, total_cost)
|
|
54
|
+
|
|
55
|
+
# Generate complete HTML
|
|
56
|
+
return f"""<!DOCTYPE html>
|
|
57
|
+
<html lang="en">
|
|
58
|
+
<head>
|
|
59
|
+
<meta charset="UTF-8">
|
|
60
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
61
|
+
<title>HtmlGraph Cost Dashboard</title>
|
|
62
|
+
<style>
|
|
63
|
+
{_get_css_styles()}
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<div class="container">
|
|
68
|
+
<header>
|
|
69
|
+
<h1>💰 HtmlGraph Cost Dashboard</h1>
|
|
70
|
+
<p class="timestamp">Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
|
|
71
|
+
<div class="metrics">
|
|
72
|
+
<div class="metric">
|
|
73
|
+
<div class="metric-label">Total Cost</div>
|
|
74
|
+
<div class="metric-value">{total_cost:,}<span class="metric-unit">tokens</span></div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="metric success">
|
|
77
|
+
<div class="metric-label">Estimated USD</div>
|
|
78
|
+
<div class="metric-value">${cost_usd:.2f}</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="metric">
|
|
81
|
+
<div class="metric-label">Average Cost</div>
|
|
82
|
+
<div class="metric-value">{avg_cost:,.0f}<span class="metric-unit">tokens</span></div>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="metric success">
|
|
85
|
+
<div class="metric-label">Delegation Rate</div>
|
|
86
|
+
<div class="metric-value">{delegation_pct:.1f}%</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</header>
|
|
90
|
+
|
|
91
|
+
<section>
|
|
92
|
+
<h2>📊 Cost by Tool</h2>
|
|
93
|
+
<table>
|
|
94
|
+
<thead>
|
|
95
|
+
<tr>
|
|
96
|
+
<th>Tool</th>
|
|
97
|
+
<th>Count</th>
|
|
98
|
+
<th>Total Tokens</th>
|
|
99
|
+
<th>% of Total</th>
|
|
100
|
+
</tr>
|
|
101
|
+
</thead>
|
|
102
|
+
<tbody>
|
|
103
|
+
{tool_rows}
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
<section>
|
|
109
|
+
<h2>🔄 Cost by Session</h2>
|
|
110
|
+
<table>
|
|
111
|
+
<thead>
|
|
112
|
+
<tr>
|
|
113
|
+
<th>Session ID</th>
|
|
114
|
+
<th>Count</th>
|
|
115
|
+
<th>Total Tokens</th>
|
|
116
|
+
<th>% of Total</th>
|
|
117
|
+
</tr>
|
|
118
|
+
</thead>
|
|
119
|
+
<tbody>
|
|
120
|
+
{session_rows}
|
|
121
|
+
</tbody>
|
|
122
|
+
</table>
|
|
123
|
+
</section>
|
|
124
|
+
|
|
125
|
+
<section>
|
|
126
|
+
<h2>💡 Insights & Recommendations</h2>
|
|
127
|
+
<div class="insights">
|
|
128
|
+
{insights}
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
|
|
132
|
+
<div class="footer">
|
|
133
|
+
<p>HtmlGraph Cost Attribution Dashboard | Real-time cost tracking and optimization</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</body>
|
|
137
|
+
</html>"""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _build_tool_rows(sorted_tools: list[tuple[str, Any]], total_cost: int) -> str:
|
|
141
|
+
"""Build HTML table rows for tool costs."""
|
|
142
|
+
if not sorted_tools or total_cost == 0:
|
|
143
|
+
return '<tr><td colspan="4" class="cell">No data available</td></tr>'
|
|
144
|
+
|
|
145
|
+
rows = []
|
|
146
|
+
for tool, data in sorted_tools[:20]: # Top 20 tools
|
|
147
|
+
pct = data.total_tokens / total_cost * 100
|
|
148
|
+
rows.append(
|
|
149
|
+
f"""
|
|
150
|
+
<tr>
|
|
151
|
+
<td class="cell">{tool}</td>
|
|
152
|
+
<td class="cell number">{data.count}</td>
|
|
153
|
+
<td class="cell number">{data.total_tokens:,}</td>
|
|
154
|
+
<td class="cell number">{pct:.1f}%</td>
|
|
155
|
+
</tr>"""
|
|
156
|
+
)
|
|
157
|
+
return "".join(rows)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _build_session_rows(sorted_sessions: list[tuple[str, Any]], total_cost: int) -> str:
|
|
161
|
+
"""Build HTML table rows for session costs."""
|
|
162
|
+
if not sorted_sessions or total_cost == 0:
|
|
163
|
+
return '<tr><td colspan="4" class="cell">No data available</td></tr>'
|
|
164
|
+
|
|
165
|
+
rows = []
|
|
166
|
+
for session, data in sorted_sessions[:20]: # Top 20 sessions
|
|
167
|
+
pct = data.total_tokens / total_cost * 100
|
|
168
|
+
session_display = session[:12] + "..." if len(session) > 12 else session
|
|
169
|
+
rows.append(
|
|
170
|
+
f"""
|
|
171
|
+
<tr>
|
|
172
|
+
<td class="cell">{session_display}</td>
|
|
173
|
+
<td class="cell number">{data.count}</td>
|
|
174
|
+
<td class="cell number">{data.total_tokens:,}</td>
|
|
175
|
+
<td class="cell number">{pct:.1f}%</td>
|
|
176
|
+
</tr>"""
|
|
177
|
+
)
|
|
178
|
+
return "".join(rows)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _build_insights_html(
|
|
182
|
+
delegation_pct: float, sorted_tools: list[tuple[str, Any]], total_cost: int
|
|
183
|
+
) -> str:
|
|
184
|
+
"""Build insights and recommendations section."""
|
|
185
|
+
insights = []
|
|
186
|
+
|
|
187
|
+
# Delegation insight
|
|
188
|
+
delegation_msg = (
|
|
189
|
+
"Continue delegation for cost efficiency!"
|
|
190
|
+
if delegation_pct > 50
|
|
191
|
+
else "Consider delegating more operations to reduce costs."
|
|
192
|
+
)
|
|
193
|
+
insights.append(
|
|
194
|
+
f"""
|
|
195
|
+
<div class="insight">
|
|
196
|
+
<div class="insight-title">✓ Delegation Usage</div>
|
|
197
|
+
<div class="insight-text">
|
|
198
|
+
You're delegating {delegation_pct:.1f}% of operations.
|
|
199
|
+
{delegation_msg}
|
|
200
|
+
</div>
|
|
201
|
+
</div>"""
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Top cost driver insight
|
|
205
|
+
if sorted_tools and total_cost > 0:
|
|
206
|
+
top_tool, top_data = sorted_tools[0]
|
|
207
|
+
top_pct = top_data.total_tokens / total_cost * 100
|
|
208
|
+
insights.append(
|
|
209
|
+
f"""
|
|
210
|
+
<div class="insight">
|
|
211
|
+
<div class="insight-title">🎯 Top Cost Driver</div>
|
|
212
|
+
<div class="insight-text">
|
|
213
|
+
{top_tool} accounts for {top_pct:.1f}% of total cost.
|
|
214
|
+
Review if this tool usage is optimal.
|
|
215
|
+
</div>
|
|
216
|
+
</div>"""
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Parallelization insight
|
|
220
|
+
insights.append(
|
|
221
|
+
"""
|
|
222
|
+
<div class="insight">
|
|
223
|
+
<div class="insight-title">📈 Parallelization Opportunity</div>
|
|
224
|
+
<div class="insight-text">
|
|
225
|
+
Parallel Task() calls can reduce overall execution time by ~40%.
|
|
226
|
+
Look for independent operations that can run simultaneously.
|
|
227
|
+
</div>
|
|
228
|
+
</div>"""
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return "".join(insights)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _get_css_styles() -> str:
|
|
235
|
+
"""Get CSS styles for the dashboard.
|
|
236
|
+
|
|
237
|
+
Extracted to separate function for maintainability.
|
|
238
|
+
"""
|
|
239
|
+
return """ * {
|
|
240
|
+
margin: 0;
|
|
241
|
+
padding: 0;
|
|
242
|
+
box-sizing: border-box;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
body {
|
|
246
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
247
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
248
|
+
min-height: 100vh;
|
|
249
|
+
padding: 40px 20px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.container {
|
|
253
|
+
max-width: 1400px;
|
|
254
|
+
margin: 0 auto;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
header {
|
|
258
|
+
background: white;
|
|
259
|
+
border-radius: 12px;
|
|
260
|
+
padding: 30px;
|
|
261
|
+
margin-bottom: 30px;
|
|
262
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
h1 {
|
|
266
|
+
color: #667eea;
|
|
267
|
+
margin-bottom: 10px;
|
|
268
|
+
font-size: 28px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.timestamp {
|
|
272
|
+
color: #999;
|
|
273
|
+
font-size: 12px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.metrics {
|
|
277
|
+
display: grid;
|
|
278
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
279
|
+
gap: 20px;
|
|
280
|
+
margin-top: 20px;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.metric {
|
|
284
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
285
|
+
color: white;
|
|
286
|
+
padding: 20px;
|
|
287
|
+
border-radius: 8px;
|
|
288
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.metric-label {
|
|
292
|
+
font-size: 12px;
|
|
293
|
+
opacity: 0.9;
|
|
294
|
+
margin-bottom: 8px;
|
|
295
|
+
text-transform: uppercase;
|
|
296
|
+
letter-spacing: 1px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.metric-value {
|
|
300
|
+
font-size: 32px;
|
|
301
|
+
font-weight: bold;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.metric-unit {
|
|
305
|
+
font-size: 14px;
|
|
306
|
+
opacity: 0.8;
|
|
307
|
+
margin-left: 8px;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.metric.warning {
|
|
311
|
+
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.metric.success {
|
|
315
|
+
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
section {
|
|
319
|
+
background: white;
|
|
320
|
+
border-radius: 12px;
|
|
321
|
+
padding: 30px;
|
|
322
|
+
margin-bottom: 30px;
|
|
323
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
h2 {
|
|
327
|
+
color: #333;
|
|
328
|
+
margin-bottom: 20px;
|
|
329
|
+
font-size: 20px;
|
|
330
|
+
border-bottom: 2px solid #667eea;
|
|
331
|
+
padding-bottom: 10px;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
table {
|
|
335
|
+
width: 100%;
|
|
336
|
+
border-collapse: collapse;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
th {
|
|
340
|
+
background: #f5f5f5;
|
|
341
|
+
padding: 12px;
|
|
342
|
+
text-align: left;
|
|
343
|
+
font-weight: 600;
|
|
344
|
+
color: #333;
|
|
345
|
+
border-bottom: 2px solid #ddd;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
td {
|
|
349
|
+
padding: 12px;
|
|
350
|
+
border-bottom: 1px solid #eee;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
td.cell {
|
|
354
|
+
color: #333;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
td.number {
|
|
358
|
+
text-align: right;
|
|
359
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
360
|
+
color: #667eea;
|
|
361
|
+
font-weight: 500;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
tr:hover {
|
|
365
|
+
background: #f9f9f9;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.insights {
|
|
369
|
+
display: grid;
|
|
370
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
371
|
+
gap: 20px;
|
|
372
|
+
margin-top: 20px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.insight {
|
|
376
|
+
background: #f0f4ff;
|
|
377
|
+
border-left: 4px solid #667eea;
|
|
378
|
+
padding: 16px;
|
|
379
|
+
border-radius: 4px;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.insight-title {
|
|
383
|
+
font-weight: 600;
|
|
384
|
+
color: #333;
|
|
385
|
+
margin-bottom: 8px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.insight-text {
|
|
389
|
+
color: #666;
|
|
390
|
+
font-size: 14px;
|
|
391
|
+
line-height: 1.6;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.footer {
|
|
395
|
+
text-align: center;
|
|
396
|
+
color: #999;
|
|
397
|
+
font-size: 12px;
|
|
398
|
+
margin-top: 40px;
|
|
399
|
+
}"""
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""HtmlGraph CLI - Work management commands.
|
|
4
|
+
|
|
5
|
+
Commands for managing work items:
|
|
6
|
+
- Features: Work item tracking
|
|
7
|
+
- Sessions: Session management
|
|
8
|
+
- Tracks: Multi-feature planning
|
|
9
|
+
- Archives: Archival management
|
|
10
|
+
- Orchestrator: Claude Code integration
|
|
11
|
+
- Other work-related operations
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from argparse import _SubParsersAction
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def register_commands(subparsers: _SubParsersAction) -> None:
|
|
22
|
+
"""Register work management commands with the argument parser.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
subparsers: Subparser action from ArgumentParser.add_subparsers()
|
|
26
|
+
"""
|
|
27
|
+
from htmlgraph.cli.work.browse import BrowseCommand
|
|
28
|
+
from htmlgraph.cli.work.features import register_feature_commands
|
|
29
|
+
from htmlgraph.cli.work.orchestration import (
|
|
30
|
+
register_archive_commands,
|
|
31
|
+
register_claude_commands,
|
|
32
|
+
register_orchestrator_commands,
|
|
33
|
+
)
|
|
34
|
+
from htmlgraph.cli.work.report import register_report_commands
|
|
35
|
+
from htmlgraph.cli.work.sessions import register_session_commands
|
|
36
|
+
from htmlgraph.cli.work.snapshot import SnapshotCommand
|
|
37
|
+
from htmlgraph.cli.work.tracks import register_track_commands
|
|
38
|
+
|
|
39
|
+
# Register all command groups
|
|
40
|
+
register_session_commands(subparsers)
|
|
41
|
+
register_feature_commands(subparsers)
|
|
42
|
+
register_track_commands(subparsers)
|
|
43
|
+
register_archive_commands(subparsers)
|
|
44
|
+
register_orchestrator_commands(subparsers)
|
|
45
|
+
register_claude_commands(subparsers)
|
|
46
|
+
register_report_commands(subparsers)
|
|
47
|
+
|
|
48
|
+
# Snapshot command
|
|
49
|
+
snapshot_parser = subparsers.add_parser(
|
|
50
|
+
"snapshot",
|
|
51
|
+
help="Output current graph state with refs",
|
|
52
|
+
)
|
|
53
|
+
snapshot_parser.add_argument(
|
|
54
|
+
"--output-format",
|
|
55
|
+
choices=["refs", "json", "text"],
|
|
56
|
+
default="refs",
|
|
57
|
+
help="Output format (default: refs)",
|
|
58
|
+
)
|
|
59
|
+
snapshot_parser.add_argument(
|
|
60
|
+
"--type",
|
|
61
|
+
help="Filter by type (feature, track, bug, spike, chore, epic, all)",
|
|
62
|
+
)
|
|
63
|
+
snapshot_parser.add_argument(
|
|
64
|
+
"--status",
|
|
65
|
+
help="Filter by status (todo, in_progress, blocked, done, all)",
|
|
66
|
+
)
|
|
67
|
+
snapshot_parser.add_argument(
|
|
68
|
+
"--track",
|
|
69
|
+
help="Show only items in a specific track (by track ID or ref)",
|
|
70
|
+
)
|
|
71
|
+
snapshot_parser.add_argument(
|
|
72
|
+
"--active",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help="Show only TODO/IN_PROGRESS items (filters out metadata spikes)",
|
|
75
|
+
)
|
|
76
|
+
snapshot_parser.add_argument(
|
|
77
|
+
"--blockers",
|
|
78
|
+
action="store_true",
|
|
79
|
+
help="Show only critical/blocked items",
|
|
80
|
+
)
|
|
81
|
+
snapshot_parser.add_argument(
|
|
82
|
+
"--summary",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Show counts and progress summary instead of listing all items",
|
|
85
|
+
)
|
|
86
|
+
snapshot_parser.add_argument(
|
|
87
|
+
"--my-work",
|
|
88
|
+
action="store_true",
|
|
89
|
+
help="Show items assigned to current agent",
|
|
90
|
+
)
|
|
91
|
+
snapshot_parser.set_defaults(func=SnapshotCommand.from_args)
|
|
92
|
+
|
|
93
|
+
# Browse command
|
|
94
|
+
browse_parser = subparsers.add_parser(
|
|
95
|
+
"browse",
|
|
96
|
+
help="Open dashboard in browser",
|
|
97
|
+
)
|
|
98
|
+
browse_parser.add_argument(
|
|
99
|
+
"--port",
|
|
100
|
+
type=int,
|
|
101
|
+
default=8080,
|
|
102
|
+
help="Server port (default: 8080)",
|
|
103
|
+
)
|
|
104
|
+
browse_parser.add_argument(
|
|
105
|
+
"--query-type",
|
|
106
|
+
help="Filter by type (feature, track, bug, spike, chore, epic)",
|
|
107
|
+
)
|
|
108
|
+
browse_parser.add_argument(
|
|
109
|
+
"--query-status",
|
|
110
|
+
help="Filter by status (todo, in_progress, blocked, done)",
|
|
111
|
+
)
|
|
112
|
+
browse_parser.set_defaults(func=BrowseCommand.from_args)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Re-export all command classes for backward compatibility
|
|
116
|
+
from htmlgraph.cli.work.browse import BrowseCommand
|
|
117
|
+
from htmlgraph.cli.work.features import (
|
|
118
|
+
FeatureClaimCommand,
|
|
119
|
+
FeatureCompleteCommand,
|
|
120
|
+
FeatureCreateCommand,
|
|
121
|
+
FeatureListCommand,
|
|
122
|
+
FeaturePrimaryCommand,
|
|
123
|
+
FeatureReleaseCommand,
|
|
124
|
+
FeatureStartCommand,
|
|
125
|
+
)
|
|
126
|
+
from htmlgraph.cli.work.orchestration import (
|
|
127
|
+
ArchiveCreateCommand,
|
|
128
|
+
ArchiveListCommand,
|
|
129
|
+
ClaudeCommand,
|
|
130
|
+
OrchestratorDisableCommand,
|
|
131
|
+
OrchestratorEnableCommand,
|
|
132
|
+
OrchestratorResetViolationsCommand,
|
|
133
|
+
OrchestratorSetLevelCommand,
|
|
134
|
+
OrchestratorStatusCommand,
|
|
135
|
+
)
|
|
136
|
+
from htmlgraph.cli.work.report import SessionReportCommand
|
|
137
|
+
from htmlgraph.cli.work.sessions import (
|
|
138
|
+
SessionEndCommand,
|
|
139
|
+
SessionHandoffCommand,
|
|
140
|
+
SessionListCommand,
|
|
141
|
+
SessionStartCommand,
|
|
142
|
+
SessionStartInfoCommand,
|
|
143
|
+
)
|
|
144
|
+
from htmlgraph.cli.work.snapshot import SnapshotCommand
|
|
145
|
+
from htmlgraph.cli.work.tracks import (
|
|
146
|
+
TrackDeleteCommand,
|
|
147
|
+
TrackListCommand,
|
|
148
|
+
TrackNewCommand,
|
|
149
|
+
TrackPlanCommand,
|
|
150
|
+
TrackSpecCommand,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
__all__ = [
|
|
154
|
+
"register_commands",
|
|
155
|
+
# Session commands
|
|
156
|
+
"SessionStartCommand",
|
|
157
|
+
"SessionEndCommand",
|
|
158
|
+
"SessionListCommand",
|
|
159
|
+
"SessionHandoffCommand",
|
|
160
|
+
"SessionStartInfoCommand",
|
|
161
|
+
# Report commands
|
|
162
|
+
"SessionReportCommand",
|
|
163
|
+
# Snapshot commands
|
|
164
|
+
"SnapshotCommand",
|
|
165
|
+
# Browse commands
|
|
166
|
+
"BrowseCommand",
|
|
167
|
+
# Feature commands
|
|
168
|
+
"FeatureListCommand",
|
|
169
|
+
"FeatureCreateCommand",
|
|
170
|
+
"FeatureStartCommand",
|
|
171
|
+
"FeatureCompleteCommand",
|
|
172
|
+
"FeatureClaimCommand",
|
|
173
|
+
"FeatureReleaseCommand",
|
|
174
|
+
"FeaturePrimaryCommand",
|
|
175
|
+
# Track commands
|
|
176
|
+
"TrackNewCommand",
|
|
177
|
+
"TrackListCommand",
|
|
178
|
+
"TrackSpecCommand",
|
|
179
|
+
"TrackPlanCommand",
|
|
180
|
+
"TrackDeleteCommand",
|
|
181
|
+
# Orchestration commands
|
|
182
|
+
"ArchiveCreateCommand",
|
|
183
|
+
"ArchiveListCommand",
|
|
184
|
+
"OrchestratorStatusCommand",
|
|
185
|
+
"OrchestratorEnableCommand",
|
|
186
|
+
"OrchestratorDisableCommand",
|
|
187
|
+
"OrchestratorResetViolationsCommand",
|
|
188
|
+
"OrchestratorSetLevelCommand",
|
|
189
|
+
"ClaudeCommand",
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Convenience functions for backward compatibility with tests
|
|
194
|
+
def cmd_orchestrator_reset_violations(args: object) -> None:
|
|
195
|
+
"""Reset violations command."""
|
|
196
|
+
from argparse import Namespace
|
|
197
|
+
|
|
198
|
+
if isinstance(args, Namespace):
|
|
199
|
+
cmd = OrchestratorResetViolationsCommand.from_args(args)
|
|
200
|
+
cmd.graph_dir = (
|
|
201
|
+
str(args.graph_dir) if hasattr(args, "graph_dir") else ".htmlgraph"
|
|
202
|
+
)
|
|
203
|
+
result = cmd.execute()
|
|
204
|
+
from htmlgraph.cli.base import TextFormatter
|
|
205
|
+
|
|
206
|
+
formatter = TextFormatter()
|
|
207
|
+
formatter.output(result)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def cmd_orchestrator_set_level(args: object) -> None:
|
|
211
|
+
"""Set level command."""
|
|
212
|
+
from argparse import Namespace
|
|
213
|
+
|
|
214
|
+
if isinstance(args, Namespace):
|
|
215
|
+
cmd = OrchestratorSetLevelCommand.from_args(args)
|
|
216
|
+
cmd.graph_dir = (
|
|
217
|
+
str(args.graph_dir) if hasattr(args, "graph_dir") else ".htmlgraph"
|
|
218
|
+
)
|
|
219
|
+
result = cmd.execute()
|
|
220
|
+
from htmlgraph.cli.base import TextFormatter
|
|
221
|
+
|
|
222
|
+
formatter = TextFormatter()
|
|
223
|
+
formatter.output(result)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def cmd_orchestrator_status(args: object) -> None:
|
|
227
|
+
"""Status command."""
|
|
228
|
+
from argparse import Namespace
|
|
229
|
+
|
|
230
|
+
if isinstance(args, Namespace):
|
|
231
|
+
cmd = OrchestratorStatusCommand.from_args(args)
|
|
232
|
+
cmd.graph_dir = (
|
|
233
|
+
str(args.graph_dir) if hasattr(args, "graph_dir") else ".htmlgraph"
|
|
234
|
+
)
|
|
235
|
+
result = cmd.execute()
|
|
236
|
+
from htmlgraph.cli.base import TextFormatter
|
|
237
|
+
|
|
238
|
+
formatter = TextFormatter()
|
|
239
|
+
formatter.output(result)
|