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.
Files changed (331) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +173 -17
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +127 -0
  7. htmlgraph/agent_registry.py +45 -30
  8. htmlgraph/agents.py +160 -107
  9. htmlgraph/analytics/__init__.py +9 -2
  10. htmlgraph/analytics/cli.py +190 -51
  11. htmlgraph/analytics/cost_analyzer.py +391 -0
  12. htmlgraph/analytics/cost_monitor.py +664 -0
  13. htmlgraph/analytics/cost_reporter.py +675 -0
  14. htmlgraph/analytics/cross_session.py +617 -0
  15. htmlgraph/analytics/dependency.py +192 -100
  16. htmlgraph/analytics/pattern_learning.py +771 -0
  17. htmlgraph/analytics/session_graph.py +707 -0
  18. htmlgraph/analytics/strategic/__init__.py +80 -0
  19. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  20. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  21. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  22. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  23. htmlgraph/analytics/work_type.py +190 -14
  24. htmlgraph/analytics_index.py +135 -51
  25. htmlgraph/api/__init__.py +3 -0
  26. htmlgraph/api/cost_alerts_websocket.py +416 -0
  27. htmlgraph/api/main.py +2498 -0
  28. htmlgraph/api/static/htmx.min.js +1 -0
  29. htmlgraph/api/static/style-redesign.css +1344 -0
  30. htmlgraph/api/static/style.css +1079 -0
  31. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  32. htmlgraph/api/templates/dashboard.html +794 -0
  33. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  34. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  35. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  36. htmlgraph/api/templates/partials/agents.html +317 -0
  37. htmlgraph/api/templates/partials/event-traces.html +373 -0
  38. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  39. htmlgraph/api/templates/partials/features.html +578 -0
  40. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  41. htmlgraph/api/templates/partials/metrics.html +346 -0
  42. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  43. htmlgraph/api/templates/partials/orchestration.html +198 -0
  44. htmlgraph/api/templates/partials/spawners.html +375 -0
  45. htmlgraph/api/templates/partials/work-items.html +613 -0
  46. htmlgraph/api/websocket.py +538 -0
  47. htmlgraph/archive/__init__.py +24 -0
  48. htmlgraph/archive/bloom.py +234 -0
  49. htmlgraph/archive/fts.py +297 -0
  50. htmlgraph/archive/manager.py +583 -0
  51. htmlgraph/archive/search.py +244 -0
  52. htmlgraph/atomic_ops.py +560 -0
  53. htmlgraph/attribute_index.py +208 -0
  54. htmlgraph/bounded_paths.py +539 -0
  55. htmlgraph/builders/__init__.py +14 -0
  56. htmlgraph/builders/base.py +118 -29
  57. htmlgraph/builders/bug.py +150 -0
  58. htmlgraph/builders/chore.py +119 -0
  59. htmlgraph/builders/epic.py +150 -0
  60. htmlgraph/builders/feature.py +31 -6
  61. htmlgraph/builders/insight.py +195 -0
  62. htmlgraph/builders/metric.py +217 -0
  63. htmlgraph/builders/pattern.py +202 -0
  64. htmlgraph/builders/phase.py +162 -0
  65. htmlgraph/builders/spike.py +52 -19
  66. htmlgraph/builders/track.py +148 -72
  67. htmlgraph/cigs/__init__.py +81 -0
  68. htmlgraph/cigs/autonomy.py +385 -0
  69. htmlgraph/cigs/cost.py +475 -0
  70. htmlgraph/cigs/messages_basic.py +472 -0
  71. htmlgraph/cigs/messaging.py +365 -0
  72. htmlgraph/cigs/models.py +771 -0
  73. htmlgraph/cigs/pattern_storage.py +427 -0
  74. htmlgraph/cigs/patterns.py +503 -0
  75. htmlgraph/cigs/posttool_analyzer.py +234 -0
  76. htmlgraph/cigs/reporter.py +818 -0
  77. htmlgraph/cigs/tracker.py +317 -0
  78. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  79. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  80. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  81. htmlgraph/cli/__init__.py +42 -0
  82. htmlgraph/cli/__main__.py +6 -0
  83. htmlgraph/cli/analytics.py +1424 -0
  84. htmlgraph/cli/base.py +685 -0
  85. htmlgraph/cli/constants.py +206 -0
  86. htmlgraph/cli/core.py +954 -0
  87. htmlgraph/cli/main.py +147 -0
  88. htmlgraph/cli/models.py +475 -0
  89. htmlgraph/cli/templates/__init__.py +1 -0
  90. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  91. htmlgraph/cli/work/__init__.py +239 -0
  92. htmlgraph/cli/work/browse.py +115 -0
  93. htmlgraph/cli/work/features.py +568 -0
  94. htmlgraph/cli/work/orchestration.py +676 -0
  95. htmlgraph/cli/work/report.py +728 -0
  96. htmlgraph/cli/work/sessions.py +466 -0
  97. htmlgraph/cli/work/snapshot.py +559 -0
  98. htmlgraph/cli/work/tracks.py +486 -0
  99. htmlgraph/cli_commands/__init__.py +1 -0
  100. htmlgraph/cli_commands/feature.py +195 -0
  101. htmlgraph/cli_framework.py +115 -0
  102. htmlgraph/collections/__init__.py +18 -0
  103. htmlgraph/collections/base.py +415 -98
  104. htmlgraph/collections/bug.py +53 -0
  105. htmlgraph/collections/chore.py +53 -0
  106. htmlgraph/collections/epic.py +53 -0
  107. htmlgraph/collections/feature.py +12 -26
  108. htmlgraph/collections/insight.py +100 -0
  109. htmlgraph/collections/metric.py +92 -0
  110. htmlgraph/collections/pattern.py +97 -0
  111. htmlgraph/collections/phase.py +53 -0
  112. htmlgraph/collections/session.py +194 -0
  113. htmlgraph/collections/spike.py +56 -16
  114. htmlgraph/collections/task_delegation.py +241 -0
  115. htmlgraph/collections/todo.py +511 -0
  116. htmlgraph/collections/traces.py +487 -0
  117. htmlgraph/config/cost_models.json +56 -0
  118. htmlgraph/config.py +190 -0
  119. htmlgraph/context_analytics.py +344 -0
  120. htmlgraph/converter.py +216 -28
  121. htmlgraph/cost_analysis/__init__.py +5 -0
  122. htmlgraph/cost_analysis/analyzer.py +438 -0
  123. htmlgraph/dashboard.html +2406 -307
  124. htmlgraph/dashboard.html.backup +6592 -0
  125. htmlgraph/dashboard.html.bak +7181 -0
  126. htmlgraph/dashboard.html.bak2 +7231 -0
  127. htmlgraph/dashboard.html.bak3 +7232 -0
  128. htmlgraph/db/__init__.py +38 -0
  129. htmlgraph/db/queries.py +790 -0
  130. htmlgraph/db/schema.py +1788 -0
  131. htmlgraph/decorators.py +317 -0
  132. htmlgraph/dependency_models.py +19 -2
  133. htmlgraph/deploy.py +142 -125
  134. htmlgraph/deployment_models.py +474 -0
  135. htmlgraph/docs/API_REFERENCE.md +841 -0
  136. htmlgraph/docs/HTTP_API.md +750 -0
  137. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  138. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  139. htmlgraph/docs/README.md +532 -0
  140. htmlgraph/docs/__init__.py +77 -0
  141. htmlgraph/docs/docs_version.py +55 -0
  142. htmlgraph/docs/metadata.py +93 -0
  143. htmlgraph/docs/migrations.py +232 -0
  144. htmlgraph/docs/template_engine.py +143 -0
  145. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  146. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  147. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  148. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  149. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  150. htmlgraph/docs/version_check.py +163 -0
  151. htmlgraph/edge_index.py +182 -27
  152. htmlgraph/error_handler.py +544 -0
  153. htmlgraph/event_log.py +100 -52
  154. htmlgraph/event_migration.py +13 -4
  155. htmlgraph/exceptions.py +49 -0
  156. htmlgraph/file_watcher.py +101 -28
  157. htmlgraph/find_api.py +75 -63
  158. htmlgraph/git_events.py +145 -63
  159. htmlgraph/graph.py +1122 -106
  160. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  161. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  162. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  163. htmlgraph/hooks/__init__.py +45 -0
  164. htmlgraph/hooks/bootstrap.py +169 -0
  165. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  166. htmlgraph/hooks/concurrent_sessions.py +208 -0
  167. htmlgraph/hooks/context.py +350 -0
  168. htmlgraph/hooks/drift_handler.py +525 -0
  169. htmlgraph/hooks/event_tracker.py +1314 -0
  170. htmlgraph/hooks/git_commands.py +175 -0
  171. htmlgraph/hooks/hooks-config.example.json +12 -0
  172. htmlgraph/hooks/installer.py +343 -0
  173. htmlgraph/hooks/orchestrator.py +674 -0
  174. htmlgraph/hooks/orchestrator_reflector.py +223 -0
  175. htmlgraph/hooks/post-checkout.sh +28 -0
  176. htmlgraph/hooks/post-commit.sh +24 -0
  177. htmlgraph/hooks/post-merge.sh +26 -0
  178. htmlgraph/hooks/post_tool_use_failure.py +273 -0
  179. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  180. htmlgraph/hooks/posttooluse.py +408 -0
  181. htmlgraph/hooks/pre-commit.sh +94 -0
  182. htmlgraph/hooks/pre-push.sh +28 -0
  183. htmlgraph/hooks/pretooluse.py +819 -0
  184. htmlgraph/hooks/prompt_analyzer.py +637 -0
  185. htmlgraph/hooks/session_handler.py +668 -0
  186. htmlgraph/hooks/session_summary.py +395 -0
  187. htmlgraph/hooks/state_manager.py +504 -0
  188. htmlgraph/hooks/subagent_detection.py +202 -0
  189. htmlgraph/hooks/subagent_stop.py +369 -0
  190. htmlgraph/hooks/task_enforcer.py +255 -0
  191. htmlgraph/hooks/task_validator.py +177 -0
  192. htmlgraph/hooks/validator.py +628 -0
  193. htmlgraph/ids.py +41 -27
  194. htmlgraph/index.d.ts +286 -0
  195. htmlgraph/learning.py +767 -0
  196. htmlgraph/mcp_server.py +69 -23
  197. htmlgraph/models.py +1586 -87
  198. htmlgraph/operations/README.md +62 -0
  199. htmlgraph/operations/__init__.py +79 -0
  200. htmlgraph/operations/analytics.py +339 -0
  201. htmlgraph/operations/bootstrap.py +289 -0
  202. htmlgraph/operations/events.py +244 -0
  203. htmlgraph/operations/fastapi_server.py +231 -0
  204. htmlgraph/operations/hooks.py +350 -0
  205. htmlgraph/operations/initialization.py +597 -0
  206. htmlgraph/operations/initialization.py.backup +228 -0
  207. htmlgraph/operations/server.py +303 -0
  208. htmlgraph/orchestration/__init__.py +58 -0
  209. htmlgraph/orchestration/claude_launcher.py +179 -0
  210. htmlgraph/orchestration/command_builder.py +72 -0
  211. htmlgraph/orchestration/headless_spawner.py +281 -0
  212. htmlgraph/orchestration/live_events.py +377 -0
  213. htmlgraph/orchestration/model_selection.py +327 -0
  214. htmlgraph/orchestration/plugin_manager.py +140 -0
  215. htmlgraph/orchestration/prompts.py +137 -0
  216. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  217. htmlgraph/orchestration/spawners/__init__.py +16 -0
  218. htmlgraph/orchestration/spawners/base.py +194 -0
  219. htmlgraph/orchestration/spawners/claude.py +173 -0
  220. htmlgraph/orchestration/spawners/codex.py +435 -0
  221. htmlgraph/orchestration/spawners/copilot.py +294 -0
  222. htmlgraph/orchestration/spawners/gemini.py +471 -0
  223. htmlgraph/orchestration/subprocess_runner.py +36 -0
  224. htmlgraph/orchestration/task_coordination.py +343 -0
  225. htmlgraph/orchestration.md +563 -0
  226. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  227. htmlgraph/orchestrator.py +669 -0
  228. htmlgraph/orchestrator_config.py +357 -0
  229. htmlgraph/orchestrator_mode.py +328 -0
  230. htmlgraph/orchestrator_validator.py +133 -0
  231. htmlgraph/parallel.py +646 -0
  232. htmlgraph/parser.py +160 -35
  233. htmlgraph/path_query.py +608 -0
  234. htmlgraph/pattern_matcher.py +636 -0
  235. htmlgraph/planning.py +147 -52
  236. htmlgraph/pydantic_models.py +476 -0
  237. htmlgraph/quality_gates.py +350 -0
  238. htmlgraph/query_builder.py +109 -72
  239. htmlgraph/query_composer.py +509 -0
  240. htmlgraph/reflection.py +443 -0
  241. htmlgraph/refs.py +344 -0
  242. htmlgraph/repo_hash.py +512 -0
  243. htmlgraph/repositories/__init__.py +292 -0
  244. htmlgraph/repositories/analytics_repository.py +455 -0
  245. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  246. htmlgraph/repositories/feature_repository.py +581 -0
  247. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  248. htmlgraph/repositories/feature_repository_memory.py +607 -0
  249. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  250. htmlgraph/repositories/filter_service.py +620 -0
  251. htmlgraph/repositories/filter_service_standard.py +445 -0
  252. htmlgraph/repositories/shared_cache.py +621 -0
  253. htmlgraph/repositories/shared_cache_memory.py +395 -0
  254. htmlgraph/repositories/track_repository.py +552 -0
  255. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  256. htmlgraph/repositories/track_repository_memory.py +508 -0
  257. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  258. htmlgraph/routing.py +8 -19
  259. htmlgraph/scripts/deploy.py +1 -2
  260. htmlgraph/sdk/__init__.py +398 -0
  261. htmlgraph/sdk/__init__.pyi +14 -0
  262. htmlgraph/sdk/analytics/__init__.py +19 -0
  263. htmlgraph/sdk/analytics/engine.py +155 -0
  264. htmlgraph/sdk/analytics/helpers.py +178 -0
  265. htmlgraph/sdk/analytics/registry.py +109 -0
  266. htmlgraph/sdk/base.py +484 -0
  267. htmlgraph/sdk/constants.py +216 -0
  268. htmlgraph/sdk/core.pyi +308 -0
  269. htmlgraph/sdk/discovery.py +120 -0
  270. htmlgraph/sdk/help/__init__.py +12 -0
  271. htmlgraph/sdk/help/mixin.py +699 -0
  272. htmlgraph/sdk/mixins/__init__.py +15 -0
  273. htmlgraph/sdk/mixins/attribution.py +113 -0
  274. htmlgraph/sdk/mixins/mixin.py +410 -0
  275. htmlgraph/sdk/operations/__init__.py +12 -0
  276. htmlgraph/sdk/operations/mixin.py +427 -0
  277. htmlgraph/sdk/orchestration/__init__.py +17 -0
  278. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  279. htmlgraph/sdk/orchestration/spawner.py +204 -0
  280. htmlgraph/sdk/planning/__init__.py +19 -0
  281. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  282. htmlgraph/sdk/planning/mixin.py +211 -0
  283. htmlgraph/sdk/planning/parallel.py +186 -0
  284. htmlgraph/sdk/planning/queue.py +210 -0
  285. htmlgraph/sdk/planning/recommendations.py +87 -0
  286. htmlgraph/sdk/planning/smart_planning.py +319 -0
  287. htmlgraph/sdk/session/__init__.py +19 -0
  288. htmlgraph/sdk/session/continuity.py +57 -0
  289. htmlgraph/sdk/session/handoff.py +110 -0
  290. htmlgraph/sdk/session/info.py +309 -0
  291. htmlgraph/sdk/session/manager.py +103 -0
  292. htmlgraph/sdk/strategic/__init__.py +26 -0
  293. htmlgraph/sdk/strategic/mixin.py +563 -0
  294. htmlgraph/server.py +685 -180
  295. htmlgraph/services/__init__.py +10 -0
  296. htmlgraph/services/claiming.py +199 -0
  297. htmlgraph/session_hooks.py +300 -0
  298. htmlgraph/session_manager.py +1392 -175
  299. htmlgraph/session_registry.py +587 -0
  300. htmlgraph/session_state.py +436 -0
  301. htmlgraph/session_warning.py +201 -0
  302. htmlgraph/sessions/__init__.py +23 -0
  303. htmlgraph/sessions/handoff.py +756 -0
  304. htmlgraph/setup.py +34 -17
  305. htmlgraph/spike_index.py +143 -0
  306. htmlgraph/sync_docs.py +12 -15
  307. htmlgraph/system_prompts.py +450 -0
  308. htmlgraph/templates/AGENTS.md.template +366 -0
  309. htmlgraph/templates/CLAUDE.md.template +97 -0
  310. htmlgraph/templates/GEMINI.md.template +87 -0
  311. htmlgraph/templates/orchestration-view.html +350 -0
  312. htmlgraph/track_builder.py +146 -15
  313. htmlgraph/track_manager.py +69 -21
  314. htmlgraph/transcript.py +890 -0
  315. htmlgraph/transcript_analytics.py +699 -0
  316. htmlgraph/types.py +323 -0
  317. htmlgraph/validation.py +115 -0
  318. htmlgraph/watch.py +8 -5
  319. htmlgraph/work_type_utils.py +3 -2
  320. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
  321. htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
  322. htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
  323. htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
  324. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
  325. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  326. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  327. htmlgraph/cli.py +0 -2688
  328. htmlgraph/sdk.py +0 -709
  329. htmlgraph-0.9.3.dist-info/RECORD +0 -61
  330. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  331. {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 sys
10
+ import shutil
11
11
  import subprocess
12
+ import sys
12
13
  import time
13
- import shutil
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
- # ANSI color codes
20
- class Colors:
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: Optional[str] = None
32
+ pypi_package: str | None = None
35
33
 
36
34
  # Deployment steps
37
- steps: List[str] = field(default_factory=lambda: [
38
- "git-push",
39
- "build",
40
- "pypi-publish",
41
- "local-install",
42
- "update-plugins"
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: Dict[str, str] = field(default_factory=dict)
59
+ plugins: dict[str, str] = field(default_factory=dict)
60
60
 
61
61
  # Custom 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)
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) -> 'DeploymentConfig':
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, 'rb') as f:
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('project', {})
80
- deployment = data.get('deployment', {})
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('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('token_env_var', 'PyPI_API_TOKEN'),
101
- pypi_wait_after_publish=deployment.get('pypi', {}).get('wait_after_publish', 10),
102
- plugins=deployment.get('plugins', {}),
103
- pre_build_hooks=deployment.get('hooks', {}).get('pre_build', []),
104
- post_build_hooks=deployment.get('hooks', {}).get('post_build', []),
105
- pre_publish_hooks=deployment.get('hooks', {}).get('pre_publish', []),
106
- post_publish_hooks=deployment.get('hooks', {}).get('post_publish', []),
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: Optional[List[str]] = None,
118
- only_steps: Optional[List[str]] = None,
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: Dict[str, Callable] = {
128
- 'git-push': self._step_git_push,
129
- 'build': self._step_build,
130
- 'pypi-publish': self._step_pypi_publish,
131
- 'local-install': self._step_local_install,
132
- 'update-plugins': self._step_update_plugins,
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, 'rb') as f:
150
+ with open(pyproject, "rb") as f:
147
151
  data = tomllib.load(f)
148
152
 
149
- return data.get('project', {}).get('version', 'unknown')
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"{Colors.BLUE}{'=' * 60}{Colors.NC}")
155
- print(f"{Colors.BLUE}{message}{Colors.NC}")
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"{Colors.GREEN}✅ {message}{Colors.NC}")
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"{Colors.RED}❌ {message}{Colors.NC}")
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"{Colors.YELLOW}⚠️ {message}{Colors.NC}")
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: List[str],
179
+ cmd: list[str],
178
180
  description: str,
179
- env: Optional[Dict[str, str]] = None,
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
- print(e.stderr, file=sys.stderr)
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: List[str], hook_name: str):
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
- ['git', 'diff-index', '--quiet', 'HEAD', '--'],
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
- response = input("Continue anyway? (y/n) ")
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 = ['git', 'push', self.config.git_remote, self.config.git_branch]
245
+ cmd = ["git", "push", self.config.git_remote, self.config.git_branch]
249
246
  if self.config.git_push_tags:
250
- cmd.append('--tags')
247
+ cmd.append("--tags")
251
248
 
252
- self.run_command(cmd, f"Pushing to {self.config.git_remote}/{self.config.git_branch}...")
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('#') and '=' in line:
307
- key, val = line.split('=', 1)
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['UV_PUBLISH_TOKEN'] = val
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(f"PyPI token not found (looking for {token_var} or UV_PUBLISH_TOKEN)")
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
- response = input("Continue anyway? (y/n) ")
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
- ['uv', 'publish'],
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(f"Waiting {self.config.pypi_wait_after_publish}s for PyPI to process...")
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(f"View at: https://pypi.org/project/{package_name}/{self.version}/")
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
- ['pip', 'install', '--upgrade', f'{package_name}=={self.version}'],
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
- ['pip', 'install', '--force-reinstall', f'{package_name}=={self.version}'],
360
- "Force reinstalling..."
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
- ['python', '-c', f'import {package_name.replace("-", "_")}; print({package_name.replace("-", "_")}.__version__)'],
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(f"Verified: {package_name} {installed_version} is installed")
387
+ self.log_success(
388
+ f"Verified: {package_name} {installed_version} is installed"
389
+ )
375
390
  else:
376
- self.log_warning(f"Installed version ({installed_version}) doesn't match expected ({self.version})")
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(f"✅ Created deployment config template: {output_path}")
512
- print("\nNext steps:")
513
- print("1. Edit htmlgraph-deploy.toml to customize your deployment")
514
- print("2. Run: htmlgraph deploy run")
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]")