htmlgraph 0.20.1__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 +51 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/__init__.py +8 -1
- htmlgraph/analytics/cli.py +86 -20
- 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 +10 -6
- 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 +67 -27
- htmlgraph/analytics_index.py +53 -20
- 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 +2 -1
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/base.py +57 -2
- htmlgraph/builders/bug.py +19 -3
- htmlgraph/builders/chore.py +19 -3
- htmlgraph/builders/epic.py +19 -3
- htmlgraph/builders/feature.py +27 -3
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +19 -3
- htmlgraph/builders/spike.py +29 -3
- htmlgraph/builders/track.py +42 -1
- 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 +2 -0
- htmlgraph/collections/base.py +197 -14
- htmlgraph/collections/bug.py +2 -1
- htmlgraph/collections/chore.py +2 -1
- htmlgraph/collections/epic.py +2 -1
- htmlgraph/collections/feature.py +2 -1
- htmlgraph/collections/insight.py +2 -1
- htmlgraph/collections/metric.py +2 -1
- htmlgraph/collections/pattern.py +2 -1
- htmlgraph/collections/phase.py +2 -1
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +13 -2
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +116 -7
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2246 -248
- 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 +2 -1
- 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 +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 +2 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +86 -37
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +67 -9
- 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 +8 -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 +790 -99
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +327 -76
- htmlgraph/hooks/orchestrator_reflector.py +31 -4
- htmlgraph/hooks/post_tool_use_failure.py +32 -7
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +92 -19
- htmlgraph/hooks/pretooluse.py +527 -7
- 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 +99 -4
- htmlgraph/hooks/validator.py +212 -91
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +125 -100
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +217 -18
- 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.py → orchestration/task_coordination.py} +16 -8
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +115 -4
- htmlgraph/parallel.py +2 -1
- htmlgraph/parser.py +86 -6
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +2 -1
- 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/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 +295 -107
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +285 -3
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +33 -1
- htmlgraph/track_manager.py +38 -0
- htmlgraph/transcript.py +18 -5
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -4839
- htmlgraph/sdk.py +0 -2359
- htmlgraph-0.20.1.dist-info/RECORD +0 -118
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
htmlgraph/config.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HtmlGraph Configuration Management.
|
|
3
|
+
|
|
4
|
+
This module provides centralized configuration management using Pydantic Settings,
|
|
5
|
+
allowing configuration from environment variables, .env files, and CLI arguments.
|
|
6
|
+
|
|
7
|
+
IMPORTANT: Database path functions (get_database_path, get_analytics_cache_path)
|
|
8
|
+
are lightweight and have NO dependencies. They can be imported anywhere.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# LIGHTWEIGHT DATABASE PATH FUNCTIONS (NO DEPENDENCIES)
|
|
17
|
+
# These MUST come before any heavy imports so spawners can use them
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
# Database filenames (SINGLE SOURCE OF TRUTH)
|
|
21
|
+
DATABASE_FILENAME = "htmlgraph.db" # Unified event database
|
|
22
|
+
ANALYTICS_CACHE_FILENAME = "index.sqlite" # Analytics cache (rebuildable)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_database_path(project_root: Path | str | None = None) -> Path:
|
|
26
|
+
"""
|
|
27
|
+
Get the unified database path for event tracking.
|
|
28
|
+
|
|
29
|
+
This is the SINGLE source of truth for database location.
|
|
30
|
+
All hooks, agents, and spawners MUST use this function.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_root: Optional project root path. If None, uses HTMLGRAPH_PROJECT_ROOT
|
|
34
|
+
env var or current working directory.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Path to htmlgraph.db (the unified event database)
|
|
38
|
+
"""
|
|
39
|
+
if project_root is None:
|
|
40
|
+
project_root = Path(os.environ.get("HTMLGRAPH_PROJECT_ROOT", os.getcwd()))
|
|
41
|
+
else:
|
|
42
|
+
project_root = Path(project_root)
|
|
43
|
+
|
|
44
|
+
return project_root / ".htmlgraph" / DATABASE_FILENAME
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_analytics_cache_path(project_root: Path | str | None = None) -> Path:
|
|
48
|
+
"""
|
|
49
|
+
Get the analytics cache database path.
|
|
50
|
+
|
|
51
|
+
This is for read-only analytics queries (rebuildable from events).
|
|
52
|
+
NOT for event tracking - use get_database_path() for that.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
project_root: Optional project root path. If None, uses HTMLGRAPH_PROJECT_ROOT
|
|
56
|
+
env var or current working directory.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Path to index.sqlite (analytics cache, gitignored)
|
|
60
|
+
"""
|
|
61
|
+
if project_root is None:
|
|
62
|
+
project_root = Path(os.environ.get("HTMLGRAPH_PROJECT_ROOT", os.getcwd()))
|
|
63
|
+
else:
|
|
64
|
+
project_root = Path(project_root)
|
|
65
|
+
|
|
66
|
+
return project_root / ".htmlgraph" / ANALYTICS_CACHE_FILENAME
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# =============================================================================
|
|
70
|
+
# PYDANTIC CONFIGURATION (Heavy imports below - spawners don't need this)
|
|
71
|
+
# =============================================================================
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
from pydantic_settings import BaseSettings
|
|
75
|
+
|
|
76
|
+
_PYDANTIC_AVAILABLE = True
|
|
77
|
+
except ImportError:
|
|
78
|
+
_PYDANTIC_AVAILABLE = False
|
|
79
|
+
BaseSettings = object # type: ignore
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if _PYDANTIC_AVAILABLE:
|
|
83
|
+
|
|
84
|
+
class HtmlGraphConfig(BaseSettings):
|
|
85
|
+
"""Global HtmlGraph configuration using Pydantic Settings.
|
|
86
|
+
|
|
87
|
+
Configuration can be provided via:
|
|
88
|
+
1. Environment variables (prefix: HTMLGRAPH_)
|
|
89
|
+
2. .env file
|
|
90
|
+
3. Direct instantiation with parameters
|
|
91
|
+
4. CLI argument overrides
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# Core paths
|
|
95
|
+
graph_dir: Path = Path.home() / ".htmlgraph"
|
|
96
|
+
|
|
97
|
+
# Database paths (SINGLE SOURCE OF TRUTH)
|
|
98
|
+
# All hooks, agents, and spawners MUST use these via get_database_path()
|
|
99
|
+
database_filename: str = "htmlgraph.db" # Unified event database
|
|
100
|
+
analytics_cache_filename: str = "index.sqlite" # Analytics cache (rebuildable)
|
|
101
|
+
|
|
102
|
+
# Feature tracking
|
|
103
|
+
features_dir: Path | None = None
|
|
104
|
+
sessions_dir: Path | None = None
|
|
105
|
+
spikes_dir: Path | None = None
|
|
106
|
+
tracks_dir: Path | None = None
|
|
107
|
+
archives_dir: Path | None = None
|
|
108
|
+
|
|
109
|
+
# CLI behavior
|
|
110
|
+
debug: bool = False
|
|
111
|
+
verbose: bool = False
|
|
112
|
+
auto_sync: bool = True
|
|
113
|
+
color_output: bool = True
|
|
114
|
+
|
|
115
|
+
# Session management
|
|
116
|
+
max_sessions: int = 100
|
|
117
|
+
session_retention_days: int = 30
|
|
118
|
+
auto_archive_sessions: bool = True
|
|
119
|
+
|
|
120
|
+
# Performance
|
|
121
|
+
max_query_results: int = 1000
|
|
122
|
+
cache_enabled: bool = True
|
|
123
|
+
cache_ttl_seconds: int = 3600
|
|
124
|
+
|
|
125
|
+
# Logging
|
|
126
|
+
log_level: str = "INFO"
|
|
127
|
+
log_file: Path | None = None
|
|
128
|
+
|
|
129
|
+
model_config = {
|
|
130
|
+
"env_prefix": "HTMLGRAPH_",
|
|
131
|
+
"env_file": ".env",
|
|
132
|
+
"case_sensitive": False,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
def __init__(self, **data: Any) -> None:
|
|
136
|
+
"""Initialize config and compute derived paths."""
|
|
137
|
+
super().__init__(**data)
|
|
138
|
+
# Compute derived paths if not explicitly set
|
|
139
|
+
if self.features_dir is None:
|
|
140
|
+
self.features_dir = self.graph_dir / "features"
|
|
141
|
+
if self.sessions_dir is None:
|
|
142
|
+
self.sessions_dir = self.graph_dir / "sessions"
|
|
143
|
+
if self.spikes_dir is None:
|
|
144
|
+
self.spikes_dir = self.graph_dir / "spikes"
|
|
145
|
+
if self.tracks_dir is None:
|
|
146
|
+
self.tracks_dir = self.graph_dir / "tracks"
|
|
147
|
+
if self.archives_dir is None:
|
|
148
|
+
self.archives_dir = self.graph_dir / "archives"
|
|
149
|
+
|
|
150
|
+
def ensure_directories(self) -> None:
|
|
151
|
+
"""Create all configured directories if they don't exist."""
|
|
152
|
+
for directory in [
|
|
153
|
+
self.graph_dir,
|
|
154
|
+
self.features_dir,
|
|
155
|
+
self.sessions_dir,
|
|
156
|
+
self.spikes_dir,
|
|
157
|
+
self.tracks_dir,
|
|
158
|
+
self.archives_dir,
|
|
159
|
+
]:
|
|
160
|
+
if directory:
|
|
161
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
|
|
163
|
+
def get_config_dict(self) -> dict[str, Any]:
|
|
164
|
+
"""Get configuration as dictionary."""
|
|
165
|
+
return {
|
|
166
|
+
"graph_dir": str(self.graph_dir),
|
|
167
|
+
"features_dir": str(self.features_dir),
|
|
168
|
+
"sessions_dir": str(self.sessions_dir),
|
|
169
|
+
"spikes_dir": str(self.spikes_dir),
|
|
170
|
+
"tracks_dir": str(self.tracks_dir),
|
|
171
|
+
"archives_dir": str(self.archives_dir),
|
|
172
|
+
"debug": self.debug,
|
|
173
|
+
"verbose": self.verbose,
|
|
174
|
+
"auto_sync": self.auto_sync,
|
|
175
|
+
"color_output": self.color_output,
|
|
176
|
+
"max_sessions": self.max_sessions,
|
|
177
|
+
"session_retention_days": self.session_retention_days,
|
|
178
|
+
"auto_archive_sessions": self.auto_archive_sessions,
|
|
179
|
+
"max_query_results": self.max_query_results,
|
|
180
|
+
"cache_enabled": self.cache_enabled,
|
|
181
|
+
"cache_ttl_seconds": self.cache_ttl_seconds,
|
|
182
|
+
"log_level": self.log_level,
|
|
183
|
+
"log_file": str(self.log_file) if self.log_file else None,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# Global configuration instance
|
|
187
|
+
config: HtmlGraphConfig = HtmlGraphConfig()
|
|
188
|
+
else:
|
|
189
|
+
# Pydantic not available - config object won't work but database functions will
|
|
190
|
+
config = None # type: ignore
|
htmlgraph/context_analytics.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Context Analytics for HtmlGraph
|
|
3
5
|
|
|
@@ -7,7 +9,6 @@ Provides hierarchical context usage tracking and analytics:
|
|
|
7
9
|
Enables drill-down analysis of where context was consumed.
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
|
-
from __future__ import annotations
|
|
11
12
|
|
|
12
13
|
from dataclasses import dataclass, field
|
|
13
14
|
from typing import TYPE_CHECKING, Any
|
htmlgraph/converter.py
CHANGED
|
@@ -12,7 +12,16 @@ import logging
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, cast
|
|
14
14
|
|
|
15
|
-
from htmlgraph.models import
|
|
15
|
+
from htmlgraph.models import (
|
|
16
|
+
ActivityEntry,
|
|
17
|
+
Chore,
|
|
18
|
+
Edge,
|
|
19
|
+
Node,
|
|
20
|
+
Pattern,
|
|
21
|
+
Session,
|
|
22
|
+
Spike,
|
|
23
|
+
Step,
|
|
24
|
+
)
|
|
16
25
|
from htmlgraph.parser import HtmlParser
|
|
17
26
|
|
|
18
27
|
logger = logging.getLogger(__name__)
|
|
@@ -74,6 +83,7 @@ def html_to_node(filepath: Path | str) -> Node:
|
|
|
74
83
|
model_classes: dict[str, type[Node]] = {
|
|
75
84
|
"spike": Spike,
|
|
76
85
|
"chore": Chore,
|
|
86
|
+
"pattern": Pattern,
|
|
77
87
|
"node": Node,
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -385,7 +395,8 @@ def html_to_session(filepath: Path | str) -> Session:
|
|
|
385
395
|
parser = HtmlParser.from_file(filepath)
|
|
386
396
|
|
|
387
397
|
# Get article element with session data
|
|
388
|
-
|
|
398
|
+
article_results = parser.query("article[data-type='session']")
|
|
399
|
+
article = article_results[0] if article_results else None
|
|
389
400
|
if not article:
|
|
390
401
|
raise ValueError(f"No session article found in: {filepath}")
|
|
391
402
|
|
|
@@ -455,7 +466,8 @@ def html_to_session(filepath: Path | str) -> Session:
|
|
|
455
466
|
data["transcript_git_branch"] = transcript_branch
|
|
456
467
|
|
|
457
468
|
# Parse title
|
|
458
|
-
|
|
469
|
+
title_el_results = parser.query("h1")
|
|
470
|
+
title_el = title_el_results[0] if title_el_results else None
|
|
459
471
|
if title_el:
|
|
460
472
|
data["title"] = title_el.to_text().strip()
|
|
461
473
|
|
|
@@ -472,24 +484,28 @@ def html_to_session(filepath: Path | str) -> Session:
|
|
|
472
484
|
data["worked_on"] = worked_on
|
|
473
485
|
|
|
474
486
|
# Parse continued_from edge
|
|
475
|
-
|
|
487
|
+
continued_link_results = parser.query(
|
|
476
488
|
"nav[data-graph-edges] section[data-edge-type='continued-from'] a"
|
|
477
489
|
)
|
|
490
|
+
continued_link = continued_link_results[0] if continued_link_results else None
|
|
478
491
|
if continued_link:
|
|
479
492
|
href = continued_link.attrs.get("href") or ""
|
|
480
493
|
data["continued_from"] = href.replace(".html", "")
|
|
481
494
|
|
|
482
495
|
# Parse handoff context
|
|
483
|
-
|
|
496
|
+
handoff_section_results = parser.query("section[data-handoff]")
|
|
497
|
+
handoff_section = handoff_section_results[0] if handoff_section_results else None
|
|
484
498
|
if handoff_section:
|
|
485
|
-
|
|
499
|
+
notes_el_results = parser.query("section[data-handoff] [data-handoff-notes]")
|
|
500
|
+
notes_el = notes_el_results[0] if notes_el_results else None
|
|
486
501
|
if notes_el:
|
|
487
502
|
notes_text = notes_el.to_text().strip()
|
|
488
503
|
if notes_text.lower().startswith("notes:"):
|
|
489
504
|
notes_text = notes_text.split(":", 1)[1].strip()
|
|
490
505
|
data["handoff_notes"] = notes_text
|
|
491
506
|
|
|
492
|
-
|
|
507
|
+
next_el_results = parser.query("section[data-handoff] [data-recommended-next]")
|
|
508
|
+
next_el = next_el_results[0] if next_el_results else None
|
|
493
509
|
if next_el:
|
|
494
510
|
next_text = next_el.to_text().strip()
|
|
495
511
|
if next_text.lower().startswith("recommended next:"):
|
|
@@ -504,6 +520,17 @@ def html_to_session(filepath: Path | str) -> Session:
|
|
|
504
520
|
if blockers:
|
|
505
521
|
data["blockers"] = blockers
|
|
506
522
|
|
|
523
|
+
# Parse recommended context files
|
|
524
|
+
recommended_context = []
|
|
525
|
+
for li in parser.query(
|
|
526
|
+
"section[data-handoff] div[data-recommended-context] li"
|
|
527
|
+
):
|
|
528
|
+
file_path = li.to_text().strip()
|
|
529
|
+
if file_path:
|
|
530
|
+
recommended_context.append(file_path)
|
|
531
|
+
if recommended_context:
|
|
532
|
+
data["recommended_context"] = recommended_context
|
|
533
|
+
|
|
507
534
|
# Parse activity log
|
|
508
535
|
activity_log = []
|
|
509
536
|
for li in parser.query("section[data-activity-log] ol li"):
|
|
@@ -538,6 +565,88 @@ def html_to_session(filepath: Path | str) -> Session:
|
|
|
538
565
|
# Activity log in HTML is reversed (newest first), so reverse back
|
|
539
566
|
data["activity_log"] = list(reversed(activity_log))
|
|
540
567
|
|
|
568
|
+
# Parse detected patterns from table (if present)
|
|
569
|
+
detected_patterns = []
|
|
570
|
+
for tr in parser.query("section[data-detected-patterns] table tbody tr"):
|
|
571
|
+
# Extract pattern data from table row
|
|
572
|
+
pattern_type = tr.attrs.get("data-pattern-type", "neutral")
|
|
573
|
+
|
|
574
|
+
# Extract sequence from first <td class="sequence">
|
|
575
|
+
seq_tds = tr.query("td.sequence")
|
|
576
|
+
seq_td = seq_tds[0] if seq_tds else None
|
|
577
|
+
sequence_str = seq_td.to_text().strip() if seq_td else ""
|
|
578
|
+
sequence = [s.strip() for s in sequence_str.split("→")] if sequence_str else []
|
|
579
|
+
|
|
580
|
+
# Extract count from third <td>
|
|
581
|
+
tds = tr.query("td")
|
|
582
|
+
count_td = tds[2] if len(tds) > 2 else None
|
|
583
|
+
count_str = count_td.to_text().strip() if count_td else "0"
|
|
584
|
+
try:
|
|
585
|
+
count = int(count_str)
|
|
586
|
+
except (ValueError, TypeError):
|
|
587
|
+
count = 0
|
|
588
|
+
|
|
589
|
+
# Extract timestamps from fourth <td>
|
|
590
|
+
time_td = tds[3] if len(tds) > 3 else None
|
|
591
|
+
time_str = time_td.to_text().strip() if time_td else ""
|
|
592
|
+
times = time_str.split(" / ")
|
|
593
|
+
first_detected = times[0].strip() if len(times) > 0 else ""
|
|
594
|
+
last_detected = times[1].strip() if len(times) > 1 else ""
|
|
595
|
+
|
|
596
|
+
if sequence: # Only add if we have a valid sequence
|
|
597
|
+
detected_patterns.append(
|
|
598
|
+
{
|
|
599
|
+
"sequence": sequence,
|
|
600
|
+
"pattern_type": pattern_type,
|
|
601
|
+
"detection_count": count,
|
|
602
|
+
"first_detected": first_detected,
|
|
603
|
+
"last_detected": last_detected,
|
|
604
|
+
}
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
data["detected_patterns"] = detected_patterns
|
|
608
|
+
|
|
609
|
+
# Parse error log from error section (if present)
|
|
610
|
+
error_log = []
|
|
611
|
+
for details in parser.query("section[data-error-log] details"):
|
|
612
|
+
error_data = {
|
|
613
|
+
"error_type": details.attrs.get("data-error-type", "Unknown"),
|
|
614
|
+
"message": "",
|
|
615
|
+
"traceback": None,
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
ts = details.attrs.get("data-ts")
|
|
619
|
+
if ts:
|
|
620
|
+
error_data["timestamp"] = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
621
|
+
|
|
622
|
+
tool = details.attrs.get("data-tool")
|
|
623
|
+
if tool:
|
|
624
|
+
error_data["tool"] = tool
|
|
625
|
+
|
|
626
|
+
# Parse summary text (first line of details)
|
|
627
|
+
summary_el_results = details.query("summary")
|
|
628
|
+
summary_el = summary_el_results[0] if summary_el_results else None
|
|
629
|
+
if summary_el:
|
|
630
|
+
summary_text = summary_el.to_text().strip()
|
|
631
|
+
# Extract message from "ErrorType: message" format
|
|
632
|
+
if ": " in summary_text:
|
|
633
|
+
error_data["message"] = summary_text.split(": ", 1)[1]
|
|
634
|
+
else:
|
|
635
|
+
error_data["message"] = summary_text
|
|
636
|
+
|
|
637
|
+
# Parse traceback (if present)
|
|
638
|
+
traceback_el_results = details.query("pre.traceback")
|
|
639
|
+
traceback_el = traceback_el_results[0] if traceback_el_results else None
|
|
640
|
+
if traceback_el:
|
|
641
|
+
error_data["traceback"] = traceback_el.to_text().strip()
|
|
642
|
+
|
|
643
|
+
if error_data.get("message") or error_data.get("traceback"):
|
|
644
|
+
from htmlgraph.models import ErrorEntry
|
|
645
|
+
|
|
646
|
+
error_log.append(ErrorEntry(**error_data))
|
|
647
|
+
|
|
648
|
+
data["error_log"] = error_log
|
|
649
|
+
|
|
541
650
|
return Session(**data)
|
|
542
651
|
|
|
543
652
|
|