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
htmlgraph/deploy.py
CHANGED
|
@@ -7,22 +7,20 @@ Projects can customize deployment via htmlgraph-deploy.toml configuration file.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
-
import
|
|
10
|
+
import shutil
|
|
11
11
|
import subprocess
|
|
12
|
+
import sys
|
|
12
13
|
import time
|
|
13
|
-
import
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
from typing import Dict, List, Optional, Any, Callable
|
|
14
|
+
from collections.abc import Callable
|
|
16
15
|
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import cast
|
|
17
18
|
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.prompt import Confirm
|
|
18
21
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
RED = '\033[0;31m'
|
|
22
|
-
GREEN = '\033[0;32m'
|
|
23
|
-
YELLOW = '\033[1;33m'
|
|
24
|
-
BLUE = '\033[0;34m'
|
|
25
|
-
NC = '\033[0m' # No Color
|
|
22
|
+
# Global Rich Console for beautiful CLI output
|
|
23
|
+
console = Console()
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
@dataclass
|
|
@@ -31,16 +29,18 @@ class DeploymentConfig:
|
|
|
31
29
|
|
|
32
30
|
# Project info
|
|
33
31
|
project_name: str = "my-project"
|
|
34
|
-
pypi_package:
|
|
32
|
+
pypi_package: str | None = None
|
|
35
33
|
|
|
36
34
|
# Deployment steps
|
|
37
|
-
steps:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
steps: list[str] = field(
|
|
36
|
+
default_factory=lambda: [
|
|
37
|
+
"git-push",
|
|
38
|
+
"build",
|
|
39
|
+
"pypi-publish",
|
|
40
|
+
"local-install",
|
|
41
|
+
"update-plugins",
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
44
|
|
|
45
45
|
# Git config
|
|
46
46
|
git_branch: str = "main"
|
|
@@ -56,28 +56,28 @@ class DeploymentConfig:
|
|
|
56
56
|
pypi_wait_after_publish: int = 10 # seconds
|
|
57
57
|
|
|
58
58
|
# Plugin config
|
|
59
|
-
plugins:
|
|
59
|
+
plugins: dict[str, str] = field(default_factory=dict)
|
|
60
60
|
|
|
61
61
|
# Custom hooks
|
|
62
|
-
pre_build_hooks:
|
|
63
|
-
post_build_hooks:
|
|
64
|
-
pre_publish_hooks:
|
|
65
|
-
post_publish_hooks:
|
|
62
|
+
pre_build_hooks: list[str] = field(default_factory=list)
|
|
63
|
+
post_build_hooks: list[str] = field(default_factory=list)
|
|
64
|
+
pre_publish_hooks: list[str] = field(default_factory=list)
|
|
65
|
+
post_publish_hooks: list[str] = field(default_factory=list)
|
|
66
66
|
|
|
67
67
|
@classmethod
|
|
68
|
-
def from_toml(cls, config_path: Path) ->
|
|
68
|
+
def from_toml(cls, config_path: Path) -> "DeploymentConfig":
|
|
69
69
|
"""Load configuration from TOML file."""
|
|
70
70
|
try:
|
|
71
71
|
import tomllib
|
|
72
72
|
except ImportError:
|
|
73
73
|
import tomli as tomllib # fallback for Python < 3.11
|
|
74
74
|
|
|
75
|
-
with open(config_path,
|
|
75
|
+
with open(config_path, "rb") as f:
|
|
76
76
|
data = tomllib.load(f)
|
|
77
77
|
|
|
78
78
|
# Extract config sections
|
|
79
|
-
project = data.get(
|
|
80
|
-
deployment = data.get(
|
|
79
|
+
project = data.get("project", {})
|
|
80
|
+
deployment = data.get("deployment", {})
|
|
81
81
|
|
|
82
82
|
# Default steps
|
|
83
83
|
default_steps = [
|
|
@@ -85,25 +85,29 @@ class DeploymentConfig:
|
|
|
85
85
|
"build",
|
|
86
86
|
"pypi-publish",
|
|
87
87
|
"local-install",
|
|
88
|
-
"update-plugins"
|
|
88
|
+
"update-plugins",
|
|
89
89
|
]
|
|
90
90
|
|
|
91
91
|
return cls(
|
|
92
|
-
project_name=project.get(
|
|
93
|
-
pypi_package=project.get(
|
|
94
|
-
steps=deployment.get(
|
|
95
|
-
git_branch=deployment.get(
|
|
96
|
-
git_remote=deployment.get(
|
|
97
|
-
git_push_tags=deployment.get(
|
|
98
|
-
build_command=deployment.get(
|
|
99
|
-
clean_dist=deployment.get(
|
|
100
|
-
pypi_token_env_var=deployment.get(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
project_name=project.get("name", "my-project"),
|
|
93
|
+
pypi_package=project.get("pypi_package"),
|
|
94
|
+
steps=deployment.get("steps", default_steps),
|
|
95
|
+
git_branch=deployment.get("git", {}).get("branch", "main"),
|
|
96
|
+
git_remote=deployment.get("git", {}).get("remote", "origin"),
|
|
97
|
+
git_push_tags=deployment.get("git", {}).get("push_tags", True),
|
|
98
|
+
build_command=deployment.get("build", {}).get("command", "uv build"),
|
|
99
|
+
clean_dist=deployment.get("build", {}).get("clean_dist", True),
|
|
100
|
+
pypi_token_env_var=deployment.get("pypi", {}).get(
|
|
101
|
+
"token_env_var", "PyPI_API_TOKEN"
|
|
102
|
+
),
|
|
103
|
+
pypi_wait_after_publish=deployment.get("pypi", {}).get(
|
|
104
|
+
"wait_after_publish", 10
|
|
105
|
+
),
|
|
106
|
+
plugins=deployment.get("plugins", {}),
|
|
107
|
+
pre_build_hooks=deployment.get("hooks", {}).get("pre_build", []),
|
|
108
|
+
post_build_hooks=deployment.get("hooks", {}).get("post_build", []),
|
|
109
|
+
pre_publish_hooks=deployment.get("hooks", {}).get("pre_publish", []),
|
|
110
|
+
post_publish_hooks=deployment.get("hooks", {}).get("post_publish", []),
|
|
107
111
|
)
|
|
108
112
|
|
|
109
113
|
|
|
@@ -114,8 +118,8 @@ class Deployer:
|
|
|
114
118
|
self,
|
|
115
119
|
config: DeploymentConfig,
|
|
116
120
|
dry_run: bool = False,
|
|
117
|
-
skip_steps:
|
|
118
|
-
only_steps:
|
|
121
|
+
skip_steps: list[str] | None = None,
|
|
122
|
+
only_steps: list[str] | None = None,
|
|
119
123
|
):
|
|
120
124
|
self.config = config
|
|
121
125
|
self.dry_run = dry_run
|
|
@@ -124,12 +128,12 @@ class Deployer:
|
|
|
124
128
|
self.version = self._detect_version()
|
|
125
129
|
|
|
126
130
|
# Step handlers
|
|
127
|
-
self.step_handlers:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
self.step_handlers: dict[str, Callable] = {
|
|
132
|
+
"git-push": self._step_git_push,
|
|
133
|
+
"build": self._step_build,
|
|
134
|
+
"pypi-publish": self._step_pypi_publish,
|
|
135
|
+
"local-install": self._step_local_install,
|
|
136
|
+
"update-plugins": self._step_update_plugins,
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
def _detect_version(self) -> str:
|
|
@@ -143,41 +147,39 @@ class Deployer:
|
|
|
143
147
|
except ImportError:
|
|
144
148
|
import tomli as tomllib
|
|
145
149
|
|
|
146
|
-
with open(pyproject,
|
|
150
|
+
with open(pyproject, "rb") as f:
|
|
147
151
|
data = tomllib.load(f)
|
|
148
152
|
|
|
149
|
-
return data.get(
|
|
153
|
+
return cast(str, data.get("project", {}).get("version", "unknown"))
|
|
150
154
|
|
|
151
|
-
def log_section(self, message: str):
|
|
155
|
+
def log_section(self, message: str) -> None:
|
|
152
156
|
"""Log a section header."""
|
|
153
|
-
print()
|
|
154
|
-
print(f"{
|
|
155
|
-
print(
|
|
156
|
-
print(f"{Colors.BLUE}{'=' * 60}{Colors.NC}")
|
|
157
|
-
print()
|
|
157
|
+
console.print()
|
|
158
|
+
console.print(f"[bold blue]{message}[/bold blue]")
|
|
159
|
+
console.print()
|
|
158
160
|
|
|
159
|
-
def log_success(self, message: str):
|
|
161
|
+
def log_success(self, message: str) -> None:
|
|
160
162
|
"""Log a success message."""
|
|
161
|
-
print(f"
|
|
163
|
+
console.print(f"[green]✅ {message}[/green]")
|
|
162
164
|
|
|
163
|
-
def log_error(self, message: str):
|
|
165
|
+
def log_error(self, message: str) -> None:
|
|
164
166
|
"""Log an error message."""
|
|
165
|
-
print(f"
|
|
167
|
+
console.print(f"[red]❌ {message}[/red]")
|
|
166
168
|
|
|
167
|
-
def log_warning(self, message: str):
|
|
169
|
+
def log_warning(self, message: str) -> None:
|
|
168
170
|
"""Log a warning message."""
|
|
169
|
-
print(f"
|
|
171
|
+
console.print(f"[yellow]⚠️ {message}[/yellow]")
|
|
170
172
|
|
|
171
|
-
def log_info(self, message: str):
|
|
173
|
+
def log_info(self, message: str) -> None:
|
|
172
174
|
"""Log an info message."""
|
|
173
|
-
print(f"ℹ️ {message}")
|
|
175
|
+
console.print(f"[cyan]ℹ️ {message}[/cyan]")
|
|
174
176
|
|
|
175
177
|
def run_command(
|
|
176
178
|
self,
|
|
177
|
-
cmd:
|
|
179
|
+
cmd: list[str],
|
|
178
180
|
description: str,
|
|
179
|
-
env:
|
|
180
|
-
check: bool = True
|
|
181
|
+
env: dict[str, str] | None = None,
|
|
182
|
+
check: bool = True,
|
|
181
183
|
) -> subprocess.CompletedProcess:
|
|
182
184
|
"""Run a command with optional dry-run mode."""
|
|
183
185
|
if self.dry_run:
|
|
@@ -192,16 +194,17 @@ class Deployer:
|
|
|
192
194
|
env=env or os.environ.copy(),
|
|
193
195
|
check=check,
|
|
194
196
|
capture_output=True,
|
|
195
|
-
text=True
|
|
197
|
+
text=True,
|
|
196
198
|
)
|
|
197
199
|
return result
|
|
198
200
|
except subprocess.CalledProcessError as e:
|
|
199
201
|
self.log_error(f"Command failed: {' '.join(cmd)}")
|
|
200
202
|
if e.stderr:
|
|
201
|
-
|
|
203
|
+
# Print error to stderr using console (Rich supports stderr output)
|
|
204
|
+
console.print(e.stderr, style="red")
|
|
202
205
|
raise
|
|
203
206
|
|
|
204
|
-
def run_hook(self, hook_commands:
|
|
207
|
+
def run_hook(self, hook_commands: list[str], hook_name: str) -> None:
|
|
205
208
|
"""Run custom hook commands."""
|
|
206
209
|
if not hook_commands:
|
|
207
210
|
return
|
|
@@ -211,13 +214,9 @@ class Deployer:
|
|
|
211
214
|
# Replace placeholders
|
|
212
215
|
cmd = cmd.format(
|
|
213
216
|
version=self.version,
|
|
214
|
-
package=self.config.pypi_package or self.config.project_name
|
|
215
|
-
)
|
|
216
|
-
self.run_command(
|
|
217
|
-
cmd.split(),
|
|
218
|
-
f"Hook: {cmd}",
|
|
219
|
-
check=False
|
|
217
|
+
package=self.config.pypi_package or self.config.project_name,
|
|
220
218
|
)
|
|
219
|
+
self.run_command(cmd.split(), f"Hook: {cmd}", check=False)
|
|
221
220
|
|
|
222
221
|
def should_run_step(self, step: str) -> bool:
|
|
223
222
|
"""Check if a step should be run based on filters."""
|
|
@@ -227,32 +226,32 @@ class Deployer:
|
|
|
227
226
|
return False
|
|
228
227
|
return True
|
|
229
228
|
|
|
230
|
-
def _step_git_push(self):
|
|
229
|
+
def _step_git_push(self) -> None:
|
|
231
230
|
"""Push to git remote."""
|
|
232
231
|
self.log_section("Step 1: Git Push")
|
|
233
232
|
|
|
234
233
|
# Check for uncommitted changes
|
|
235
234
|
result = subprocess.run(
|
|
236
|
-
[
|
|
237
|
-
capture_output=True
|
|
235
|
+
["git", "diff-index", "--quiet", "HEAD", "--"], capture_output=True
|
|
238
236
|
)
|
|
239
237
|
|
|
240
238
|
if result.returncode != 0:
|
|
241
239
|
self.log_warning("You have uncommitted changes")
|
|
242
240
|
if not self.dry_run:
|
|
243
|
-
|
|
244
|
-
if response.lower() != 'y':
|
|
241
|
+
if not Confirm.ask("Continue anyway?", default=False):
|
|
245
242
|
sys.exit(1)
|
|
246
243
|
|
|
247
244
|
# Push to remote
|
|
248
|
-
cmd = [
|
|
245
|
+
cmd = ["git", "push", self.config.git_remote, self.config.git_branch]
|
|
249
246
|
if self.config.git_push_tags:
|
|
250
|
-
cmd.append(
|
|
247
|
+
cmd.append("--tags")
|
|
251
248
|
|
|
252
|
-
self.run_command(
|
|
249
|
+
self.run_command(
|
|
250
|
+
cmd, f"Pushing to {self.config.git_remote}/{self.config.git_branch}..."
|
|
251
|
+
)
|
|
253
252
|
self.log_success("Git push complete")
|
|
254
253
|
|
|
255
|
-
def _step_build(self):
|
|
254
|
+
def _step_build(self) -> None:
|
|
256
255
|
"""Build the package."""
|
|
257
256
|
self.log_section("Step 2: Build Package")
|
|
258
257
|
|
|
@@ -282,9 +281,9 @@ class Deployer:
|
|
|
282
281
|
if dist_dir.exists():
|
|
283
282
|
self.log_info("Build artifacts:")
|
|
284
283
|
for file in dist_dir.iterdir():
|
|
285
|
-
print(f" - {file.name}")
|
|
284
|
+
console.print(f"[dim] - {file.name}[/dim]")
|
|
286
285
|
|
|
287
|
-
def _step_pypi_publish(self):
|
|
286
|
+
def _step_pypi_publish(self) -> None:
|
|
288
287
|
"""Publish to PyPI."""
|
|
289
288
|
self.log_section("Step 3: Publish to PyPI")
|
|
290
289
|
|
|
@@ -303,12 +302,12 @@ class Deployer:
|
|
|
303
302
|
with open(env_file) as f:
|
|
304
303
|
for line in f:
|
|
305
304
|
line = line.strip()
|
|
306
|
-
if line and not line.startswith(
|
|
307
|
-
key, val = line.split(
|
|
305
|
+
if line and not line.startswith("#") and "=" in line:
|
|
306
|
+
key, val = line.split("=", 1)
|
|
308
307
|
key = key.strip()
|
|
309
308
|
val = val.strip().strip('"').strip("'")
|
|
310
309
|
if key == token_var:
|
|
311
|
-
env[
|
|
310
|
+
env["UV_PUBLISH_TOKEN"] = val
|
|
312
311
|
break
|
|
313
312
|
|
|
314
313
|
# Check for token
|
|
@@ -316,32 +315,37 @@ class Deployer:
|
|
|
316
315
|
env["UV_PUBLISH_TOKEN"] = env[token_var]
|
|
317
316
|
|
|
318
317
|
if "UV_PUBLISH_TOKEN" not in env:
|
|
319
|
-
self.log_warning(
|
|
318
|
+
self.log_warning(
|
|
319
|
+
f"PyPI token not found (looking for {token_var} or UV_PUBLISH_TOKEN)"
|
|
320
|
+
)
|
|
320
321
|
if not self.dry_run:
|
|
321
|
-
|
|
322
|
-
if response.lower() != 'y':
|
|
322
|
+
if not Confirm.ask("Continue anyway?", default=False):
|
|
323
323
|
sys.exit(1)
|
|
324
324
|
|
|
325
325
|
# Publish
|
|
326
326
|
package_name = self.config.pypi_package or self.config.project_name
|
|
327
327
|
self.run_command(
|
|
328
|
-
[
|
|
328
|
+
["uv", "publish"],
|
|
329
329
|
f"Publishing {package_name} {self.version} to PyPI...",
|
|
330
|
-
env=env
|
|
330
|
+
env=env,
|
|
331
331
|
)
|
|
332
332
|
|
|
333
333
|
# Wait for PyPI to process
|
|
334
334
|
if not self.dry_run and self.config.pypi_wait_after_publish > 0:
|
|
335
|
-
self.log_info(
|
|
335
|
+
self.log_info(
|
|
336
|
+
f"Waiting {self.config.pypi_wait_after_publish}s for PyPI to process..."
|
|
337
|
+
)
|
|
336
338
|
time.sleep(self.config.pypi_wait_after_publish)
|
|
337
339
|
|
|
338
340
|
# Run post-publish hooks
|
|
339
341
|
self.run_hook(self.config.post_publish_hooks, "post-publish")
|
|
340
342
|
|
|
341
343
|
self.log_success("Published to PyPI")
|
|
342
|
-
self.log_info(
|
|
344
|
+
self.log_info(
|
|
345
|
+
f"View at: https://pypi.org/project/{package_name}/{self.version}/"
|
|
346
|
+
)
|
|
343
347
|
|
|
344
|
-
def _step_local_install(self):
|
|
348
|
+
def _step_local_install(self) -> None:
|
|
345
349
|
"""Install package locally."""
|
|
346
350
|
self.log_section("Step 4: Install Locally")
|
|
347
351
|
|
|
@@ -350,34 +354,47 @@ class Deployer:
|
|
|
350
354
|
# Try install
|
|
351
355
|
try:
|
|
352
356
|
self.run_command(
|
|
353
|
-
[
|
|
354
|
-
f"Installing {package_name}=={self.version}..."
|
|
357
|
+
["pip", "install", "--upgrade", f"{package_name}=={self.version}"],
|
|
358
|
+
f"Installing {package_name}=={self.version}...",
|
|
355
359
|
)
|
|
356
360
|
except subprocess.CalledProcessError:
|
|
357
361
|
self.log_warning("Install failed, trying with --force-reinstall...")
|
|
358
362
|
self.run_command(
|
|
359
|
-
[
|
|
360
|
-
|
|
363
|
+
[
|
|
364
|
+
"pip",
|
|
365
|
+
"install",
|
|
366
|
+
"--force-reinstall",
|
|
367
|
+
f"{package_name}=={self.version}",
|
|
368
|
+
],
|
|
369
|
+
"Force reinstalling...",
|
|
361
370
|
)
|
|
362
371
|
|
|
363
372
|
# Verify installation
|
|
364
373
|
if not self.dry_run:
|
|
365
374
|
try:
|
|
366
375
|
result = subprocess.run(
|
|
367
|
-
[
|
|
376
|
+
[
|
|
377
|
+
"python",
|
|
378
|
+
"-c",
|
|
379
|
+
f"import {package_name.replace('-', '_')}; print({package_name.replace('-', '_')}.__version__)",
|
|
380
|
+
],
|
|
368
381
|
capture_output=True,
|
|
369
382
|
text=True,
|
|
370
|
-
check=True
|
|
383
|
+
check=True,
|
|
371
384
|
)
|
|
372
385
|
installed_version = result.stdout.strip()
|
|
373
386
|
if installed_version == self.version:
|
|
374
|
-
self.log_success(
|
|
387
|
+
self.log_success(
|
|
388
|
+
f"Verified: {package_name} {installed_version} is installed"
|
|
389
|
+
)
|
|
375
390
|
else:
|
|
376
|
-
self.log_warning(
|
|
391
|
+
self.log_warning(
|
|
392
|
+
f"Installed version ({installed_version}) doesn't match expected ({self.version})"
|
|
393
|
+
)
|
|
377
394
|
except Exception as e:
|
|
378
395
|
self.log_warning(f"Could not verify installation: {e}")
|
|
379
396
|
|
|
380
|
-
def _step_update_plugins(self):
|
|
397
|
+
def _step_update_plugins(self) -> None:
|
|
381
398
|
"""Update plugins (Claude, Gemini, etc.)."""
|
|
382
399
|
self.log_section("Step 5: Update Plugins")
|
|
383
400
|
|
|
@@ -389,10 +406,7 @@ class Deployer:
|
|
|
389
406
|
|
|
390
407
|
for plugin_name, plugin_cmd in self.config.plugins.items():
|
|
391
408
|
# Replace placeholders
|
|
392
|
-
cmd = plugin_cmd.format(
|
|
393
|
-
package=package_name,
|
|
394
|
-
version=self.version
|
|
395
|
-
)
|
|
409
|
+
cmd = plugin_cmd.format(package=package_name, version=self.version)
|
|
396
410
|
|
|
397
411
|
# Check if command exists
|
|
398
412
|
cmd_parts = cmd.split()
|
|
@@ -403,15 +417,13 @@ class Deployer:
|
|
|
403
417
|
# Run update command
|
|
404
418
|
try:
|
|
405
419
|
self.run_command(
|
|
406
|
-
cmd_parts,
|
|
407
|
-
f"Updating {plugin_name} plugin...",
|
|
408
|
-
check=False
|
|
420
|
+
cmd_parts, f"Updating {plugin_name} plugin...", check=False
|
|
409
421
|
)
|
|
410
422
|
self.log_success(f"{plugin_name} plugin updated")
|
|
411
423
|
except Exception as e:
|
|
412
424
|
self.log_warning(f"{plugin_name} update failed: {e}")
|
|
413
425
|
|
|
414
|
-
def deploy(self):
|
|
426
|
+
def deploy(self) -> None:
|
|
415
427
|
"""Run the full deployment process."""
|
|
416
428
|
self.log_section(f"Deployment - {self.config.project_name} v{self.version}")
|
|
417
429
|
|
|
@@ -446,10 +458,10 @@ class Deployer:
|
|
|
446
458
|
# Summary
|
|
447
459
|
self.log_section("Deployment Complete! 🎉")
|
|
448
460
|
self.log_success("All deployment steps completed successfully!")
|
|
449
|
-
print()
|
|
461
|
+
console.print()
|
|
450
462
|
|
|
451
463
|
|
|
452
|
-
def create_deployment_config_template(output_path: Path):
|
|
464
|
+
def create_deployment_config_template(output_path: Path) -> None:
|
|
453
465
|
"""Create a template deployment configuration file."""
|
|
454
466
|
|
|
455
467
|
template = """# HtmlGraph Deployment Configuration
|
|
@@ -508,7 +520,12 @@ post_publish = []
|
|
|
508
520
|
"""
|
|
509
521
|
|
|
510
522
|
output_path.write_text(template)
|
|
511
|
-
print(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
print(
|
|
523
|
+
console.print(
|
|
524
|
+
f"[green]✅ Created deployment config template: {output_path}[/green]"
|
|
525
|
+
)
|
|
526
|
+
console.print()
|
|
527
|
+
console.print("[bold cyan]Next steps:[/bold cyan]")
|
|
528
|
+
console.print(
|
|
529
|
+
"[dim]1. Edit htmlgraph-deploy.toml to customize your deployment[/dim]"
|
|
530
|
+
)
|
|
531
|
+
console.print("[dim]2. Run: htmlgraph deploy run[/dim]")
|