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/file_watcher.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
logger = logging.getLogger(__name__)
|
|
4
|
+
|
|
1
5
|
"""
|
|
2
6
|
File watcher for automatic graph reloading.
|
|
3
7
|
|
|
@@ -67,7 +71,7 @@ class GraphFileHandler(FileSystemEventHandler):
|
|
|
67
71
|
|
|
68
72
|
def _trigger_reload(self) -> None:
|
|
69
73
|
"""Trigger a reload after debounce delay."""
|
|
70
|
-
|
|
74
|
+
logger.info(f"[FileWatcher] Reloading collection: {self.collection}")
|
|
71
75
|
self.reload_callback()
|
|
72
76
|
|
|
73
77
|
def _debounced_reload(self) -> None:
|
|
@@ -87,7 +91,7 @@ class GraphFileHandler(FileSystemEventHandler):
|
|
|
87
91
|
if not self._is_relevant_file(str(event.src_path)):
|
|
88
92
|
return
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
logger.debug(
|
|
91
95
|
f"[FileWatcher] {self.collection}: File created - {Path(str(event.src_path)).name}"
|
|
92
96
|
)
|
|
93
97
|
self._debounced_reload()
|
|
@@ -101,7 +105,7 @@ class GraphFileHandler(FileSystemEventHandler):
|
|
|
101
105
|
if not self._is_relevant_file(str(event.src_path)):
|
|
102
106
|
return
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
logger.debug(
|
|
105
109
|
f"[FileWatcher] {self.collection}: File modified - {Path(str(event.src_path)).name}"
|
|
106
110
|
)
|
|
107
111
|
self._debounced_reload()
|
|
@@ -115,7 +119,7 @@ class GraphFileHandler(FileSystemEventHandler):
|
|
|
115
119
|
if not self._is_relevant_file(str(event.src_path)):
|
|
116
120
|
return
|
|
117
121
|
|
|
118
|
-
|
|
122
|
+
logger.debug(
|
|
119
123
|
f"[FileWatcher] {self.collection}: File deleted - {Path(str(event.src_path)).name}"
|
|
120
124
|
)
|
|
121
125
|
self._debounced_reload()
|
|
@@ -146,7 +150,7 @@ class GraphWatcher:
|
|
|
146
150
|
|
|
147
151
|
def start(self) -> None:
|
|
148
152
|
"""Start watching for file changes."""
|
|
149
|
-
|
|
153
|
+
logger.info(
|
|
150
154
|
f"[FileWatcher] Starting file watcher for {len(self.collections)} collections..."
|
|
151
155
|
)
|
|
152
156
|
|
|
@@ -160,7 +164,7 @@ class GraphWatcher:
|
|
|
160
164
|
def reload() -> None:
|
|
161
165
|
graph = self.get_graph_callback(coll)
|
|
162
166
|
count = graph.reload()
|
|
163
|
-
|
|
167
|
+
logger.info(f"[FileWatcher] Reloaded {count} nodes in {coll}")
|
|
164
168
|
|
|
165
169
|
return reload
|
|
166
170
|
|
|
@@ -173,11 +177,11 @@ class GraphWatcher:
|
|
|
173
177
|
self.observer.schedule(handler, str(collection_dir), recursive=recursive)
|
|
174
178
|
|
|
175
179
|
self.observer.start()
|
|
176
|
-
|
|
180
|
+
logger.info(f"[FileWatcher] Watching {self.graph_dir} for changes...")
|
|
177
181
|
|
|
178
182
|
def stop(self) -> None:
|
|
179
183
|
"""Stop watching for file changes."""
|
|
180
|
-
|
|
184
|
+
logger.info("[FileWatcher] Stopping file watcher...")
|
|
181
185
|
self.observer.stop()
|
|
182
186
|
self.observer.join()
|
|
183
187
|
|
htmlgraph/find_api.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
BeautifulSoup-style Find API for HtmlGraph.
|
|
3
5
|
|
|
@@ -22,7 +24,6 @@ Example:
|
|
|
22
24
|
nodes = graph.find_all(properties__effort__gt=8)
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
|
-
from __future__ import annotations
|
|
26
27
|
|
|
27
28
|
import re
|
|
28
29
|
from collections.abc import Callable
|
htmlgraph/git_events.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Git event logging for HtmlGraph.
|
|
3
5
|
|
|
@@ -10,7 +12,6 @@ Design goals:
|
|
|
10
12
|
- Analytics-friendly: schema compatible with EventRecord/AnalyticsIndex
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
|
-
from __future__ import annotations
|
|
14
15
|
|
|
15
16
|
import os
|
|
16
17
|
import re
|
|
@@ -168,9 +169,19 @@ def parse_feature_refs(message: str) -> list[str]:
|
|
|
168
169
|
Parse feature IDs from commit message.
|
|
169
170
|
|
|
170
171
|
Looks for patterns like:
|
|
171
|
-
- Implements:
|
|
172
|
+
- Implements: feat-xyz
|
|
172
173
|
- Fixes: bug-abc
|
|
173
|
-
-
|
|
174
|
+
- [feat-123abc]
|
|
175
|
+
- feat-xyz
|
|
176
|
+
|
|
177
|
+
Supports HtmlGraph ID formats:
|
|
178
|
+
- feat-XXXXXXXX (features)
|
|
179
|
+
- feature-XXXXXXXX (legacy features)
|
|
180
|
+
- bug-XXXXXXXX (bugs)
|
|
181
|
+
- spk-XXXXXXXX (spikes)
|
|
182
|
+
- spike-XXXXXXXX (legacy spikes)
|
|
183
|
+
- chr-XXXXXXXX (chores)
|
|
184
|
+
- trk-XXXXXXXX (tracks)
|
|
174
185
|
|
|
175
186
|
Args:
|
|
176
187
|
message: Commit message
|
|
@@ -180,14 +191,21 @@ def parse_feature_refs(message: str) -> list[str]:
|
|
|
180
191
|
"""
|
|
181
192
|
features = []
|
|
182
193
|
|
|
183
|
-
#
|
|
184
|
-
|
|
194
|
+
# All HtmlGraph ID prefixes (current + legacy)
|
|
195
|
+
id_prefixes = r"(?:feat|feature|bug|spk|spike|chr|chore|trk|track|todo)"
|
|
196
|
+
|
|
197
|
+
# Pattern 1: Explicit tags (Implements: feat-xyz)
|
|
198
|
+
pattern1 = rf"(?:Implements|Fixes|Closes|Refs):\s*({id_prefixes}-[\w-]+)"
|
|
185
199
|
features.extend(re.findall(pattern1, message, re.IGNORECASE))
|
|
186
200
|
|
|
187
|
-
# Pattern:
|
|
188
|
-
pattern2 =
|
|
201
|
+
# Pattern 2: Square brackets [feat-xyz] (common in commit messages)
|
|
202
|
+
pattern2 = rf"\[({id_prefixes}-[\w-]+)\]"
|
|
189
203
|
features.extend(re.findall(pattern2, message, re.IGNORECASE))
|
|
190
204
|
|
|
205
|
+
# Pattern 3: Anywhere in message as word boundary
|
|
206
|
+
pattern3 = rf"\b({id_prefixes}-[\w-]+)\b"
|
|
207
|
+
features.extend(re.findall(pattern3, message, re.IGNORECASE))
|
|
208
|
+
|
|
191
209
|
# Remove duplicates while preserving order
|
|
192
210
|
seen = set()
|
|
193
211
|
unique_features = []
|
|
@@ -260,7 +278,10 @@ def _append_event(
|
|
|
260
278
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
261
279
|
with p.open("a", encoding="utf-8") as f:
|
|
262
280
|
f.write(
|
|
263
|
-
json.dumps(
|
|
281
|
+
json.dumps(
|
|
282
|
+
record.model_dump(mode="json"), ensure_ascii=False, default=str
|
|
283
|
+
)
|
|
284
|
+
+ "\n"
|
|
264
285
|
)
|
|
265
286
|
return
|
|
266
287
|
|
|
@@ -288,7 +309,44 @@ def _determine_context(graph_dir: Path, commit_message: str | None = None) -> di
|
|
|
288
309
|
if f and f not in all_features:
|
|
289
310
|
all_features.append(f)
|
|
290
311
|
|
|
291
|
-
session
|
|
312
|
+
# Try to find the right session based on feature IDs in commit message
|
|
313
|
+
# This handles multi-agent scenarios where multiple sessions are active
|
|
314
|
+
session = None
|
|
315
|
+
if message_features:
|
|
316
|
+
# If commit mentions specific features, find the session working on them
|
|
317
|
+
manager = _get_session_manager(graph_dir)
|
|
318
|
+
if manager:
|
|
319
|
+
try:
|
|
320
|
+
# Try to find a session that has any of the message features as active
|
|
321
|
+
for feature_id in message_features:
|
|
322
|
+
# Get the feature to check which agent is working on it
|
|
323
|
+
try:
|
|
324
|
+
# Try features graph first
|
|
325
|
+
feature = manager.features_graph.get(feature_id)
|
|
326
|
+
if not feature:
|
|
327
|
+
# Try bugs graph
|
|
328
|
+
feature = manager.bugs_graph.get(feature_id)
|
|
329
|
+
|
|
330
|
+
if (
|
|
331
|
+
feature
|
|
332
|
+
and hasattr(feature, "agent_assigned")
|
|
333
|
+
and feature.agent_assigned
|
|
334
|
+
):
|
|
335
|
+
# Find active session for this agent
|
|
336
|
+
session = manager.get_active_session(
|
|
337
|
+
agent=feature.agent_assigned
|
|
338
|
+
)
|
|
339
|
+
if session:
|
|
340
|
+
break
|
|
341
|
+
except Exception:
|
|
342
|
+
pass
|
|
343
|
+
except Exception:
|
|
344
|
+
pass
|
|
345
|
+
|
|
346
|
+
# Fallback to any active session if we couldn't match by feature
|
|
347
|
+
if not session:
|
|
348
|
+
session = get_active_session(graph_dir)
|
|
349
|
+
|
|
292
350
|
if session:
|
|
293
351
|
return {
|
|
294
352
|
"session_id": session.id,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"updated": "2026-01-10T08:55:56.503507",
|
|
4
|
+
"agents": {
|
|
5
|
+
"claude": {
|
|
6
|
+
"id": "claude",
|
|
7
|
+
"name": "Claude",
|
|
8
|
+
"capabilities": [
|
|
9
|
+
"python",
|
|
10
|
+
"javascript",
|
|
11
|
+
"typescript",
|
|
12
|
+
"html",
|
|
13
|
+
"css",
|
|
14
|
+
"code-review",
|
|
15
|
+
"testing",
|
|
16
|
+
"documentation",
|
|
17
|
+
"debugging",
|
|
18
|
+
"refactoring",
|
|
19
|
+
"architecture",
|
|
20
|
+
"api-design"
|
|
21
|
+
],
|
|
22
|
+
"max_parallel_tasks": 3,
|
|
23
|
+
"preferred_complexity": [
|
|
24
|
+
"low",
|
|
25
|
+
"medium",
|
|
26
|
+
"high",
|
|
27
|
+
"very-high"
|
|
28
|
+
],
|
|
29
|
+
"active": true,
|
|
30
|
+
"metadata": {}
|
|
31
|
+
},
|
|
32
|
+
"gemini": {
|
|
33
|
+
"id": "gemini",
|
|
34
|
+
"name": "Gemini",
|
|
35
|
+
"capabilities": [
|
|
36
|
+
"python",
|
|
37
|
+
"data-analysis",
|
|
38
|
+
"documentation",
|
|
39
|
+
"testing",
|
|
40
|
+
"code-review",
|
|
41
|
+
"javascript"
|
|
42
|
+
],
|
|
43
|
+
"max_parallel_tasks": 2,
|
|
44
|
+
"preferred_complexity": [
|
|
45
|
+
"low",
|
|
46
|
+
"medium",
|
|
47
|
+
"high"
|
|
48
|
+
],
|
|
49
|
+
"active": true,
|
|
50
|
+
"metadata": {}
|
|
51
|
+
},
|
|
52
|
+
"codex": {
|
|
53
|
+
"id": "codex",
|
|
54
|
+
"name": "Codex",
|
|
55
|
+
"capabilities": [
|
|
56
|
+
"python",
|
|
57
|
+
"javascript",
|
|
58
|
+
"debugging",
|
|
59
|
+
"testing",
|
|
60
|
+
"code-generation",
|
|
61
|
+
"documentation"
|
|
62
|
+
],
|
|
63
|
+
"max_parallel_tasks": 2,
|
|
64
|
+
"preferred_complexity": [
|
|
65
|
+
"low",
|
|
66
|
+
"medium"
|
|
67
|
+
],
|
|
68
|
+
"active": true,
|
|
69
|
+
"metadata": {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
Binary file
|
htmlgraph/hooks/__init__.py
CHANGED
|
@@ -16,6 +16,11 @@ from pathlib import Path
|
|
|
16
16
|
|
|
17
17
|
from htmlgraph.hooks.posttooluse import posttooluse_hook
|
|
18
18
|
from htmlgraph.hooks.pretooluse import pretooluse_hook
|
|
19
|
+
from htmlgraph.hooks.state_manager import (
|
|
20
|
+
DriftQueueManager,
|
|
21
|
+
ParentActivityTracker,
|
|
22
|
+
UserQueryEventTracker,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
# Directory containing hook scripts
|
|
21
26
|
HOOKS_DIR = Path(__file__).parent
|
|
@@ -32,6 +37,9 @@ AVAILABLE_HOOKS = [
|
|
|
32
37
|
__all__ = [
|
|
33
38
|
"pretooluse_hook",
|
|
34
39
|
"posttooluse_hook",
|
|
40
|
+
"ParentActivityTracker",
|
|
41
|
+
"UserQueryEventTracker",
|
|
42
|
+
"DriftQueueManager",
|
|
35
43
|
"AVAILABLE_HOOKS",
|
|
36
44
|
"HOOKS_DIR",
|
|
37
45
|
]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bootstrap utilities for hook scripts.
|
|
3
|
+
|
|
4
|
+
Centralizes environment setup and project directory resolution used by all hooks.
|
|
5
|
+
Handles both development (src/python) and installed (package) modes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_project_dir(cwd: str | None = None) -> str:
|
|
16
|
+
"""Resolve the project directory with sensible fallbacks.
|
|
17
|
+
|
|
18
|
+
Hierarchy:
|
|
19
|
+
1. CLAUDE_PROJECT_DIR environment variable (set by Claude Code)
|
|
20
|
+
2. Git repository root (via git rev-parse --show-toplevel)
|
|
21
|
+
3. Current working directory (or provided cwd)
|
|
22
|
+
|
|
23
|
+
This supports running hooks in multiple contexts:
|
|
24
|
+
- Within a Claude Code session
|
|
25
|
+
- In git repositories
|
|
26
|
+
- In arbitrary directories
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
cwd: Starting directory for git search. Defaults to os.getcwd().
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Absolute path to the project directory.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
No exceptions - always returns a valid path.
|
|
36
|
+
"""
|
|
37
|
+
# First priority: Claude's explicit project directory
|
|
38
|
+
env_dir = os.environ.get("CLAUDE_PROJECT_DIR")
|
|
39
|
+
if env_dir:
|
|
40
|
+
return env_dir
|
|
41
|
+
|
|
42
|
+
# Second priority: Git repository root
|
|
43
|
+
start_dir = cwd or os.getcwd()
|
|
44
|
+
try:
|
|
45
|
+
result = subprocess.run(
|
|
46
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
47
|
+
capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
cwd=start_dir,
|
|
50
|
+
timeout=5,
|
|
51
|
+
)
|
|
52
|
+
if result.returncode == 0:
|
|
53
|
+
return result.stdout.strip()
|
|
54
|
+
except Exception:
|
|
55
|
+
# Git not available or not a repo - continue to fallback
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
# Final fallback: current working directory
|
|
59
|
+
return start_dir
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bootstrap_pythonpath(project_dir: str) -> None:
|
|
63
|
+
"""Bootstrap Python path for htmlgraph imports.
|
|
64
|
+
|
|
65
|
+
Handles two common deployment modes:
|
|
66
|
+
1. Development: Running inside htmlgraph repository (src/python exists)
|
|
67
|
+
2. Installed: Running where htmlgraph is installed as a package (do nothing)
|
|
68
|
+
|
|
69
|
+
This allows hooks to work correctly whether htmlgraph is:
|
|
70
|
+
- Being developed locally (add src/python to path)
|
|
71
|
+
- Installed in a virtual environment (already in path)
|
|
72
|
+
- Installed globally (already in path)
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
project_dir: Project directory from resolve_project_dir().
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
None (modifies sys.path in-place).
|
|
79
|
+
|
|
80
|
+
Side Effects:
|
|
81
|
+
- Modifies sys.path to ensure htmlgraph is importable
|
|
82
|
+
- Adds .venv/lib/pythonX.Y/site-packages if virtual environment exists
|
|
83
|
+
- Adds src/python if in htmlgraph repository
|
|
84
|
+
"""
|
|
85
|
+
project_path = Path(project_dir)
|
|
86
|
+
|
|
87
|
+
# First, try to use local virtual environment if it exists
|
|
88
|
+
venv = project_path / ".venv"
|
|
89
|
+
if venv.exists():
|
|
90
|
+
pyver = f"python{sys.version_info.major}.{sys.version_info.minor}"
|
|
91
|
+
candidates = [
|
|
92
|
+
venv / "lib" / pyver / "site-packages", # macOS/Linux
|
|
93
|
+
venv / "Lib" / "site-packages", # Windows
|
|
94
|
+
]
|
|
95
|
+
for candidate in candidates:
|
|
96
|
+
if candidate.exists():
|
|
97
|
+
sys.path.insert(0, str(candidate))
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
# Then, add src/python if this is the htmlgraph repository itself
|
|
101
|
+
repo_src = project_path / "src" / "python"
|
|
102
|
+
if repo_src.exists():
|
|
103
|
+
sys.path.insert(0, str(repo_src))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_graph_dir(cwd: str | None = None) -> Path:
|
|
107
|
+
"""Get the .htmlgraph directory path, creating it if necessary.
|
|
108
|
+
|
|
109
|
+
The .htmlgraph directory is the root for all HtmlGraph tracking:
|
|
110
|
+
- .htmlgraph/sessions/ - Session HTML files
|
|
111
|
+
- .htmlgraph/features/ - Feature tracking
|
|
112
|
+
- .htmlgraph/events/ - Event JSON files
|
|
113
|
+
- .htmlgraph/htmlgraph.db - SQLite database
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
cwd: Starting directory for project resolution. Defaults to os.getcwd().
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Path to the .htmlgraph directory (guaranteed to exist).
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
OSError: If directory creation fails (e.g., permission denied).
|
|
123
|
+
"""
|
|
124
|
+
project_dir = resolve_project_dir(cwd)
|
|
125
|
+
graph_dir = Path(project_dir) / ".htmlgraph"
|
|
126
|
+
graph_dir.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
return graph_dir
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def init_logger(name: str) -> logging.Logger:
|
|
131
|
+
"""Initialize a logger with standardized configuration.
|
|
132
|
+
|
|
133
|
+
Sets up a logger for hook scripts with:
|
|
134
|
+
- Consistent format across all hooks
|
|
135
|
+
- basicConfig applied only once (subsequent calls are ignored)
|
|
136
|
+
- Named logger returned (can be used for filtering)
|
|
137
|
+
|
|
138
|
+
Format: "[TIMESTAMP] [LEVEL] [logger_name] message"
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
name: Logger name (typically __name__ from calling module).
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
logging.Logger instance configured and ready to use.
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
```python
|
|
148
|
+
logger = init_logger(__name__)
|
|
149
|
+
logger.info("Hook started")
|
|
150
|
+
logger.error("Something went wrong")
|
|
151
|
+
```
|
|
152
|
+
"""
|
|
153
|
+
# Configure basicConfig only once (subsequent calls are no-ops)
|
|
154
|
+
logging.basicConfig(
|
|
155
|
+
level=logging.INFO,
|
|
156
|
+
format="[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
|
|
157
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Return named logger for this module
|
|
161
|
+
return logging.getLogger(name)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
__all__ = [
|
|
165
|
+
"resolve_project_dir",
|
|
166
|
+
"bootstrap_pythonpath",
|
|
167
|
+
"get_graph_dir",
|
|
168
|
+
"init_logger",
|
|
169
|
+
]
|