htmlgraph 0.24.2__py3-none-any.whl → 0.25.0__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 (103) hide show
  1. htmlgraph/__init__.py +20 -1
  2. htmlgraph/agent_detection.py +26 -10
  3. htmlgraph/analytics/cross_session.py +4 -3
  4. htmlgraph/analytics/work_type.py +52 -16
  5. htmlgraph/analytics_index.py +51 -19
  6. htmlgraph/api/__init__.py +3 -0
  7. htmlgraph/api/main.py +2115 -0
  8. htmlgraph/api/static/htmx.min.js +1 -0
  9. htmlgraph/api/static/style-redesign.css +1344 -0
  10. htmlgraph/api/static/style.css +1079 -0
  11. htmlgraph/api/templates/dashboard-redesign.html +812 -0
  12. htmlgraph/api/templates/dashboard.html +783 -0
  13. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  14. htmlgraph/api/templates/partials/activity-feed.html +570 -0
  15. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  16. htmlgraph/api/templates/partials/agents.html +317 -0
  17. htmlgraph/api/templates/partials/event-traces.html +373 -0
  18. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  19. htmlgraph/api/templates/partials/features.html +509 -0
  20. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  21. htmlgraph/api/templates/partials/metrics.html +346 -0
  22. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  23. htmlgraph/api/templates/partials/orchestration.html +163 -0
  24. htmlgraph/api/templates/partials/spawners.html +375 -0
  25. htmlgraph/atomic_ops.py +560 -0
  26. htmlgraph/builders/base.py +55 -1
  27. htmlgraph/builders/bug.py +17 -2
  28. htmlgraph/builders/chore.py +17 -2
  29. htmlgraph/builders/epic.py +17 -2
  30. htmlgraph/builders/feature.py +25 -2
  31. htmlgraph/builders/phase.py +17 -2
  32. htmlgraph/builders/spike.py +27 -2
  33. htmlgraph/builders/track.py +14 -0
  34. htmlgraph/cigs/__init__.py +4 -0
  35. htmlgraph/cigs/reporter.py +818 -0
  36. htmlgraph/cli.py +1427 -401
  37. htmlgraph/cli_commands/__init__.py +1 -0
  38. htmlgraph/cli_commands/feature.py +195 -0
  39. htmlgraph/cli_framework.py +115 -0
  40. htmlgraph/collections/__init__.py +2 -0
  41. htmlgraph/collections/base.py +21 -0
  42. htmlgraph/collections/session.py +189 -0
  43. htmlgraph/collections/spike.py +7 -1
  44. htmlgraph/collections/task_delegation.py +236 -0
  45. htmlgraph/collections/traces.py +482 -0
  46. htmlgraph/config.py +113 -0
  47. htmlgraph/converter.py +41 -0
  48. htmlgraph/cost_analysis/__init__.py +5 -0
  49. htmlgraph/cost_analysis/analyzer.py +438 -0
  50. htmlgraph/dashboard.html +3315 -492
  51. htmlgraph-0.24.2.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
  52. htmlgraph/dashboard.html.bak +7181 -0
  53. htmlgraph/dashboard.html.bak2 +7231 -0
  54. htmlgraph/dashboard.html.bak3 +7232 -0
  55. htmlgraph/db/__init__.py +38 -0
  56. htmlgraph/db/queries.py +790 -0
  57. htmlgraph/db/schema.py +1334 -0
  58. htmlgraph/deploy.py +26 -27
  59. htmlgraph/docs/API_REFERENCE.md +841 -0
  60. htmlgraph/docs/HTTP_API.md +750 -0
  61. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  62. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
  63. htmlgraph/docs/README.md +533 -0
  64. htmlgraph/docs/version_check.py +3 -1
  65. htmlgraph/error_handler.py +544 -0
  66. htmlgraph/event_log.py +2 -0
  67. htmlgraph/hooks/__init__.py +8 -0
  68. htmlgraph/hooks/bootstrap.py +169 -0
  69. htmlgraph/hooks/context.py +271 -0
  70. htmlgraph/hooks/drift_handler.py +521 -0
  71. htmlgraph/hooks/event_tracker.py +405 -15
  72. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  73. htmlgraph/hooks/pretooluse.py +476 -6
  74. htmlgraph/hooks/prompt_analyzer.py +648 -0
  75. htmlgraph/hooks/session_handler.py +583 -0
  76. htmlgraph/hooks/state_manager.py +501 -0
  77. htmlgraph/hooks/subagent_stop.py +309 -0
  78. htmlgraph/hooks/task_enforcer.py +39 -0
  79. htmlgraph/models.py +111 -15
  80. htmlgraph/operations/fastapi_server.py +230 -0
  81. htmlgraph/orchestration/headless_spawner.py +22 -14
  82. htmlgraph/pydantic_models.py +476 -0
  83. htmlgraph/quality_gates.py +350 -0
  84. htmlgraph/repo_hash.py +511 -0
  85. htmlgraph/sdk.py +348 -10
  86. htmlgraph/server.py +194 -0
  87. htmlgraph/session_hooks.py +300 -0
  88. htmlgraph/session_manager.py +131 -1
  89. htmlgraph/session_registry.py +587 -0
  90. htmlgraph/session_state.py +436 -0
  91. htmlgraph/system_prompts.py +449 -0
  92. htmlgraph/templates/orchestration-view.html +350 -0
  93. htmlgraph/track_builder.py +19 -0
  94. htmlgraph/validation.py +115 -0
  95. htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
  96. {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
  97. {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
  98. {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
  99. {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  100. {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  101. {htmlgraph-0.24.2.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  102. {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
  103. {htmlgraph-0.24.2.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,230 @@
1
+ """FastAPI-based server for HtmlGraph dashboard with real-time observability."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from htmlgraph.mcp_server import _resolve_project_dir
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class FastAPIServerHandle:
17
+ """Handle to a running FastAPI server."""
18
+
19
+ url: str
20
+ port: int
21
+ host: str
22
+ server: Any | None = None
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class FastAPIServerStartResult:
27
+ """Result of starting FastAPI server."""
28
+
29
+ handle: FastAPIServerHandle
30
+ warnings: list[str]
31
+ config_used: dict[str, Any]
32
+
33
+
34
+ class FastAPIServerError(RuntimeError):
35
+ """FastAPI server error."""
36
+
37
+ pass
38
+
39
+
40
+ class PortInUseError(FastAPIServerError):
41
+ """Requested port is already in use."""
42
+
43
+ pass
44
+
45
+
46
+ def start_fastapi_server(
47
+ *,
48
+ port: int = 8000,
49
+ host: str = "127.0.0.1",
50
+ db_path: str | None = None,
51
+ auto_port: bool = False,
52
+ reload: bool = False,
53
+ ) -> FastAPIServerStartResult:
54
+ """
55
+ Start FastAPI-based HtmlGraph dashboard server.
56
+
57
+ Args:
58
+ port: Port to listen on (default: 8000)
59
+ host: Host to bind to (default: 127.0.0.1)
60
+ db_path: Path to SQLite database file
61
+ auto_port: Automatically find available port if in use
62
+ reload: Enable auto-reload on file changes (development mode)
63
+
64
+ Returns:
65
+ FastAPIServerStartResult with handle, warnings, and config used
66
+
67
+ Raises:
68
+ PortInUseError: If port is in use and auto_port=False
69
+ FastAPIServerError: If server fails to start
70
+ """
71
+ import uvicorn
72
+
73
+ from htmlgraph.api.main import create_app
74
+
75
+ warnings: list[str] = []
76
+ original_port = port
77
+
78
+ # Default database path - prefer project-local database if available
79
+ if db_path is None:
80
+ # Check for project-local database first
81
+ project_dir = _resolve_project_dir()
82
+ project_db = Path(project_dir) / ".htmlgraph" / "index.sqlite"
83
+ if project_db.exists():
84
+ db_path = str(project_db) # Use project-local database
85
+ else:
86
+ db_path = str(
87
+ Path.home() / ".htmlgraph" / "index.sqlite"
88
+ ) # Fall back to home
89
+
90
+ # Ensure database exists
91
+ db_path_obj = Path(db_path)
92
+ db_path_obj.parent.mkdir(parents=True, exist_ok=True)
93
+
94
+ # Handle auto-port selection
95
+ if auto_port and _check_port_in_use(port, host):
96
+ port = _find_available_port(port + 1)
97
+ warnings.append(f"Port {original_port} is in use, using {port} instead")
98
+
99
+ # Check if port is in use
100
+ if not auto_port and _check_port_in_use(port, host):
101
+ raise PortInUseError(
102
+ f"Port {port} is already in use. Use auto_port=True or choose a different port."
103
+ )
104
+
105
+ # Create FastAPI app
106
+ app = create_app(db_path=db_path)
107
+
108
+ # Create server config
109
+ config = uvicorn.Config(
110
+ app,
111
+ host=host,
112
+ port=port,
113
+ log_level="info",
114
+ reload=reload,
115
+ reload_dirs=None, # Disable file watching for now
116
+ )
117
+
118
+ # Create server instance
119
+ server = uvicorn.Server(config)
120
+
121
+ # Create handle
122
+ handle = FastAPIServerHandle(
123
+ url=f"http://{host}:{port}",
124
+ port=port,
125
+ host=host,
126
+ server=server,
127
+ )
128
+
129
+ # Configuration used
130
+ config_used = {
131
+ "port": port,
132
+ "original_port": original_port,
133
+ "host": host,
134
+ "db_path": db_path,
135
+ "auto_port": auto_port,
136
+ "reload": reload,
137
+ }
138
+
139
+ return FastAPIServerStartResult(
140
+ handle=handle,
141
+ warnings=warnings,
142
+ config_used=config_used,
143
+ )
144
+
145
+
146
+ async def run_fastapi_server(handle: FastAPIServerHandle) -> None:
147
+ """
148
+ Run FastAPI server (async).
149
+
150
+ Args:
151
+ handle: FastAPIServerHandle from start_fastapi_server()
152
+
153
+ Raises:
154
+ FastAPIServerError: If server fails
155
+ """
156
+ if handle.server is None:
157
+ raise FastAPIServerError("Invalid server handle")
158
+
159
+ try:
160
+ await handle.server.serve()
161
+ except Exception as e:
162
+ raise FastAPIServerError(f"Server error: {e}") from e
163
+
164
+
165
+ def stop_fastapi_server(handle: FastAPIServerHandle) -> None:
166
+ """
167
+ Stop FastAPI server.
168
+
169
+ Args:
170
+ handle: FastAPIServerHandle from start_fastapi_server()
171
+
172
+ Raises:
173
+ FastAPIServerError: If shutdown fails
174
+ """
175
+ if handle.server is None:
176
+ return
177
+
178
+ try:
179
+ handle.server.should_exit = True
180
+ except Exception as e:
181
+ raise FastAPIServerError(f"Failed to stop server: {e}") from e
182
+
183
+
184
+ def _check_port_in_use(port: int, host: str = "localhost") -> bool:
185
+ """
186
+ Check if a port is already in use.
187
+
188
+ Args:
189
+ port: Port number to check
190
+ host: Host to check on
191
+
192
+ Returns:
193
+ True if port is in use, False otherwise
194
+ """
195
+ import socket
196
+
197
+ try:
198
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
199
+ s.bind((host, port))
200
+ return False
201
+ except OSError:
202
+ return True
203
+
204
+
205
+ def _find_available_port(start_port: int = 8000, max_attempts: int = 10) -> int:
206
+ """
207
+ Find an available port starting from start_port.
208
+
209
+ Args:
210
+ start_port: Port to start searching from
211
+ max_attempts: Maximum number of ports to try
212
+
213
+ Returns:
214
+ Available port number
215
+
216
+ Raises:
217
+ FastAPIServerError: If no available port found
218
+ """
219
+ import socket
220
+
221
+ for port in range(start_port, start_port + max_attempts):
222
+ try:
223
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
224
+ s.bind(("", port))
225
+ return port
226
+ except OSError:
227
+ continue
228
+ raise FastAPIServerError(
229
+ f"No available ports found in range {start_port}-{start_port + max_attempts}"
230
+ )
@@ -382,14 +382,14 @@ class HeadlessSpawner:
382
382
 
383
383
  Args:
384
384
  prompt: Task description for Gemini
385
- output_format: "json" or "stream-json" (default: "stream-json" for real-time tracking)
386
- model: Model selection (e.g., "gemini-2.0-flash"). Default: None (uses default)
387
- include_directories: List of directories to include for context. Default: None
385
+ output_format: "json" or "stream-json" (enables real-time tracking)
386
+ model: Model selection (e.g., "gemini-2.0-flash"). Default: None
387
+ include_directories: Directories to include for context. Default: None
388
388
  track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
389
389
  timeout: Max seconds to wait
390
390
 
391
391
  Returns:
392
- AIResult with response or error and tracked events if tracking enabled
392
+ AIResult with response, error, and tracked events if tracking enabled
393
393
  """
394
394
  # Initialize tracking if enabled
395
395
  sdk: SDK | None = None
@@ -585,17 +585,17 @@ class HeadlessSpawner:
585
585
 
586
586
  Args:
587
587
  prompt: Task description for Codex
588
- output_json: Use --json flag for JSONL output (enables real-time tracking)
588
+ output_json: JSONL output flag (enables real-time tracking)
589
589
  model: Model selection (e.g., "gpt-4-turbo"). Default: None
590
- sandbox: Sandbox mode ("read-only", "workspace-write", "danger-full-access"). Default: None
591
- full_auto: Enable full auto mode (--full-auto). Default: True (required for headless)
590
+ sandbox: Sandbox mode ("read-only", "workspace-write", or full)
591
+ full_auto: Enable full auto mode. Default: True (required headless)
592
592
  images: List of image paths (--image). Default: None
593
- output_last_message: Write last message to file (--output-last-message). Default: None
594
- output_schema: JSON schema for validation (--output-schema). Default: None
595
- skip_git_check: Skip git repo check (--skip-git-repo-check). Default: False
593
+ output_last_message: Write last message to file. Default: None
594
+ output_schema: JSON schema for validation. Default: None
595
+ skip_git_check: Skip git repo check. Default: False
596
596
  working_directory: Workspace directory (--cd). Default: None
597
597
  use_oss: Use local Ollama provider (--oss). Default: False
598
- bypass_approvals: Dangerously bypass approvals (--dangerously-bypass-approvals-and-sandbox). Default: False
598
+ bypass_approvals: Bypass approval checks. Default: False
599
599
  track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
600
600
  timeout: Max seconds to wait
601
601
 
@@ -794,9 +794,9 @@ class HeadlessSpawner:
794
794
 
795
795
  Args:
796
796
  prompt: Task description for Copilot
797
- allow_tools: List of tools to auto-approve (e.g., ["shell(git)", "write(*.py)"])
798
- allow_all_tools: Auto-approve all tools (--allow-all-tools). Default: False
799
- deny_tools: List of tools to deny (--deny-tool). Default: None
797
+ allow_tools: Tools to auto-approve (e.g., ["shell(git)"])
798
+ allow_all_tools: Auto-approve all tools. Default: False
799
+ deny_tools: Tools to deny (--deny-tool). Default: None
800
800
  track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
801
801
  timeout: Max seconds to wait
802
802
 
@@ -924,6 +924,7 @@ class HeadlessSpawner:
924
924
  resume: str | None = None,
925
925
  verbose: bool = False,
926
926
  timeout: int = 300,
927
+ extra_args: list[str] | None = None,
927
928
  ) -> AIResult:
928
929
  """
929
930
  Spawn Claude in headless mode.
@@ -948,6 +949,7 @@ class HeadlessSpawner:
948
949
  resume: Resume from previous session (--resume). Default: None
949
950
  verbose: Enable verbose output (--verbose). Default: False
950
951
  timeout: Max seconds (default: 300, Claude can be slow with initialization)
952
+ extra_args: Additional arguments to pass to Claude CLI
951
953
 
952
954
  Returns:
953
955
  AIResult with response or error
@@ -975,6 +977,12 @@ class HeadlessSpawner:
975
977
  if verbose:
976
978
  cmd.append("--verbose")
977
979
 
980
+ # Add extra args
981
+ if extra_args:
982
+ cmd.extend(extra_args)
983
+
984
+ # Use -- separator to ensure prompt isn't consumed by variadic args
985
+ cmd.append("--")
978
986
  cmd.append(prompt)
979
987
 
980
988
  try: