htmlgraph 0.9.3__py3-none-any.whl → 0.27.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +173 -17
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +127 -0
  7. htmlgraph/agent_registry.py +45 -30
  8. htmlgraph/agents.py +160 -107
  9. htmlgraph/analytics/__init__.py +9 -2
  10. htmlgraph/analytics/cli.py +190 -51
  11. htmlgraph/analytics/cost_analyzer.py +391 -0
  12. htmlgraph/analytics/cost_monitor.py +664 -0
  13. htmlgraph/analytics/cost_reporter.py +675 -0
  14. htmlgraph/analytics/cross_session.py +617 -0
  15. htmlgraph/analytics/dependency.py +192 -100
  16. htmlgraph/analytics/pattern_learning.py +771 -0
  17. htmlgraph/analytics/session_graph.py +707 -0
  18. htmlgraph/analytics/strategic/__init__.py +80 -0
  19. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  20. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  21. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  22. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  23. htmlgraph/analytics/work_type.py +190 -14
  24. htmlgraph/analytics_index.py +135 -51
  25. htmlgraph/api/__init__.py +3 -0
  26. htmlgraph/api/cost_alerts_websocket.py +416 -0
  27. htmlgraph/api/main.py +2498 -0
  28. htmlgraph/api/static/htmx.min.js +1 -0
  29. htmlgraph/api/static/style-redesign.css +1344 -0
  30. htmlgraph/api/static/style.css +1079 -0
  31. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  32. htmlgraph/api/templates/dashboard.html +794 -0
  33. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  34. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  35. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  36. htmlgraph/api/templates/partials/agents.html +317 -0
  37. htmlgraph/api/templates/partials/event-traces.html +373 -0
  38. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  39. htmlgraph/api/templates/partials/features.html +578 -0
  40. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  41. htmlgraph/api/templates/partials/metrics.html +346 -0
  42. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  43. htmlgraph/api/templates/partials/orchestration.html +198 -0
  44. htmlgraph/api/templates/partials/spawners.html +375 -0
  45. htmlgraph/api/templates/partials/work-items.html +613 -0
  46. htmlgraph/api/websocket.py +538 -0
  47. htmlgraph/archive/__init__.py +24 -0
  48. htmlgraph/archive/bloom.py +234 -0
  49. htmlgraph/archive/fts.py +297 -0
  50. htmlgraph/archive/manager.py +583 -0
  51. htmlgraph/archive/search.py +244 -0
  52. htmlgraph/atomic_ops.py +560 -0
  53. htmlgraph/attribute_index.py +208 -0
  54. htmlgraph/bounded_paths.py +539 -0
  55. htmlgraph/builders/__init__.py +14 -0
  56. htmlgraph/builders/base.py +118 -29
  57. htmlgraph/builders/bug.py +150 -0
  58. htmlgraph/builders/chore.py +119 -0
  59. htmlgraph/builders/epic.py +150 -0
  60. htmlgraph/builders/feature.py +31 -6
  61. htmlgraph/builders/insight.py +195 -0
  62. htmlgraph/builders/metric.py +217 -0
  63. htmlgraph/builders/pattern.py +202 -0
  64. htmlgraph/builders/phase.py +162 -0
  65. htmlgraph/builders/spike.py +52 -19
  66. htmlgraph/builders/track.py +148 -72
  67. htmlgraph/cigs/__init__.py +81 -0
  68. htmlgraph/cigs/autonomy.py +385 -0
  69. htmlgraph/cigs/cost.py +475 -0
  70. htmlgraph/cigs/messages_basic.py +472 -0
  71. htmlgraph/cigs/messaging.py +365 -0
  72. htmlgraph/cigs/models.py +771 -0
  73. htmlgraph/cigs/pattern_storage.py +427 -0
  74. htmlgraph/cigs/patterns.py +503 -0
  75. htmlgraph/cigs/posttool_analyzer.py +234 -0
  76. htmlgraph/cigs/reporter.py +818 -0
  77. htmlgraph/cigs/tracker.py +317 -0
  78. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  79. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  80. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  81. htmlgraph/cli/__init__.py +42 -0
  82. htmlgraph/cli/__main__.py +6 -0
  83. htmlgraph/cli/analytics.py +1424 -0
  84. htmlgraph/cli/base.py +685 -0
  85. htmlgraph/cli/constants.py +206 -0
  86. htmlgraph/cli/core.py +954 -0
  87. htmlgraph/cli/main.py +147 -0
  88. htmlgraph/cli/models.py +475 -0
  89. htmlgraph/cli/templates/__init__.py +1 -0
  90. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  91. htmlgraph/cli/work/__init__.py +239 -0
  92. htmlgraph/cli/work/browse.py +115 -0
  93. htmlgraph/cli/work/features.py +568 -0
  94. htmlgraph/cli/work/orchestration.py +676 -0
  95. htmlgraph/cli/work/report.py +728 -0
  96. htmlgraph/cli/work/sessions.py +466 -0
  97. htmlgraph/cli/work/snapshot.py +559 -0
  98. htmlgraph/cli/work/tracks.py +486 -0
  99. htmlgraph/cli_commands/__init__.py +1 -0
  100. htmlgraph/cli_commands/feature.py +195 -0
  101. htmlgraph/cli_framework.py +115 -0
  102. htmlgraph/collections/__init__.py +18 -0
  103. htmlgraph/collections/base.py +415 -98
  104. htmlgraph/collections/bug.py +53 -0
  105. htmlgraph/collections/chore.py +53 -0
  106. htmlgraph/collections/epic.py +53 -0
  107. htmlgraph/collections/feature.py +12 -26
  108. htmlgraph/collections/insight.py +100 -0
  109. htmlgraph/collections/metric.py +92 -0
  110. htmlgraph/collections/pattern.py +97 -0
  111. htmlgraph/collections/phase.py +53 -0
  112. htmlgraph/collections/session.py +194 -0
  113. htmlgraph/collections/spike.py +56 -16
  114. htmlgraph/collections/task_delegation.py +241 -0
  115. htmlgraph/collections/todo.py +511 -0
  116. htmlgraph/collections/traces.py +487 -0
  117. htmlgraph/config/cost_models.json +56 -0
  118. htmlgraph/config.py +190 -0
  119. htmlgraph/context_analytics.py +344 -0
  120. htmlgraph/converter.py +216 -28
  121. htmlgraph/cost_analysis/__init__.py +5 -0
  122. htmlgraph/cost_analysis/analyzer.py +438 -0
  123. htmlgraph/dashboard.html +2406 -307
  124. htmlgraph/dashboard.html.backup +6592 -0
  125. htmlgraph/dashboard.html.bak +7181 -0
  126. htmlgraph/dashboard.html.bak2 +7231 -0
  127. htmlgraph/dashboard.html.bak3 +7232 -0
  128. htmlgraph/db/__init__.py +38 -0
  129. htmlgraph/db/queries.py +790 -0
  130. htmlgraph/db/schema.py +1788 -0
  131. htmlgraph/decorators.py +317 -0
  132. htmlgraph/dependency_models.py +19 -2
  133. htmlgraph/deploy.py +142 -125
  134. htmlgraph/deployment_models.py +474 -0
  135. htmlgraph/docs/API_REFERENCE.md +841 -0
  136. htmlgraph/docs/HTTP_API.md +750 -0
  137. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  138. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  139. htmlgraph/docs/README.md +532 -0
  140. htmlgraph/docs/__init__.py +77 -0
  141. htmlgraph/docs/docs_version.py +55 -0
  142. htmlgraph/docs/metadata.py +93 -0
  143. htmlgraph/docs/migrations.py +232 -0
  144. htmlgraph/docs/template_engine.py +143 -0
  145. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  146. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  147. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  148. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  149. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  150. htmlgraph/docs/version_check.py +163 -0
  151. htmlgraph/edge_index.py +182 -27
  152. htmlgraph/error_handler.py +544 -0
  153. htmlgraph/event_log.py +100 -52
  154. htmlgraph/event_migration.py +13 -4
  155. htmlgraph/exceptions.py +49 -0
  156. htmlgraph/file_watcher.py +101 -28
  157. htmlgraph/find_api.py +75 -63
  158. htmlgraph/git_events.py +145 -63
  159. htmlgraph/graph.py +1122 -106
  160. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  161. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  162. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  163. htmlgraph/hooks/__init__.py +45 -0
  164. htmlgraph/hooks/bootstrap.py +169 -0
  165. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  166. htmlgraph/hooks/concurrent_sessions.py +208 -0
  167. htmlgraph/hooks/context.py +350 -0
  168. htmlgraph/hooks/drift_handler.py +525 -0
  169. htmlgraph/hooks/event_tracker.py +1314 -0
  170. htmlgraph/hooks/git_commands.py +175 -0
  171. htmlgraph/hooks/hooks-config.example.json +12 -0
  172. htmlgraph/hooks/installer.py +343 -0
  173. htmlgraph/hooks/orchestrator.py +674 -0
  174. htmlgraph/hooks/orchestrator_reflector.py +223 -0
  175. htmlgraph/hooks/post-checkout.sh +28 -0
  176. htmlgraph/hooks/post-commit.sh +24 -0
  177. htmlgraph/hooks/post-merge.sh +26 -0
  178. htmlgraph/hooks/post_tool_use_failure.py +273 -0
  179. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  180. htmlgraph/hooks/posttooluse.py +408 -0
  181. htmlgraph/hooks/pre-commit.sh +94 -0
  182. htmlgraph/hooks/pre-push.sh +28 -0
  183. htmlgraph/hooks/pretooluse.py +819 -0
  184. htmlgraph/hooks/prompt_analyzer.py +637 -0
  185. htmlgraph/hooks/session_handler.py +668 -0
  186. htmlgraph/hooks/session_summary.py +395 -0
  187. htmlgraph/hooks/state_manager.py +504 -0
  188. htmlgraph/hooks/subagent_detection.py +202 -0
  189. htmlgraph/hooks/subagent_stop.py +369 -0
  190. htmlgraph/hooks/task_enforcer.py +255 -0
  191. htmlgraph/hooks/task_validator.py +177 -0
  192. htmlgraph/hooks/validator.py +628 -0
  193. htmlgraph/ids.py +41 -27
  194. htmlgraph/index.d.ts +286 -0
  195. htmlgraph/learning.py +767 -0
  196. htmlgraph/mcp_server.py +69 -23
  197. htmlgraph/models.py +1586 -87
  198. htmlgraph/operations/README.md +62 -0
  199. htmlgraph/operations/__init__.py +79 -0
  200. htmlgraph/operations/analytics.py +339 -0
  201. htmlgraph/operations/bootstrap.py +289 -0
  202. htmlgraph/operations/events.py +244 -0
  203. htmlgraph/operations/fastapi_server.py +231 -0
  204. htmlgraph/operations/hooks.py +350 -0
  205. htmlgraph/operations/initialization.py +597 -0
  206. htmlgraph/operations/initialization.py.backup +228 -0
  207. htmlgraph/operations/server.py +303 -0
  208. htmlgraph/orchestration/__init__.py +58 -0
  209. htmlgraph/orchestration/claude_launcher.py +179 -0
  210. htmlgraph/orchestration/command_builder.py +72 -0
  211. htmlgraph/orchestration/headless_spawner.py +281 -0
  212. htmlgraph/orchestration/live_events.py +377 -0
  213. htmlgraph/orchestration/model_selection.py +327 -0
  214. htmlgraph/orchestration/plugin_manager.py +140 -0
  215. htmlgraph/orchestration/prompts.py +137 -0
  216. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  217. htmlgraph/orchestration/spawners/__init__.py +16 -0
  218. htmlgraph/orchestration/spawners/base.py +194 -0
  219. htmlgraph/orchestration/spawners/claude.py +173 -0
  220. htmlgraph/orchestration/spawners/codex.py +435 -0
  221. htmlgraph/orchestration/spawners/copilot.py +294 -0
  222. htmlgraph/orchestration/spawners/gemini.py +471 -0
  223. htmlgraph/orchestration/subprocess_runner.py +36 -0
  224. htmlgraph/orchestration/task_coordination.py +343 -0
  225. htmlgraph/orchestration.md +563 -0
  226. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  227. htmlgraph/orchestrator.py +669 -0
  228. htmlgraph/orchestrator_config.py +357 -0
  229. htmlgraph/orchestrator_mode.py +328 -0
  230. htmlgraph/orchestrator_validator.py +133 -0
  231. htmlgraph/parallel.py +646 -0
  232. htmlgraph/parser.py +160 -35
  233. htmlgraph/path_query.py +608 -0
  234. htmlgraph/pattern_matcher.py +636 -0
  235. htmlgraph/planning.py +147 -52
  236. htmlgraph/pydantic_models.py +476 -0
  237. htmlgraph/quality_gates.py +350 -0
  238. htmlgraph/query_builder.py +109 -72
  239. htmlgraph/query_composer.py +509 -0
  240. htmlgraph/reflection.py +443 -0
  241. htmlgraph/refs.py +344 -0
  242. htmlgraph/repo_hash.py +512 -0
  243. htmlgraph/repositories/__init__.py +292 -0
  244. htmlgraph/repositories/analytics_repository.py +455 -0
  245. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  246. htmlgraph/repositories/feature_repository.py +581 -0
  247. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  248. htmlgraph/repositories/feature_repository_memory.py +607 -0
  249. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  250. htmlgraph/repositories/filter_service.py +620 -0
  251. htmlgraph/repositories/filter_service_standard.py +445 -0
  252. htmlgraph/repositories/shared_cache.py +621 -0
  253. htmlgraph/repositories/shared_cache_memory.py +395 -0
  254. htmlgraph/repositories/track_repository.py +552 -0
  255. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  256. htmlgraph/repositories/track_repository_memory.py +508 -0
  257. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  258. htmlgraph/routing.py +8 -19
  259. htmlgraph/scripts/deploy.py +1 -2
  260. htmlgraph/sdk/__init__.py +398 -0
  261. htmlgraph/sdk/__init__.pyi +14 -0
  262. htmlgraph/sdk/analytics/__init__.py +19 -0
  263. htmlgraph/sdk/analytics/engine.py +155 -0
  264. htmlgraph/sdk/analytics/helpers.py +178 -0
  265. htmlgraph/sdk/analytics/registry.py +109 -0
  266. htmlgraph/sdk/base.py +484 -0
  267. htmlgraph/sdk/constants.py +216 -0
  268. htmlgraph/sdk/core.pyi +308 -0
  269. htmlgraph/sdk/discovery.py +120 -0
  270. htmlgraph/sdk/help/__init__.py +12 -0
  271. htmlgraph/sdk/help/mixin.py +699 -0
  272. htmlgraph/sdk/mixins/__init__.py +15 -0
  273. htmlgraph/sdk/mixins/attribution.py +113 -0
  274. htmlgraph/sdk/mixins/mixin.py +410 -0
  275. htmlgraph/sdk/operations/__init__.py +12 -0
  276. htmlgraph/sdk/operations/mixin.py +427 -0
  277. htmlgraph/sdk/orchestration/__init__.py +17 -0
  278. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  279. htmlgraph/sdk/orchestration/spawner.py +204 -0
  280. htmlgraph/sdk/planning/__init__.py +19 -0
  281. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  282. htmlgraph/sdk/planning/mixin.py +211 -0
  283. htmlgraph/sdk/planning/parallel.py +186 -0
  284. htmlgraph/sdk/planning/queue.py +210 -0
  285. htmlgraph/sdk/planning/recommendations.py +87 -0
  286. htmlgraph/sdk/planning/smart_planning.py +319 -0
  287. htmlgraph/sdk/session/__init__.py +19 -0
  288. htmlgraph/sdk/session/continuity.py +57 -0
  289. htmlgraph/sdk/session/handoff.py +110 -0
  290. htmlgraph/sdk/session/info.py +309 -0
  291. htmlgraph/sdk/session/manager.py +103 -0
  292. htmlgraph/sdk/strategic/__init__.py +26 -0
  293. htmlgraph/sdk/strategic/mixin.py +563 -0
  294. htmlgraph/server.py +685 -180
  295. htmlgraph/services/__init__.py +10 -0
  296. htmlgraph/services/claiming.py +199 -0
  297. htmlgraph/session_hooks.py +300 -0
  298. htmlgraph/session_manager.py +1392 -175
  299. htmlgraph/session_registry.py +587 -0
  300. htmlgraph/session_state.py +436 -0
  301. htmlgraph/session_warning.py +201 -0
  302. htmlgraph/sessions/__init__.py +23 -0
  303. htmlgraph/sessions/handoff.py +756 -0
  304. htmlgraph/setup.py +34 -17
  305. htmlgraph/spike_index.py +143 -0
  306. htmlgraph/sync_docs.py +12 -15
  307. htmlgraph/system_prompts.py +450 -0
  308. htmlgraph/templates/AGENTS.md.template +366 -0
  309. htmlgraph/templates/CLAUDE.md.template +97 -0
  310. htmlgraph/templates/GEMINI.md.template +87 -0
  311. htmlgraph/templates/orchestration-view.html +350 -0
  312. htmlgraph/track_builder.py +146 -15
  313. htmlgraph/track_manager.py +69 -21
  314. htmlgraph/transcript.py +890 -0
  315. htmlgraph/transcript_analytics.py +699 -0
  316. htmlgraph/types.py +323 -0
  317. htmlgraph/validation.py +115 -0
  318. htmlgraph/watch.py +8 -5
  319. htmlgraph/work_type_utils.py +3 -2
  320. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
  321. htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
  322. htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
  323. htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
  324. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
  325. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  326. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  327. htmlgraph/cli.py +0 -2688
  328. htmlgraph/sdk.py +0 -709
  329. htmlgraph-0.9.3.dist-info/RECORD +0 -61
  330. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  331. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,289 @@
