htmlgraph 0.24.1__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.
- htmlgraph/__init__.py +20 -1
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/analytics/cross_session.py +4 -3
- htmlgraph/analytics/work_type.py +52 -16
- htmlgraph/analytics_index.py +51 -19
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/main.py +2115 -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 +812 -0
- htmlgraph/api/templates/dashboard.html +783 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +570 -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 +509 -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 +163 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/builders/base.py +55 -1
- htmlgraph/builders/bug.py +17 -2
- htmlgraph/builders/chore.py +17 -2
- htmlgraph/builders/epic.py +17 -2
- htmlgraph/builders/feature.py +25 -2
- htmlgraph/builders/phase.py +17 -2
- htmlgraph/builders/spike.py +27 -2
- htmlgraph/builders/track.py +14 -0
- htmlgraph/cigs/__init__.py +4 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cli.py +1427 -401
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +2 -0
- htmlgraph/collections/base.py +21 -0
- htmlgraph/collections/session.py +189 -0
- htmlgraph/collections/spike.py +7 -1
- htmlgraph/collections/task_delegation.py +236 -0
- htmlgraph/collections/traces.py +482 -0
- htmlgraph/config.py +113 -0
- htmlgraph/converter.py +41 -0
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +3315 -492
- htmlgraph-0.24.1.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
- 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 +1334 -0
- htmlgraph/deploy.py +26 -27
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
- htmlgraph/docs/README.md +533 -0
- htmlgraph/docs/version_check.py +3 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +2 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/context.py +271 -0
- htmlgraph/hooks/drift_handler.py +521 -0
- htmlgraph/hooks/event_tracker.py +405 -15
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/pretooluse.py +476 -6
- htmlgraph/hooks/prompt_analyzer.py +648 -0
- htmlgraph/hooks/session_handler.py +583 -0
- htmlgraph/hooks/state_manager.py +501 -0
- htmlgraph/hooks/subagent_stop.py +309 -0
- htmlgraph/hooks/task_enforcer.py +39 -0
- htmlgraph/models.py +111 -15
- htmlgraph/operations/fastapi_server.py +230 -0
- htmlgraph/orchestration/headless_spawner.py +22 -14
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/repo_hash.py +511 -0
- htmlgraph/sdk.py +348 -10
- htmlgraph/server.py +194 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +131 -1
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/system_prompts.py +449 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +19 -0
- htmlgraph/validation.py +115 -0
- htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bootstrap utilities for hook scripts.
|
|
3
|
+
|
|
4
|
+
Centralizes environment setup and project directory resolution used by all hooks.
|
|
5
|
+
Handles both development (src/python) and installed (package) modes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_project_dir(cwd: str | None = None) -> str:
|
|
16
|
+
"""Resolve the project directory with sensible fallbacks.
|
|
17
|
+
|
|
18
|
+
Hierarchy:
|
|
19
|
+
1. CLAUDE_PROJECT_DIR environment variable (set by Claude Code)
|
|
20
|
+
2. Git repository root (via git rev-parse --show-toplevel)
|
|
21
|
+
3. Current working directory (or provided cwd)
|
|
22
|
+
|
|
23
|
+
This supports running hooks in multiple contexts:
|
|
24
|
+
- Within a Claude Code session
|
|
25
|
+
- In git repositories
|
|
26
|
+
- In arbitrary directories
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
cwd: Starting directory for git search. Defaults to os.getcwd().
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Absolute path to the project directory.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
No exceptions - always returns a valid path.
|
|
36
|
+
"""
|
|
37
|
+
# First priority: Claude's explicit project directory
|
|
38
|
+
env_dir = os.environ.get("CLAUDE_PROJECT_DIR")
|
|
39
|
+
if env_dir:
|
|
40
|
+
return env_dir
|
|
41
|
+
|
|
42
|
+
# Second priority: Git repository root
|
|
43
|
+
start_dir = cwd or os.getcwd()
|
|
44
|
+
try:
|
|
45
|
+
result = subprocess.run(
|
|
46
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
47
|
+
capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
cwd=start_dir,
|
|
50
|
+
timeout=5,
|
|
51
|
+
)
|
|
52
|
+
if result.returncode == 0:
|
|
53
|
+
return result.stdout.strip()
|
|
54
|
+
except Exception:
|
|
55
|
+
# Git not available or not a repo - continue to fallback
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
# Final fallback: current working directory
|
|
59
|
+
return start_dir
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bootstrap_pythonpath(project_dir: str) -> None:
|
|
63
|
+
"""Bootstrap Python path for htmlgraph imports.
|
|
64
|
+
|
|
65
|
+
Handles two common deployment modes:
|
|
66
|
+
1. Development: Running inside htmlgraph repository (src/python exists)
|
|
67
|
+
2. Installed: Running where htmlgraph is installed as a package (do nothing)
|
|
68
|
+
|
|
69
|
+
This allows hooks to work correctly whether htmlgraph is:
|
|
70
|
+
- Being developed locally (add src/python to path)
|
|
71
|
+
- Installed in a virtual environment (already in path)
|
|
72
|
+
- Installed globally (already in path)
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
project_dir: Project directory from resolve_project_dir().
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
None (modifies sys.path in-place).
|
|
79
|
+
|
|
80
|
+
Side Effects:
|
|
81
|
+
- Modifies sys.path to ensure htmlgraph is importable
|
|
82
|
+
- Adds .venv/lib/pythonX.Y/site-packages if virtual environment exists
|
|
83
|
+
- Adds src/python if in htmlgraph repository
|
|
84
|
+
"""
|
|
85
|
+
project_path = Path(project_dir)
|
|
86
|
+
|
|
87
|
+
# First, try to use local virtual environment if it exists
|
|
88
|
+
venv = project_path / ".venv"
|
|
89
|
+
if venv.exists():
|
|
90
|
+
pyver = f"python{sys.version_info.major}.{sys.version_info.minor}"
|
|
91
|
+
candidates = [
|
|
92
|
+
venv / "lib" / pyver / "site-packages", # macOS/Linux
|
|
93
|
+
venv / "Lib" / "site-packages", # Windows
|
|
94
|
+
]
|
|
95
|
+
for candidate in candidates:
|
|
96
|
+
if candidate.exists():
|
|
97
|
+
sys.path.insert(0, str(candidate))
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
# Then, add src/python if this is the htmlgraph repository itself
|
|
101
|
+
repo_src = project_path / "src" / "python"
|
|
102
|
+
if repo_src.exists():
|
|
103
|
+
sys.path.insert(0, str(repo_src))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_graph_dir(cwd: str | None = None) -> Path:
|
|
107
|
+
"""Get the .htmlgraph directory path, creating it if necessary.
|
|
108
|
+
|
|
109
|
+
The .htmlgraph directory is the root for all HtmlGraph tracking:
|
|
110
|
+
- .htmlgraph/sessions/ - Session HTML files
|
|
111
|
+
- .htmlgraph/features/ - Feature tracking
|
|
112
|
+
- .htmlgraph/events/ - Event JSON files
|
|
113
|
+
- .htmlgraph/htmlgraph.db - SQLite database
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
cwd: Starting directory for project resolution. Defaults to os.getcwd().
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Path to the .htmlgraph directory (guaranteed to exist).
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
OSError: If directory creation fails (e.g., permission denied).
|
|
123
|
+
"""
|
|
124
|
+
project_dir = resolve_project_dir(cwd)
|
|
125
|
+
graph_dir = Path(project_dir) / ".htmlgraph"
|
|
126
|
+
graph_dir.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
return graph_dir
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def init_logger(name: str) -> logging.Logger:
|
|
131
|
+
"""Initialize a logger with standardized configuration.
|
|
132
|
+
|
|
133
|
+
Sets up a logger for hook scripts with:
|
|
134
|
+
- Consistent format across all hooks
|
|
135
|
+
- basicConfig applied only once (subsequent calls are ignored)
|
|
136
|
+
- Named logger returned (can be used for filtering)
|
|
137
|
+
|
|
138
|
+
Format: "[TIMESTAMP] [LEVEL] [logger_name] message"
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
name: Logger name (typically __name__ from calling module).
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
logging.Logger instance configured and ready to use.
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
```python
|
|
148
|
+
logger = init_logger(__name__)
|
|
149
|
+
logger.info("Hook started")
|
|
150
|
+
logger.error("Something went wrong")
|
|
151
|
+
```
|
|
152
|
+
"""
|
|
153
|
+
# Configure basicConfig only once (subsequent calls are no-ops)
|
|
154
|
+
logging.basicConfig(
|
|
155
|
+
level=logging.INFO,
|
|
156
|
+
format="[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
|
|
157
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Return named logger for this module
|
|
161
|
+
return logging.getLogger(name)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
__all__ = [
|
|
165
|
+
"resolve_project_dir",
|
|
166
|
+
"bootstrap_pythonpath",
|
|
167
|
+
"get_graph_dir",
|
|
168
|
+
"init_logger",
|
|
169
|
+
]
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook Execution Context Manager.
|
|
3
|
+
|
|
4
|
+
Manages hook execution context including lazy-loading of expensive resources
|
|
5
|
+
(database, session manager) to minimize initialization overhead.
|
|
6
|
+
|
|
7
|
+
This module provides a centralized context object that hooks can use to:
|
|
8
|
+
- Access the graph directory and project directory
|
|
9
|
+
- Retrieve session information
|
|
10
|
+
- Access the database for event recording
|
|
11
|
+
- Perform unified logging
|
|
12
|
+
|
|
13
|
+
Key Design Principles:
|
|
14
|
+
- Lazy-loading: Expensive resources (DB, SessionManager) are only loaded on first access
|
|
15
|
+
- Resource cleanup: Context properly closes resources when done
|
|
16
|
+
- Type safety: Full type hints for all public methods and properties
|
|
17
|
+
- Error handling: Graceful degradation if resources fail to initialize
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class HookContext:
|
|
31
|
+
"""
|
|
32
|
+
Hook execution context with lazy-loaded resources.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
project_dir: Absolute path to project root directory
|
|
36
|
+
graph_dir: Path to .htmlgraph directory for tracking data
|
|
37
|
+
session_id: Unique session identifier for this execution
|
|
38
|
+
agent_id: Agent/tool that's executing (e.g., 'claude-code', 'codex')
|
|
39
|
+
hook_input: Raw hook input data from Claude Code
|
|
40
|
+
_session_manager: Cached SessionManager instance (lazy-loaded)
|
|
41
|
+
_database: Cached HtmlGraphDB instance (lazy-loaded)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
project_dir: str
|
|
45
|
+
graph_dir: Path
|
|
46
|
+
session_id: str
|
|
47
|
+
agent_id: str
|
|
48
|
+
hook_input: dict
|
|
49
|
+
_session_manager: Any | None = field(default=None, repr=False)
|
|
50
|
+
_database: Any | None = field(default=None, repr=False)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_input(cls, hook_input: dict) -> "HookContext":
|
|
54
|
+
"""
|
|
55
|
+
Create HookContext from raw hook input.
|
|
56
|
+
|
|
57
|
+
Performs automatic environment resolution:
|
|
58
|
+
- Extracts session_id from hook_input
|
|
59
|
+
- Detects agent_id from environment or hook_input
|
|
60
|
+
- Resolves project directory via bootstrap
|
|
61
|
+
- Initializes graph directory
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
hook_input: Raw hook input dict from Claude Code hook system
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Initialized HookContext instance
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ImportError: If bootstrap module cannot be imported
|
|
71
|
+
OSError: If graph directory cannot be created
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
```python
|
|
75
|
+
hook_input = {
|
|
76
|
+
'session_id': 'sess-abc123',
|
|
77
|
+
'type': 'pretooluse',
|
|
78
|
+
'tool_name': 'Edit',
|
|
79
|
+
...
|
|
80
|
+
}
|
|
81
|
+
context = HookContext.from_input(hook_input)
|
|
82
|
+
logger.info(f"Session: {context.session_id}, Agent: {context.agent_id}")
|
|
83
|
+
```
|
|
84
|
+
"""
|
|
85
|
+
# Import bootstrap locally to avoid circular imports
|
|
86
|
+
from htmlgraph.hooks.bootstrap import (
|
|
87
|
+
get_graph_dir,
|
|
88
|
+
resolve_project_dir,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Extract session ID from hook input
|
|
92
|
+
session_id = hook_input.get("session_id", "unknown")
|
|
93
|
+
|
|
94
|
+
# Detect agent ID (priority order)
|
|
95
|
+
# 1. Explicit agent_id in hook input
|
|
96
|
+
# 2. HTMLGRAPH_AGENT_ID environment variable
|
|
97
|
+
# 3. CLAUDE_AGENT_NICKNAME environment variable (Claude Code)
|
|
98
|
+
# 4. Default to 'unknown'
|
|
99
|
+
agent_id = (
|
|
100
|
+
hook_input.get("agent_id")
|
|
101
|
+
or os.environ.get("HTMLGRAPH_AGENT_ID")
|
|
102
|
+
or os.environ.get("CLAUDE_AGENT_NICKNAME", "unknown")
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Resolve project directory with fallbacks
|
|
106
|
+
project_dir = resolve_project_dir()
|
|
107
|
+
|
|
108
|
+
# Get or create graph directory
|
|
109
|
+
graph_dir = get_graph_dir(project_dir)
|
|
110
|
+
|
|
111
|
+
logger.info(
|
|
112
|
+
f"Initializing hook context: session={session_id}, "
|
|
113
|
+
f"agent={agent_id}, project={project_dir}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return cls(
|
|
117
|
+
project_dir=project_dir,
|
|
118
|
+
graph_dir=graph_dir,
|
|
119
|
+
session_id=session_id,
|
|
120
|
+
agent_id=agent_id,
|
|
121
|
+
hook_input=hook_input,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def session_manager(self) -> Any:
|
|
126
|
+
"""
|
|
127
|
+
Lazy-load and cache SessionManager instance.
|
|
128
|
+
|
|
129
|
+
Importing SessionManager is expensive (thousands of file system operations
|
|
130
|
+
for graph initialization), so we defer until first access.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
SessionManager instance for session tracking and activity attribution
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
ImportError: If SessionManager cannot be imported
|
|
137
|
+
Exception: If SessionManager initialization fails
|
|
138
|
+
|
|
139
|
+
Note:
|
|
140
|
+
SessionManager is cached after first access. Multiple accesses
|
|
141
|
+
return the same instance.
|
|
142
|
+
"""
|
|
143
|
+
if self._session_manager is not None:
|
|
144
|
+
return self._session_manager
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
from htmlgraph.session_manager import SessionManager
|
|
148
|
+
|
|
149
|
+
logger.debug(f"Loading SessionManager for {self.graph_dir}")
|
|
150
|
+
self._session_manager = SessionManager(graph_dir=self.graph_dir)
|
|
151
|
+
logger.info("SessionManager loaded successfully")
|
|
152
|
+
return self._session_manager
|
|
153
|
+
except ImportError as e:
|
|
154
|
+
logger.error(f"Failed to import SessionManager: {e}")
|
|
155
|
+
raise
|
|
156
|
+
except Exception as e:
|
|
157
|
+
logger.error(f"Failed to initialize SessionManager: {e}")
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def database(self) -> Any:
|
|
162
|
+
"""
|
|
163
|
+
Lazy-load and cache HtmlGraphDB instance.
|
|
164
|
+
|
|
165
|
+
Database access is needed for event recording, but we defer initialization
|
|
166
|
+
until first access to minimize startup overhead.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
HtmlGraphDB instance for recording events and features
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ImportError: If HtmlGraphDB cannot be imported
|
|
173
|
+
Exception: If database connection fails
|
|
174
|
+
|
|
175
|
+
Note:
|
|
176
|
+
Database connection is cached after first access. Multiple accesses
|
|
177
|
+
return the same instance.
|
|
178
|
+
"""
|
|
179
|
+
if self._database is not None:
|
|
180
|
+
return self._database
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
from htmlgraph.db.schema import HtmlGraphDB
|
|
184
|
+
|
|
185
|
+
db_path = self.graph_dir / "htmlgraph.db"
|
|
186
|
+
logger.debug(f"Loading HtmlGraphDB at {db_path}")
|
|
187
|
+
self._database = HtmlGraphDB(str(db_path))
|
|
188
|
+
logger.info("HtmlGraphDB loaded successfully")
|
|
189
|
+
return self._database
|
|
190
|
+
except ImportError as e:
|
|
191
|
+
logger.error(f"Failed to import HtmlGraphDB: {e}")
|
|
192
|
+
raise
|
|
193
|
+
except Exception as e:
|
|
194
|
+
logger.error(f"Failed to initialize HtmlGraphDB: {e}")
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
def close(self) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Clean up and close all resources gracefully.
|
|
200
|
+
|
|
201
|
+
Closes database connections and session manager resources.
|
|
202
|
+
Safe to call multiple times (idempotent).
|
|
203
|
+
|
|
204
|
+
This should be called in a finally block to ensure cleanup:
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
```python
|
|
208
|
+
context = HookContext.from_input(hook_input)
|
|
209
|
+
try:
|
|
210
|
+
# Use context
|
|
211
|
+
context.session_manager.track_activity(...)
|
|
212
|
+
finally:
|
|
213
|
+
context.close() # Always cleanup
|
|
214
|
+
```
|
|
215
|
+
"""
|
|
216
|
+
# Close database if loaded
|
|
217
|
+
if self._database is not None:
|
|
218
|
+
try:
|
|
219
|
+
logger.debug("Closing database connection")
|
|
220
|
+
self._database.close()
|
|
221
|
+
self._database = None
|
|
222
|
+
logger.info("Database closed successfully")
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.warning(f"Error closing database: {e}")
|
|
225
|
+
|
|
226
|
+
# Close session manager if loaded
|
|
227
|
+
if self._session_manager is not None:
|
|
228
|
+
try:
|
|
229
|
+
logger.debug("Closing session manager")
|
|
230
|
+
# SessionManager doesn't currently have a close method,
|
|
231
|
+
# but we keep this for future resource management
|
|
232
|
+
self._session_manager = None
|
|
233
|
+
logger.info("Session manager cleaned up")
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.warning(f"Error closing session manager: {e}")
|
|
236
|
+
|
|
237
|
+
def log(self, level: str, message: str) -> None:
|
|
238
|
+
"""
|
|
239
|
+
Unified logging for hooks.
|
|
240
|
+
|
|
241
|
+
Provides consistent logging across all hook modules with context
|
|
242
|
+
information (session_id, agent_id, project_dir).
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
level: Log level as string ('debug', 'info', 'warning', 'error', 'critical')
|
|
246
|
+
message: Message to log
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
```python
|
|
250
|
+
context.log('info', 'Processing user query')
|
|
251
|
+
context.log('error', f'Failed to track activity: {error}')
|
|
252
|
+
```
|
|
253
|
+
"""
|
|
254
|
+
log_func = getattr(logger, level.lower(), logger.info)
|
|
255
|
+
|
|
256
|
+
# Prefix message with context for better debugging
|
|
257
|
+
context_msg = f"[{self.session_id[:8]}][{self.agent_id}] {message}"
|
|
258
|
+
log_func(context_msg)
|
|
259
|
+
|
|
260
|
+
def __enter__(self) -> "HookContext":
|
|
261
|
+
"""Context manager entry."""
|
|
262
|
+
return self
|
|
263
|
+
|
|
264
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
265
|
+
"""Context manager exit with resource cleanup."""
|
|
266
|
+
self.close()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
__all__ = [
|
|
270
|
+
"HookContext",
|
|
271
|
+
]
|