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
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""HtmlGraph initialization operations.
|
|
4
|
+
|
|
5
|
+
This module provides functions for initializing the .htmlgraph directory structure,
|
|
6
|
+
creating necessary files, and optionally installing Git hooks.
|
|
7
|
+
|
|
8
|
+
The initialization process includes:
|
|
9
|
+
1. Directory structure creation (.htmlgraph with subdirectories)
|
|
10
|
+
2. Database initialization (htmlgraph.db)
|
|
11
|
+
3. Index creation (index.sqlite)
|
|
12
|
+
4. Configuration files
|
|
13
|
+
5. Optional Git hooks installation
|
|
14
|
+
|
|
15
|
+
Extracted from monolithic cmd_init() implementation for better maintainability.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import sqlite3
|
|
21
|
+
import subprocess
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from htmlgraph.cli.models import InitConfig, InitResult, ValidationResult
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Default collections to create
|
|
30
|
+
DEFAULT_COLLECTIONS = [
|
|
31
|
+
"features",
|
|
32
|
+
"bugs",
|
|
33
|
+
"chores",
|
|
34
|
+
"spikes",
|
|
35
|
+
"epics",
|
|
36
|
+
"tracks",
|
|
37
|
+
"sessions",
|
|
38
|
+
"insights",
|
|
39
|
+
"metrics",
|
|
40
|
+
"cigs",
|
|
41
|
+
"patterns", # Learning collection (SDK patterns API)
|
|
42
|
+
"todos", # Persistent task tracking
|
|
43
|
+
"task-delegations", # Spawned agent observability
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Additional directories
|
|
47
|
+
ADDITIONAL_DIRECTORIES = [
|
|
48
|
+
"events",
|
|
49
|
+
"logs",
|
|
50
|
+
"archive-index",
|
|
51
|
+
"archives",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def validate_directory(base_dir: Path) -> ValidationResult:
|
|
56
|
+
"""
|
|
57
|
+
Validate that directory is ready for initialization.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
base_dir: Directory to validate
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
ValidationResult with validation status and details
|
|
64
|
+
"""
|
|
65
|
+
from htmlgraph.cli.models import ValidationResult
|
|
66
|
+
|
|
67
|
+
result = ValidationResult(exists=base_dir.exists())
|
|
68
|
+
|
|
69
|
+
# Check if directory exists
|
|
70
|
+
if not base_dir.exists():
|
|
71
|
+
result.valid = False
|
|
72
|
+
result.errors.append(f"Directory does not exist: {base_dir}")
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
# Check if already initialized
|
|
76
|
+
graph_dir = base_dir / ".htmlgraph"
|
|
77
|
+
if graph_dir.exists():
|
|
78
|
+
result.is_initialized = True
|
|
79
|
+
|
|
80
|
+
# Check for nested .htmlgraph directory (initialization corruption bug)
|
|
81
|
+
nested_graph = graph_dir / ".htmlgraph"
|
|
82
|
+
if nested_graph.exists():
|
|
83
|
+
result.errors.append(
|
|
84
|
+
f"ERROR: Nested .htmlgraph directory detected at {nested_graph}\n"
|
|
85
|
+
" This indicates initialization corruption.\n"
|
|
86
|
+
" Fix: Remove nested directory with: rm -rf .htmlgraph/.htmlgraph/"
|
|
87
|
+
)
|
|
88
|
+
result.valid = False
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
result.errors.append(
|
|
92
|
+
f"Directory already initialized: {graph_dir}\n"
|
|
93
|
+
" Directory already contains .htmlgraph folder"
|
|
94
|
+
)
|
|
95
|
+
result.valid = False
|
|
96
|
+
return result
|
|
97
|
+
|
|
98
|
+
# Check if in git repository
|
|
99
|
+
try:
|
|
100
|
+
subprocess.run(
|
|
101
|
+
["git", "rev-parse", "--git-dir"],
|
|
102
|
+
cwd=base_dir,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
check=True,
|
|
105
|
+
)
|
|
106
|
+
result.has_git = True
|
|
107
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
108
|
+
result.has_git = False
|
|
109
|
+
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create_directory_structure(base_dir: Path) -> list[str]:
|
|
114
|
+
"""
|
|
115
|
+
Create the .htmlgraph directory structure.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
base_dir: Base directory (usually current working directory)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
List of created directory paths
|
|
122
|
+
"""
|
|
123
|
+
created = []
|
|
124
|
+
graph_dir = base_dir / ".htmlgraph"
|
|
125
|
+
|
|
126
|
+
# Create main graph directory
|
|
127
|
+
if not graph_dir.exists():
|
|
128
|
+
graph_dir.mkdir(parents=True)
|
|
129
|
+
created.append(str(graph_dir))
|
|
130
|
+
|
|
131
|
+
# Create collection directories
|
|
132
|
+
for collection in DEFAULT_COLLECTIONS:
|
|
133
|
+
coll_dir = graph_dir / collection
|
|
134
|
+
if not coll_dir.exists():
|
|
135
|
+
coll_dir.mkdir(parents=True)
|
|
136
|
+
created.append(str(coll_dir))
|
|
137
|
+
|
|
138
|
+
# Create additional directories
|
|
139
|
+
for dirname in ADDITIONAL_DIRECTORIES:
|
|
140
|
+
add_dir = graph_dir / dirname
|
|
141
|
+
if not add_dir.exists():
|
|
142
|
+
add_dir.mkdir(parents=True)
|
|
143
|
+
created.append(str(add_dir))
|
|
144
|
+
|
|
145
|
+
# Create logs subdirectory for errors
|
|
146
|
+
logs_errors = graph_dir / "logs" / "errors"
|
|
147
|
+
if not logs_errors.exists():
|
|
148
|
+
logs_errors.mkdir(parents=True)
|
|
149
|
+
created.append(str(logs_errors))
|
|
150
|
+
|
|
151
|
+
return created
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def create_database(graph_dir: Path) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Create the SQLite database for agent events and sessions.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
graph_dir: Path to .htmlgraph directory
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Path to created database file
|
|
163
|
+
"""
|
|
164
|
+
db_path = graph_dir / "htmlgraph.db"
|
|
165
|
+
|
|
166
|
+
# Create database with schema
|
|
167
|
+
conn = sqlite3.connect(db_path)
|
|
168
|
+
cursor = conn.cursor()
|
|
169
|
+
|
|
170
|
+
# Sessions table
|
|
171
|
+
cursor.execute("""
|
|
172
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
173
|
+
session_id TEXT PRIMARY KEY,
|
|
174
|
+
agent TEXT NOT NULL,
|
|
175
|
+
status TEXT DEFAULT 'active',
|
|
176
|
+
started_at TEXT NOT NULL,
|
|
177
|
+
ended_at TEXT,
|
|
178
|
+
event_count INTEGER DEFAULT 0,
|
|
179
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
180
|
+
)
|
|
181
|
+
""")
|
|
182
|
+
|
|
183
|
+
# Agent events table
|
|
184
|
+
cursor.execute("""
|
|
185
|
+
CREATE TABLE IF NOT EXISTS agent_events (
|
|
186
|
+
event_id TEXT PRIMARY KEY,
|
|
187
|
+
session_id TEXT NOT NULL,
|
|
188
|
+
tool_name TEXT NOT NULL,
|
|
189
|
+
timestamp TEXT NOT NULL,
|
|
190
|
+
success INTEGER DEFAULT 1,
|
|
191
|
+
feature_id TEXT,
|
|
192
|
+
work_type TEXT,
|
|
193
|
+
context TEXT,
|
|
194
|
+
FOREIGN KEY (session_id) REFERENCES sessions(session_id)
|
|
195
|
+
)
|
|
196
|
+
""")
|
|
197
|
+
|
|
198
|
+
# Create indexes for common queries
|
|
199
|
+
cursor.execute("""
|
|
200
|
+
CREATE INDEX IF NOT EXISTS idx_events_session
|
|
201
|
+
ON agent_events(session_id)
|
|
202
|
+
""")
|
|
203
|
+
|
|
204
|
+
cursor.execute("""
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp
|
|
206
|
+
ON agent_events(timestamp)
|
|
207
|
+
""")
|
|
208
|
+
|
|
209
|
+
cursor.execute("""
|
|
210
|
+
CREATE INDEX IF NOT EXISTS idx_events_feature
|
|
211
|
+
ON agent_events(feature_id)
|
|
212
|
+
""")
|
|
213
|
+
|
|
214
|
+
conn.commit()
|
|
215
|
+
conn.close()
|
|
216
|
+
|
|
217
|
+
return str(db_path)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def create_analytics_index(graph_dir: Path) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Create the analytics cache database (index.sqlite).
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
graph_dir: Path to .htmlgraph directory
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Path to created index file
|
|
229
|
+
"""
|
|
230
|
+
index_path = graph_dir / "index.sqlite"
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
# Use AnalyticsIndex if available to ensure proper schema
|
|
234
|
+
from htmlgraph.analytics_index import AnalyticsIndex
|
|
235
|
+
|
|
236
|
+
index = AnalyticsIndex(str(index_path))
|
|
237
|
+
index.ensure_schema()
|
|
238
|
+
except ImportError:
|
|
239
|
+
# Fallback to simple creation if AnalyticsIndex not available
|
|
240
|
+
conn = sqlite3.connect(index_path)
|
|
241
|
+
cursor = conn.cursor()
|
|
242
|
+
|
|
243
|
+
# Create basic cache table structure
|
|
244
|
+
cursor.execute("""
|
|
245
|
+
CREATE TABLE IF NOT EXISTS cache_metadata (
|
|
246
|
+
key TEXT PRIMARY KEY,
|
|
247
|
+
value TEXT,
|
|
248
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
249
|
+
)
|
|
250
|
+
""")
|
|
251
|
+
|
|
252
|
+
# Store schema version
|
|
253
|
+
cursor.execute("""
|
|
254
|
+
INSERT OR REPLACE INTO cache_metadata (key, value)
|
|
255
|
+
VALUES ('schema_version', '1.0')
|
|
256
|
+
""")
|
|
257
|
+
|
|
258
|
+
conn.commit()
|
|
259
|
+
conn.close()
|
|
260
|
+
|
|
261
|
+
return str(index_path)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def create_config_files(graph_dir: Path) -> list[str]:
|
|
265
|
+
"""
|
|
266
|
+
Create initial configuration files.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
graph_dir: Path to .htmlgraph directory
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
List of created config file paths
|
|
273
|
+
"""
|
|
274
|
+
created = []
|
|
275
|
+
|
|
276
|
+
# Create hooks-config.json
|
|
277
|
+
hooks_config = graph_dir / "hooks-config.json"
|
|
278
|
+
if not hooks_config.exists():
|
|
279
|
+
hooks_config.write_text(
|
|
280
|
+
json.dumps(
|
|
281
|
+
{
|
|
282
|
+
"enabled": True,
|
|
283
|
+
"track_events": True,
|
|
284
|
+
"detect_drift": True,
|
|
285
|
+
"auto_spikes": False,
|
|
286
|
+
},
|
|
287
|
+
indent=2,
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
created.append(str(hooks_config))
|
|
291
|
+
|
|
292
|
+
# Create drift-queue.json
|
|
293
|
+
drift_queue = graph_dir / "drift-queue.json"
|
|
294
|
+
if not drift_queue.exists():
|
|
295
|
+
drift_queue.write_text(json.dumps([], indent=2))
|
|
296
|
+
created.append(str(drift_queue))
|
|
297
|
+
|
|
298
|
+
# Create active-auto-spikes.json
|
|
299
|
+
auto_spikes = graph_dir / "active-auto-spikes.json"
|
|
300
|
+
if not auto_spikes.exists():
|
|
301
|
+
auto_spikes.write_text(json.dumps({}, indent=2))
|
|
302
|
+
created.append(str(auto_spikes))
|
|
303
|
+
|
|
304
|
+
# Create .gitkeep for events directory
|
|
305
|
+
events_gitkeep = graph_dir / "events" / ".gitkeep"
|
|
306
|
+
if not events_gitkeep.exists():
|
|
307
|
+
events_gitkeep.touch()
|
|
308
|
+
created.append(str(events_gitkeep))
|
|
309
|
+
|
|
310
|
+
return created
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def create_hook_scripts(graph_dir: Path) -> list[str]:
|
|
314
|
+
"""
|
|
315
|
+
Copy hook scripts to .htmlgraph/hooks directory.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
graph_dir: Path to .htmlgraph directory
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
List of created hook file paths
|
|
322
|
+
"""
|
|
323
|
+
created: list[str] = []
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
# Get the path to the hooks directory in the package
|
|
327
|
+
import htmlgraph
|
|
328
|
+
|
|
329
|
+
htmlgraph_dir = Path(htmlgraph.__file__).parent
|
|
330
|
+
hooks_src = htmlgraph_dir / "hooks"
|
|
331
|
+
|
|
332
|
+
if not hooks_src.exists():
|
|
333
|
+
return created
|
|
334
|
+
|
|
335
|
+
# Create hooks directory in graph_dir
|
|
336
|
+
hooks_dir = graph_dir / "hooks"
|
|
337
|
+
if not hooks_dir.exists():
|
|
338
|
+
hooks_dir.mkdir(parents=True)
|
|
339
|
+
|
|
340
|
+
# Copy hook scripts
|
|
341
|
+
hook_names = [
|
|
342
|
+
"post-commit.sh",
|
|
343
|
+
"post-checkout.sh",
|
|
344
|
+
"post-merge.sh",
|
|
345
|
+
"pre-push.sh",
|
|
346
|
+
]
|
|
347
|
+
for hook_name in hook_names:
|
|
348
|
+
src_hook = hooks_src / hook_name
|
|
349
|
+
dest_hook = hooks_dir / hook_name
|
|
350
|
+
|
|
351
|
+
if src_hook.exists() and not dest_hook.exists():
|
|
352
|
+
# Copy the file
|
|
353
|
+
dest_hook.write_text(src_hook.read_text(encoding="utf-8"))
|
|
354
|
+
# Make it executable
|
|
355
|
+
import stat
|
|
356
|
+
|
|
357
|
+
dest_hook.chmod(
|
|
358
|
+
dest_hook.stat().st_mode
|
|
359
|
+
| stat.S_IXUSR
|
|
360
|
+
| stat.S_IXGRP
|
|
361
|
+
| stat.S_IXOTH
|
|
362
|
+
)
|
|
363
|
+
created.append(str(dest_hook))
|
|
364
|
+
|
|
365
|
+
except Exception:
|
|
366
|
+
# Silently fail if hooks can't be copied
|
|
367
|
+
pass
|
|
368
|
+
|
|
369
|
+
return created
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def update_gitignore(base_dir: Path) -> str | None:
|
|
373
|
+
"""
|
|
374
|
+
Update .gitignore to exclude HtmlGraph cache files.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
base_dir: Base directory containing .gitignore
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Path to .gitignore if updated, None otherwise
|
|
381
|
+
"""
|
|
382
|
+
gitignore_path = base_dir / ".gitignore"
|
|
383
|
+
|
|
384
|
+
# Read existing .gitignore or create new
|
|
385
|
+
existing_lines = []
|
|
386
|
+
if gitignore_path.exists():
|
|
387
|
+
existing_lines = gitignore_path.read_text().splitlines()
|
|
388
|
+
|
|
389
|
+
# Check if HtmlGraph section already exists
|
|
390
|
+
if any("# HtmlGraph" in line for line in existing_lines):
|
|
391
|
+
return None
|
|
392
|
+
|
|
393
|
+
# Add HtmlGraph section
|
|
394
|
+
new_lines = [
|
|
395
|
+
"",
|
|
396
|
+
"# HtmlGraph cache and regenerable files",
|
|
397
|
+
".htmlgraph/index.sqlite",
|
|
398
|
+
".htmlgraph/index.sqlite.backup",
|
|
399
|
+
".htmlgraph/database.db",
|
|
400
|
+
".htmlgraph/sessions/*.jsonl",
|
|
401
|
+
".htmlgraph/events/*.jsonl",
|
|
402
|
+
".htmlgraph/parent-activity.json",
|
|
403
|
+
".htmlgraph/hook-debug.jsonl",
|
|
404
|
+
".htmlgraph/errors.jsonl",
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
# Append to existing .gitignore
|
|
408
|
+
with gitignore_path.open("a") as f:
|
|
409
|
+
f.write("\n".join(new_lines) + "\n")
|
|
410
|
+
|
|
411
|
+
return str(gitignore_path)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def install_git_hooks(base_dir: Path) -> bool:
|
|
415
|
+
"""
|
|
416
|
+
Install Git hooks for event logging.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
base_dir: Base directory containing .git
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
True if hooks installed successfully, False otherwise
|
|
423
|
+
"""
|
|
424
|
+
# Check if .git directory exists
|
|
425
|
+
git_dir = base_dir / ".git"
|
|
426
|
+
if not git_dir.exists():
|
|
427
|
+
return False
|
|
428
|
+
|
|
429
|
+
# Import hook installation from operations.hooks
|
|
430
|
+
try:
|
|
431
|
+
from htmlgraph.operations.hooks import install_hooks
|
|
432
|
+
|
|
433
|
+
result = install_hooks(project_dir=base_dir, use_copy=False)
|
|
434
|
+
return bool(result.installed)
|
|
435
|
+
except Exception:
|
|
436
|
+
return False
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _run_interactive_setup(graph_dir: Path, result: InitResult) -> None:
|
|
440
|
+
"""
|
|
441
|
+
Run interactive setup wizard.
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
graph_dir: Path to .htmlgraph directory
|
|
445
|
+
result: InitResult to update with created files
|
|
446
|
+
"""
|
|
447
|
+
print("\n=== HtmlGraph Interactive Setup ===\n")
|
|
448
|
+
|
|
449
|
+
# Ask about project name
|
|
450
|
+
project_name = input("Project name (optional, press Enter to skip): ").strip()
|
|
451
|
+
|
|
452
|
+
# Ask about default agent
|
|
453
|
+
default_agent = input("Default agent name (default: claude): ").strip()
|
|
454
|
+
if not default_agent:
|
|
455
|
+
default_agent = "claude"
|
|
456
|
+
|
|
457
|
+
# Create config file
|
|
458
|
+
config_file = graph_dir / "config.json"
|
|
459
|
+
if not config_file.exists():
|
|
460
|
+
config_data = {}
|
|
461
|
+
if project_name:
|
|
462
|
+
config_data["project_name"] = project_name
|
|
463
|
+
config_data["default_agent"] = default_agent
|
|
464
|
+
|
|
465
|
+
config_file.write_text(json.dumps(config_data, indent=2) + "\n")
|
|
466
|
+
result.files_created.append(str(config_file))
|
|
467
|
+
print(f"\n✓ Created config file: {config_file}")
|
|
468
|
+
|
|
469
|
+
print("\n✓ Interactive setup complete!\n")
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def create_dashboard_index(base_dir: Path) -> str | None:
|
|
473
|
+
"""
|
|
474
|
+
Copy the dashboard HTML file to index.html at the root.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
base_dir: Base directory where index.html should be created
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Path to created index.html, or None if not created
|
|
481
|
+
"""
|
|
482
|
+
try:
|
|
483
|
+
# Get the path to the dashboard template
|
|
484
|
+
import htmlgraph
|
|
485
|
+
|
|
486
|
+
htmlgraph_dir = Path(htmlgraph.__file__).parent
|
|
487
|
+
dashboard_path = htmlgraph_dir / "dashboard.html"
|
|
488
|
+
|
|
489
|
+
if not dashboard_path.exists():
|
|
490
|
+
return None
|
|
491
|
+
|
|
492
|
+
# Copy to root as index.html
|
|
493
|
+
index_path = base_dir / "index.html"
|
|
494
|
+
index_path.write_text(dashboard_path.read_text(encoding="utf-8"))
|
|
495
|
+
return str(index_path)
|
|
496
|
+
except Exception:
|
|
497
|
+
return None
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def initialize_htmlgraph(config: InitConfig) -> InitResult:
|
|
501
|
+
"""
|
|
502
|
+
Initialize HtmlGraph directory structure.
|
|
503
|
+
|
|
504
|
+
This is the main entry point for the init command.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
config: InitConfig with initialization settings
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
InitResult with initialization details
|
|
511
|
+
"""
|
|
512
|
+
from htmlgraph.cli.models import InitResult
|
|
513
|
+
|
|
514
|
+
base_dir = Path(config.dir).resolve()
|
|
515
|
+
graph_dir = base_dir / ".htmlgraph"
|
|
516
|
+
|
|
517
|
+
# Validate directory
|
|
518
|
+
validation = validate_directory(base_dir)
|
|
519
|
+
if not validation.valid:
|
|
520
|
+
return InitResult(
|
|
521
|
+
success=False,
|
|
522
|
+
graph_dir=str(graph_dir),
|
|
523
|
+
errors=validation.errors,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
result = InitResult(graph_dir=str(graph_dir))
|
|
527
|
+
|
|
528
|
+
try:
|
|
529
|
+
# Create directory structure
|
|
530
|
+
dirs_created = create_directory_structure(base_dir)
|
|
531
|
+
result.directories_created.extend(dirs_created)
|
|
532
|
+
|
|
533
|
+
# Create database
|
|
534
|
+
db_path = create_database(graph_dir)
|
|
535
|
+
result.files_created.append(db_path)
|
|
536
|
+
|
|
537
|
+
# Create analytics index (unless disabled)
|
|
538
|
+
if not config.no_index:
|
|
539
|
+
index_path = create_analytics_index(graph_dir)
|
|
540
|
+
result.files_created.append(index_path)
|
|
541
|
+
else:
|
|
542
|
+
result.warnings.append("Skipped analytics cache creation (--no-index)")
|
|
543
|
+
|
|
544
|
+
# Create config files (unless --no-events-keep)
|
|
545
|
+
if not config.no_events_keep:
|
|
546
|
+
config_files = create_config_files(graph_dir)
|
|
547
|
+
result.files_created.extend(config_files)
|
|
548
|
+
else:
|
|
549
|
+
result.warnings.append("Skipped .gitkeep creation (--no-events-keep)")
|
|
550
|
+
|
|
551
|
+
# Create hook scripts
|
|
552
|
+
hook_files = create_hook_scripts(graph_dir)
|
|
553
|
+
result.files_created.extend(hook_files)
|
|
554
|
+
|
|
555
|
+
# Create dashboard index.html at root
|
|
556
|
+
dashboard_index = create_dashboard_index(base_dir)
|
|
557
|
+
if dashboard_index:
|
|
558
|
+
result.files_created.append(dashboard_index)
|
|
559
|
+
|
|
560
|
+
# Update .gitignore (unless disabled)
|
|
561
|
+
if not config.no_update_gitignore:
|
|
562
|
+
gitignore_path = update_gitignore(base_dir)
|
|
563
|
+
if gitignore_path:
|
|
564
|
+
result.files_created.append(gitignore_path)
|
|
565
|
+
result.warnings.append("Updated .gitignore with HtmlGraph cache rules")
|
|
566
|
+
else:
|
|
567
|
+
result.warnings.append("Skipped .gitignore update (--no-update-gitignore)")
|
|
568
|
+
|
|
569
|
+
# Install Git hooks (if requested)
|
|
570
|
+
if config.install_hooks:
|
|
571
|
+
hooks_installed = install_git_hooks(base_dir)
|
|
572
|
+
result.hooks_installed = hooks_installed
|
|
573
|
+
if hooks_installed:
|
|
574
|
+
result.warnings.append("Installed Git hooks for event logging")
|
|
575
|
+
else:
|
|
576
|
+
result.warnings.append(
|
|
577
|
+
"Failed to install Git hooks (not in git repository?)"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
# Interactive setup wizard (if requested)
|
|
581
|
+
if config.interactive:
|
|
582
|
+
_run_interactive_setup(graph_dir, result)
|
|
583
|
+
|
|
584
|
+
# Add Git reminder if not initialized
|
|
585
|
+
if not validation.has_git and not config.install_hooks:
|
|
586
|
+
result.warnings.append(
|
|
587
|
+
"Not in a Git repository. Run 'htmlgraph init --install-hooks' "
|
|
588
|
+
"if you want to track Git events."
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
result.success = True
|
|
592
|
+
|
|
593
|
+
except Exception as e:
|
|
594
|
+
result.success = False
|
|
595
|
+
result.errors.append(f"Initialization failed: {e}")
|
|
596
|
+
|
|
597
|
+
return result
|