1
+ from __future__ import annotations
2
+
3
+ """HtmlGraph bootstrap operations.
4
+
5
+ One-command setup to go from installation to first value in under 60 seconds.
6
+ This module provides functions for bootstrapping a project with HtmlGraph.
7
+
8
+ The bootstrap process includes:
9
+ 1. Auto-detecting project type (Python, Node, etc.)
10
+ 2. Creating .htmlgraph directory structure
11
+ 3. Initializing database with schema
12
+ 4. Installing Claude Code plugin hooks automatically
13
+ 5. Printing next steps for the user
14
+
15
+ This is designed for simplicity and speed - the minimal viable setup.
16
+ """
17
+
18
+
19
+ import json
20
+ import subprocess
21
+ from pathlib import Path
22
+ from typing import TYPE_CHECKING, Any
23
+
24
+ if TYPE_CHECKING:
25
+ from htmlgraph.cli.models import BootstrapConfig
26
+
27
+
28
+ def detect_project_type(project_dir: Path) -> str:
29
+ """
30
+ Auto-detect project type from files in directory.
31
+
32
+ Args:
33
+ project_dir: Project directory to inspect
34
+
35
+ Returns:
36
+ Detected project type: "python", "node", "multi", or "unknown"
37
+ """
38
+ # Check for Python project markers
39
+ has_python = any(
40
+ [
41
+ (project_dir / "pyproject.toml").exists(),
42
+ (project_dir / "setup.py").exists(),
43
+ (project_dir / "requirements.txt").exists(),
44
+ (project_dir / "Pipfile").exists(),
45
+ ]
46
+ )
47
+
48
+ # Check for Node project markers
49
+ has_node = (project_dir / "package.json").exists()
50
+
51
+ # Determine project type
52
+ if has_python and has_node:
53
+ return "multi"
54
+ elif has_python:
55
+ return "python"
56
+ elif has_node:
57
+ return "node"
58
+ else:
59
+ return "unknown"
60
+
61
+
62
+ def create_gitignore_template() -> str:
63
+ """
64
+ Create .gitignore template content for .htmlgraph directory.
65
+
66
+ Returns:
67
+ Gitignore template content
68
+ """
69
+ return """# HtmlGraph cache and regenerable files
70
+ .htmlgraph/htmlgraph.db
71
+ .htmlgraph/sessions/*.jsonl
72
+ .htmlgraph/events/*.jsonl
73
+ .htmlgraph/parent-activity.json
74
+ .htmlgraph/logs/
75
+ """
76
+
77
+
78
+ def check_already_initialized(project_dir: Path) -> bool:
79
+ """
80
+ Check if project is already initialized with HtmlGraph.
81
+
82
+ Args:
83
+ project_dir: Project directory to check
84
+
85
+ Returns:
86
+ True if already initialized, False otherwise
87
+ """
88
+ graph_dir = project_dir / ".htmlgraph"
89
+ return graph_dir.exists()
90
+
91
+
92
+ def create_bootstrap_structure(project_dir: Path) -> dict[str, list[str]]:
93
+ """
94
+ Create minimal .htmlgraph directory structure for bootstrap.
95
+
96
+ Args:
97
+ project_dir: Project directory
98
+
99
+ Returns:
100
+ Dictionary with lists of created directories and files
101
+ """
102
+ graph_dir = project_dir / ".htmlgraph"
103
+ created_dirs: list[str] = []
104
+ created_files: list[str] = []
105
+
106
+ # Create main .htmlgraph directory
107
+ if not graph_dir.exists():
108
+ graph_dir.mkdir(parents=True)
109
+ created_dirs.append(str(graph_dir))
110
+
111
+ # Create subdirectories
112
+ subdirs = [
113
+ "sessions",
114
+ "features",
115
+ "spikes",
116
+ "tracks",
117
+ "events",
118
+ "logs",
119
+ "logs/errors",
120
+ ]
121
+
122
+ for subdir in subdirs:
123
+ subdir_path = graph_dir / subdir
124
+ if not subdir_path.exists():
125
+ subdir_path.mkdir(parents=True)
126
+ created_dirs.append(str(subdir_path))
127
+
128
+ # Create .gitignore in .htmlgraph
129
+ gitignore = graph_dir / ".gitignore"
130
+ if not gitignore.exists():
131
+ gitignore.write_text(create_gitignore_template())
132
+ created_files.append(str(gitignore))
133
+
134
+ # Create config.json
135
+ config_file = graph_dir / "config.json"
136
+ if not config_file.exists():
137
+ config_data = {
138
+ "bootstrapped": True,
139
+ "version": "1.0",
140
+ }
141
+ config_file.write_text(json.dumps(config_data, indent=2) + "\n")
142
+ created_files.append(str(config_file))
143
+
144
+ return {"directories": created_dirs, "files": created_files}
145
+
146
+
147
+ def initialize_database(graph_dir: Path) -> str:
148
+ """
149
+ Initialize HtmlGraph database with schema.
150
+
151
+ Args:
152
+ graph_dir: Path to .htmlgraph directory
153
+
154
+ Returns:
155
+ Path to created database file
156
+ """
157
+ from htmlgraph.db.schema import HtmlGraphDB
158
+
159
+ db_path = graph_dir / "htmlgraph.db"
160
+
161
+ # Create database using HtmlGraphDB (auto-creates tables)
162
+ db = HtmlGraphDB(db_path=str(db_path))
163
+ db.disconnect()
164
+
165
+ return str(db_path)
166
+
167
+
168
+ def check_claude_code_available() -> bool:
169
+ """
170
+ Check if Claude Code CLI is available.
171
+
172
+ Returns:
173
+ True if claude command is available, False otherwise
174
+ """
175
+ try:
176
+ result = subprocess.run(
177
+ ["claude", "--version"],
178
+ capture_output=True,
179
+ check=False,
180
+ timeout=5,
181
+ )
182
+ return result.returncode == 0
183
+ except (subprocess.TimeoutExpired, FileNotFoundError):
184
+ return False
185
+
186
+
187
+ def get_next_steps(
188
+ project_type: str, has_claude: bool, plugin_installed: bool
189
+ ) -> list[str]:
190
+ """
191
+ Generate next steps message based on project state.
192
+
193
+ Args:
194
+ project_type: Detected project type
195
+ has_claude: Whether Claude Code CLI is available
196
+ plugin_installed: Whether plugin hooks were installed
197
+
198
+ Returns:
199
+ List of next step messages
200
+ """
201
+ steps = []
202
+
203
+ if has_claude:
204
+ if plugin_installed:
205
+ steps.append("1. Use Claude Code: Run 'claude --dev' in this project")
206
+ else:
207
+ steps.append(
208
+ "1. Install HtmlGraph plugin: Run 'claude plugin install htmlgraph'"
209
+ )
210
+ steps.append("2. Use Claude Code: Run 'claude --dev' in this project")
211
+ else:
212
+ steps.append(
213
+ "1. Install Claude Code CLI: Visit https://code.claude.com/docs/installation"
214
+ )
215
+ steps.append(
216
+ "2. Install HtmlGraph plugin: Run 'claude plugin install htmlgraph'"
217
+ )
218
+ steps.append("3. Use Claude Code: Run 'claude --dev' in this project")
219
+
220
+ steps.append(
221
+ f"{len(steps) + 1}. Track work: Create features with 'htmlgraph feature create \"Title\"'"
222
+ )
223
+ steps.append(f"{len(steps) + 1}. View progress: Run 'htmlgraph status'")
224
+ steps.append(
225
+ f"{len(steps) + 1}. See what Claude did: Run 'htmlgraph serve' and open http://localhost:8080"
226
+ )
227
+
228
+ return steps
229
+
230
+
231
+ def bootstrap_htmlgraph(config: BootstrapConfig) -> dict[str, Any]:
232
+ """
233
+ Bootstrap HtmlGraph in a project directory.
234
+
235
+ This is the main entry point for the bootstrap command.
236
+
237
+ Args:
238
+ config: BootstrapConfig with bootstrap settings
239
+
240
+ Returns:
241
+ Dictionary with bootstrap results
242
+ """
243
+ project_dir = Path(config.project_path).resolve()
244
+
245
+ # Check if already initialized
246
+ if check_already_initialized(project_dir):
247
+ # Ask user if they want to overwrite
248
+ print(f"\n⚠️ HtmlGraph already initialized in {project_dir}")
249
+ response = input("Do you want to reinitialize? (y/N): ").strip().lower()
250
+ if response not in ["y", "yes"]:
251
+ return {
252
+ "success": False,
253
+ "message": "Bootstrap cancelled - already initialized",
254
+ }
255
+
256
+ # Detect project type
257
+ project_type = detect_project_type(project_dir)
258
+
259
+ # Create directory structure
260
+ created = create_bootstrap_structure(project_dir)
261
+ graph_dir = project_dir / ".htmlgraph"
262
+
263
+ # Initialize database
264
+ db_path = initialize_database(graph_dir)
265
+ created["files"].append(db_path)
266
+
267
+ # Check for Claude Code
268
+ has_claude = check_claude_code_available()
269
+
270
+ # Check if plugin is already available (skip installation check for now)
271
+ plugin_installed = False
272
+ if not config.no_plugins and has_claude:
273
+ # We'll consider it "installed" if hooks can be configured
274
+ # The actual plugin installation happens via marketplace
275
+ plugin_installed = True
276
+
277
+ # Generate next steps
278
+ next_steps = get_next_steps(project_type, has_claude, plugin_installed)
279
+
280
+ return {
281
+ "success": True,
282
+ "project_type": project_type,
283
+ "graph_dir": str(graph_dir),
284
+ "directories_created": created["directories"],
285
+ "files_created": created["files"],
286
+ "has_claude": has_claude,
287
+ "plugin_installed": plugin_installed,
288
+ "next_steps": next_steps,
289
+ }
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+
3
+ """Event and analytics index operations for HtmlGraph."""
4
+
5
+
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class EventRebuildResult:
13
+ """Result of rebuilding the event index."""
14
+
15
+ db_path: Path
16
+ inserted: int
17
+ skipped: int
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class EventStats:
22
+ """Statistics about events in the system."""
23
+
24
+ total_events: int
25
+ session_count: int
26
+ file_count: int
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class EventQueryResult:
31
+ """Result of querying events."""
32
+
33
+ events: list[dict[str, Any]]
34
+ total: int
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class EventExportResult:
39
+ """Result of exporting sessions to JSONL."""
40
+
41
+ written: int
42
+ skipped: int
43
+ failed: int
44
+
45
+
46
+ class EventOperationError(RuntimeError):
47
+ """Base error for event operations."""
48
+
49
+
50
+ def export_sessions(*, graph_dir: Path, overwrite: bool = False) -> EventExportResult:
51
+ """
52
+ Export legacy session HTML logs to JSONL events.
53
+
54
+ Args:
55
+ graph_dir: Path to .htmlgraph directory
56
+ overwrite: Whether to overwrite existing JSONL files
57
+
58
+ Returns:
59
+ EventExportResult with counts of written, skipped, failed files
60
+
61
+ Raises:
62
+ EventOperationError: If graph_dir doesn't exist or isn't a directory
63
+ """
64
+ if not graph_dir.exists():
65
+ raise EventOperationError(f"Graph directory not found: {graph_dir}")
66
+ if not graph_dir.is_dir():
67
+ raise EventOperationError(f"Not a directory: {graph_dir}")
68
+
69
+ from htmlgraph.event_migration import export_sessions_to_jsonl
70
+
71
+ sessions_dir = graph_dir / "sessions"
72
+ events_dir = graph_dir / "events"
73
+
74
+ if not sessions_dir.exists():
75
+ raise EventOperationError(f"Sessions directory not found: {sessions_dir}")
76
+
77
+ try:
78
+ result = export_sessions_to_jsonl(
79
+ sessions_dir=sessions_dir,
80
+ events_dir=events_dir,
81
+ overwrite=overwrite,
82
+ include_subdirs=False,
83
+ )
84
+ return EventExportResult(
85
+ written=result["written"],
86
+ skipped=result["skipped"],
87
+ failed=result["failed"],
88
+ )
89
+ except Exception as e:
90
+ raise EventOperationError(f"Failed to export sessions: {e}") from e
91
+
92
+
93
+ def rebuild_index(*, graph_dir: Path) -> EventRebuildResult:
94
+ """
95
+ Rebuild the SQLite analytics index from JSONL events.
96
+
97
+ Args:
98
+ graph_dir: Path to .htmlgraph directory
99
+
100
+ Returns:
101
+ EventRebuildResult with db_path and counts of inserted/skipped events
102
+
103
+ Raises:
104
+ EventOperationError: If events directory doesn't exist or rebuild fails
105
+ """
106
+ if not graph_dir.exists():
107
+ raise EventOperationError(f"Graph directory not found: {graph_dir}")
108
+ if not graph_dir.is_dir():
109
+ raise EventOperationError(f"Not a directory: {graph_dir}")
110
+
111
+ from htmlgraph.analytics_index import AnalyticsIndex
112
+ from htmlgraph.event_log import JsonlEventLog
113
+
114
+ events_dir = graph_dir / "events"
115
+ db_path = graph_dir / "index.sqlite"
116
+
117
+ if not events_dir.exists():
118
+ raise EventOperationError(f"Events directory not found: {events_dir}")
119
+
120
+ try:
121
+ log = JsonlEventLog(events_dir)
122
+ index = AnalyticsIndex(db_path)
123
+
124
+ # Stream events from all JSONL files
125
+ events = (event for _, event in log.iter_events())
126
+ result = index.rebuild_from_events(events)
127
+
128
+ return EventRebuildResult(
129
+ db_path=db_path,
130
+ inserted=result["inserted"],
131
+ skipped=result["skipped"],
132
+ )
133
+ except Exception as e:
134
+ raise EventOperationError(f"Failed to rebuild index: {e}") from e
135
+
136
+
137
+ def query_events(
138
+ *,
139
+ graph_dir: Path,
140
+ session_id: str | None = None,
141
+ tool: str | None = None,
142
+ feature_id: str | None = None,
143
+ since: str | None = None,
144
+ limit: int | None = None,
145
+ ) -> EventQueryResult:
146
+ """
147
+ Query events from JSONL logs with optional filters.
148
+
149
+ Args:
150
+ graph_dir: Path to .htmlgraph directory
151
+ session_id: Filter by session ID (None = all sessions)
152
+ tool: Filter by tool name (e.g., 'Bash', 'Edit')
153
+ feature_id: Filter by attributed feature ID
154
+ since: Only events after this timestamp (ISO string)
155
+ limit: Maximum number of events to return
156
+
157
+ Returns:
158
+ EventQueryResult with matching events and total count
159
+
160
+ Raises:
161
+ EventOperationError: If events directory doesn't exist or query fails
162
+ """
163
+ if not graph_dir.exists():
164
+ raise EventOperationError(f"Graph directory not found: {graph_dir}")
165
+ if not graph_dir.is_dir():
166
+ raise EventOperationError(f"Not a directory: {graph_dir}")
167
+
168
+ from htmlgraph.event_log import JsonlEventLog
169
+
170
+ events_dir = graph_dir / "events"
171
+
172
+ if not events_dir.exists():
173
+ raise EventOperationError(f"Events directory not found: {events_dir}")
174
+
175
+ try:
176
+ log = JsonlEventLog(events_dir)
177
+ events = log.query_events(
178
+ session_id=session_id,
179
+ tool=tool,
180
+ feature_id=feature_id,
181
+ since=since,
182
+ limit=limit,
183
+ )
184
+
185
+ return EventQueryResult(
186
+ events=events,
187
+ total=len(events),
188
+ )
189
+ except Exception as e:
190
+ raise EventOperationError(f"Failed to query events: {e}") from e
191
+
192
+
193
+ def get_event_stats(*, graph_dir: Path) -> EventStats:
194
+ """
195
+ Get statistics about events in the system.
196
+
197
+ Args:
198
+ graph_dir: Path to .htmlgraph directory
199
+
200
+ Returns:
201
+ EventStats with counts of total events, sessions, and files
202
+
203
+ Raises:
204
+ EventOperationError: If events directory doesn't exist or stats collection fails
205
+ """
206
+ if not graph_dir.exists():
207
+ raise EventOperationError(f"Graph directory not found: {graph_dir}")
208
+ if not graph_dir.is_dir():
209
+ raise EventOperationError(f"Not a directory: {graph_dir}")
210
+
211
+ from htmlgraph.event_log import JsonlEventLog
212
+
213
+ events_dir = graph_dir / "events"
214
+
215
+ if not events_dir.exists():
216
+ # No events directory means no events
217
+ return EventStats(
218
+ total_events=0,
219
+ session_count=0,
220
+ file_count=0,
221
+ )
222
+
223
+ try:
224
+ log = JsonlEventLog(events_dir)
225
+
226
+ # Count total events and track unique sessions
227
+ total_events = 0
228
+ sessions: set[str] = set()
229
+
230
+ for _, event in log.iter_events():
231
+ total_events += 1
232
+ if session_id := event.get("session_id"):
233
+ sessions.add(session_id)
234
+
235
+ # Count JSONL files
236
+ file_count = len(list(events_dir.glob("*.jsonl")))
237
+
238
+ return EventStats(
239
+ total_events=total_events,
240
+ session_count=len(sessions),
241
+ file_count=file_count,
242
+ )
243
+ except Exception as e:
244
+ raise EventOperationError(f"Failed to get event stats: {e}") from e