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.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- 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 +192 -100
- 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 +190 -14
- htmlgraph/analytics_index.py +135 -51
- 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 +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- 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 +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- 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 +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- 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 +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- 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 +45 -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 +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- 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 +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- 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/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- 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/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- 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 +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session info mixin for SDK - session start info and active work tracking.
|
|
3
|
+
|
|
4
|
+
Provides optimized methods for session context gathering.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from htmlgraph.session_manager import SessionManager
|
|
17
|
+
from htmlgraph.types import ActiveWorkItem, SessionStartInfo
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SessionInfoMixin:
|
|
21
|
+
"""
|
|
22
|
+
Mixin providing session info and active work methods to SDK.
|
|
23
|
+
|
|
24
|
+
Provides optimized methods for gathering session context in single calls.
|
|
25
|
+
Requires SDK instance with _directory, _agent_id, session_manager attributes.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_directory: Path
|
|
29
|
+
_agent_id: str | None
|
|
30
|
+
session_manager: SessionManager
|
|
31
|
+
|
|
32
|
+
def get_session_start_info(
|
|
33
|
+
self,
|
|
34
|
+
include_git_log: bool = True,
|
|
35
|
+
git_log_count: int = 5,
|
|
36
|
+
analytics_top_n: int = 3,
|
|
37
|
+
analytics_max_agents: int = 3,
|
|
38
|
+
) -> SessionStartInfo:
|
|
39
|
+
"""
|
|
40
|
+
Get comprehensive session start information in a single call.
|
|
41
|
+
|
|
42
|
+
Consolidates all information needed for session start into one method,
|
|
43
|
+
reducing context usage from 6+ tool calls to 1.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
include_git_log: Include recent git commits (default: True)
|
|
47
|
+
git_log_count: Number of recent commits to include (default: 5)
|
|
48
|
+
analytics_top_n: Number of bottlenecks/recommendations (default: 3)
|
|
49
|
+
analytics_max_agents: Max agents for parallel work analysis (default: 3)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Dict with comprehensive session start context:
|
|
53
|
+
- status: Project status (nodes, collections, WIP)
|
|
54
|
+
- active_work: Current active work item (if any)
|
|
55
|
+
- features: List of features with status
|
|
56
|
+
- sessions: Recent sessions
|
|
57
|
+
- git_log: Recent commits (if include_git_log=True)
|
|
58
|
+
- analytics: Strategic insights (bottlenecks, recommendations, parallel)
|
|
59
|
+
|
|
60
|
+
Note:
|
|
61
|
+
Returns empty dict {} if session context unavailable.
|
|
62
|
+
Always check for expected keys before accessing.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> sdk = SDK(agent="claude")
|
|
66
|
+
>>> info = sdk.get_session_start_info()
|
|
67
|
+
>>> logger.info(f"Project: {info['status']['total_nodes']} nodes")
|
|
68
|
+
>>> logger.info(f"WIP: {info['status']['in_progress_count']}")
|
|
69
|
+
>>> if info.get('active_work'):
|
|
70
|
+
... logger.info(f"Active: {info['active_work']['title']}")
|
|
71
|
+
>>> for bn in info['analytics']['bottlenecks']:
|
|
72
|
+
... logger.info(f"Bottleneck: {bn['title']}")
|
|
73
|
+
"""
|
|
74
|
+
result: dict[str, Any] = {}
|
|
75
|
+
|
|
76
|
+
# 1. Project status
|
|
77
|
+
result["status"] = self.get_status() # type: ignore[attr-defined]
|
|
78
|
+
|
|
79
|
+
# 2. Active work item (validation status) - always include, even if None
|
|
80
|
+
result["active_work"] = self.get_active_work_item()
|
|
81
|
+
|
|
82
|
+
# 3. Features list (simplified)
|
|
83
|
+
features_list: list[dict[str, object]] = []
|
|
84
|
+
for feature in self.features.all(): # type: ignore[attr-defined]
|
|
85
|
+
features_list.append(
|
|
86
|
+
{
|
|
87
|
+
"id": feature.id,
|
|
88
|
+
"title": feature.title,
|
|
89
|
+
"status": feature.status,
|
|
90
|
+
"priority": feature.priority,
|
|
91
|
+
"steps_total": len(feature.steps),
|
|
92
|
+
"steps_completed": sum(1 for s in feature.steps if s.completed),
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
result["features"] = features_list
|
|
96
|
+
|
|
97
|
+
# 4. Sessions list (recent 20)
|
|
98
|
+
sessions_list: list[dict[str, Any]] = []
|
|
99
|
+
for session in self.sessions.all()[:20]: # type: ignore[attr-defined]
|
|
100
|
+
sessions_list.append(
|
|
101
|
+
{
|
|
102
|
+
"id": session.id,
|
|
103
|
+
"status": session.status,
|
|
104
|
+
"agent": session.properties.get("agent", "unknown"),
|
|
105
|
+
"event_count": session.properties.get("event_count", 0),
|
|
106
|
+
"started": session.created.isoformat()
|
|
107
|
+
if hasattr(session, "created")
|
|
108
|
+
else None,
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
result["sessions"] = sessions_list
|
|
112
|
+
|
|
113
|
+
# 5. Git log (if requested)
|
|
114
|
+
if include_git_log:
|
|
115
|
+
try:
|
|
116
|
+
git_result = subprocess.run(
|
|
117
|
+
["git", "log", "--oneline", f"-{git_log_count}"],
|
|
118
|
+
capture_output=True,
|
|
119
|
+
text=True,
|
|
120
|
+
check=True,
|
|
121
|
+
cwd=self._directory.parent,
|
|
122
|
+
)
|
|
123
|
+
git_lines: list[str] = git_result.stdout.strip().split("\n")
|
|
124
|
+
result["git_log"] = git_lines
|
|
125
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
126
|
+
empty_list: list[str] = []
|
|
127
|
+
result["git_log"] = empty_list
|
|
128
|
+
|
|
129
|
+
# 6. Strategic analytics
|
|
130
|
+
result["analytics"] = {
|
|
131
|
+
"bottlenecks": self.find_bottlenecks(top_n=analytics_top_n), # type: ignore[attr-defined]
|
|
132
|
+
"recommendations": self.recommend_next_work(agent_count=analytics_top_n), # type: ignore[attr-defined]
|
|
133
|
+
"parallel": self.get_parallel_work(max_agents=analytics_max_agents), # type: ignore[attr-defined]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return result # type: ignore[return-value]
|
|
137
|
+
|
|
138
|
+
def get_active_work_item(
|
|
139
|
+
self,
|
|
140
|
+
agent: str | None = None,
|
|
141
|
+
filter_by_agent: bool = False,
|
|
142
|
+
work_types: list[str] | None = None,
|
|
143
|
+
) -> ActiveWorkItem | None:
|
|
144
|
+
"""
|
|
145
|
+
Get the currently active work item (in-progress status).
|
|
146
|
+
|
|
147
|
+
This is used by the PreToolUse validation hook to check if code changes
|
|
148
|
+
have an active work item for attribution.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
agent: Agent ID for filtering (optional)
|
|
152
|
+
filter_by_agent: If True, filter by agent. If False (default), return any active work item
|
|
153
|
+
work_types: Work item types to check (defaults to all: features, bugs, spikes, chores, epics)
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Dict with work item details or None if no active work item found:
|
|
157
|
+
- id: Work item ID
|
|
158
|
+
- title: Work item title
|
|
159
|
+
- type: Work item type (feature, bug, spike, chore, epic)
|
|
160
|
+
- status: Should be "in-progress"
|
|
161
|
+
- agent: Assigned agent
|
|
162
|
+
- steps_total: Total steps
|
|
163
|
+
- steps_completed: Completed steps
|
|
164
|
+
- auto_generated: (spikes only) True if auto-generated spike
|
|
165
|
+
- spike_subtype: (spikes only) "session-init" or "transition"
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
>>> sdk = SDK(agent="claude")
|
|
169
|
+
>>> # Get any active work item
|
|
170
|
+
>>> active = sdk.get_active_work_item()
|
|
171
|
+
>>> if active:
|
|
172
|
+
... logger.info(f"Working on: {active['title']}")
|
|
173
|
+
...
|
|
174
|
+
>>> # Get only this agent's active work item
|
|
175
|
+
>>> active = sdk.get_active_work_item(filter_by_agent=True)
|
|
176
|
+
"""
|
|
177
|
+
# Default to all work item types
|
|
178
|
+
if work_types is None:
|
|
179
|
+
work_types = ["features", "bugs", "spikes", "chores", "epics"]
|
|
180
|
+
|
|
181
|
+
# Search across all work item types
|
|
182
|
+
# Separate real work items from auto-generated spikes
|
|
183
|
+
real_work_items: list[dict[str, Any]] = []
|
|
184
|
+
auto_spikes: list[dict[str, Any]] = []
|
|
185
|
+
|
|
186
|
+
for work_type in work_types:
|
|
187
|
+
collection = getattr(self, work_type, None)
|
|
188
|
+
if collection is None:
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
# Query for in-progress items
|
|
192
|
+
in_progress = collection.where(status="in-progress")
|
|
193
|
+
|
|
194
|
+
for item in in_progress:
|
|
195
|
+
# Filter by agent if requested
|
|
196
|
+
if filter_by_agent:
|
|
197
|
+
agent_id = agent or self._agent_id
|
|
198
|
+
if agent_id and hasattr(item, "agent_assigned"):
|
|
199
|
+
if item.agent_assigned != agent_id:
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
item_dict: dict[str, Any] = {
|
|
203
|
+
"id": item.id,
|
|
204
|
+
"title": item.title,
|
|
205
|
+
"type": item.type,
|
|
206
|
+
"status": item.status,
|
|
207
|
+
"agent": getattr(item, "agent_assigned", None),
|
|
208
|
+
"steps_total": len(item.steps) if hasattr(item, "steps") else 0,
|
|
209
|
+
"steps_completed": sum(1 for s in item.steps if s.completed)
|
|
210
|
+
if hasattr(item, "steps")
|
|
211
|
+
else 0,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Add spike-specific fields for auto-spike detection
|
|
215
|
+
if item.type == "spike":
|
|
216
|
+
item_dict["auto_generated"] = getattr(item, "auto_generated", False)
|
|
217
|
+
item_dict["spike_subtype"] = getattr(item, "spike_subtype", None)
|
|
218
|
+
|
|
219
|
+
# Separate auto-spikes from real work
|
|
220
|
+
# Auto-spikes are temporary tracking items (session-init, transition, conversation-init)
|
|
221
|
+
is_auto_spike = item_dict["auto_generated"] and item_dict[
|
|
222
|
+
"spike_subtype"
|
|
223
|
+
] in ("session-init", "transition", "conversation-init")
|
|
224
|
+
|
|
225
|
+
if is_auto_spike:
|
|
226
|
+
auto_spikes.append(item_dict)
|
|
227
|
+
else:
|
|
228
|
+
# Real user-created spike
|
|
229
|
+
real_work_items.append(item_dict)
|
|
230
|
+
else:
|
|
231
|
+
# Features, bugs, chores, epics are always real work
|
|
232
|
+
real_work_items.append(item_dict)
|
|
233
|
+
|
|
234
|
+
# Prioritize real work items over auto-spikes
|
|
235
|
+
# Auto-spikes should only show if there's NO other active work item
|
|
236
|
+
if real_work_items:
|
|
237
|
+
return real_work_items[0] # type: ignore[return-value]
|
|
238
|
+
|
|
239
|
+
if auto_spikes:
|
|
240
|
+
return auto_spikes[0] # type: ignore[return-value]
|
|
241
|
+
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
def track_activity(
|
|
245
|
+
self,
|
|
246
|
+
tool: str,
|
|
247
|
+
summary: str,
|
|
248
|
+
file_paths: list[str] | None = None,
|
|
249
|
+
success: bool = True,
|
|
250
|
+
feature_id: str | None = None,
|
|
251
|
+
session_id: str | None = None,
|
|
252
|
+
parent_activity_id: str | None = None,
|
|
253
|
+
payload: dict[str, Any] | None = None,
|
|
254
|
+
) -> Any:
|
|
255
|
+
"""
|
|
256
|
+
Track an activity in the current or specified session.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
tool: Tool name (Edit, Bash, Read, etc.)
|
|
260
|
+
summary: Human-readable summary of the activity
|
|
261
|
+
file_paths: Files involved in this activity
|
|
262
|
+
success: Whether the tool call succeeded
|
|
263
|
+
feature_id: Explicit feature ID (skips attribution if provided)
|
|
264
|
+
session_id: Session ID (defaults to parent session if available, then active session)
|
|
265
|
+
parent_activity_id: ID of parent activity (e.g., Skill/Task invocation)
|
|
266
|
+
payload: Optional rich payload data
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Created ActivityEntry with attribution
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
>>> sdk = SDK(agent="claude")
|
|
273
|
+
>>> entry = sdk.track_activity(
|
|
274
|
+
... tool="CustomTool",
|
|
275
|
+
... summary="Performed custom analysis",
|
|
276
|
+
... file_paths=["src/main.py"],
|
|
277
|
+
... success=True
|
|
278
|
+
... )
|
|
279
|
+
>>> logger.info(f"Tracked: [{entry.tool}] {entry.summary}")
|
|
280
|
+
"""
|
|
281
|
+
# Determine target session: explicit parameter > parent_session > active > none
|
|
282
|
+
if not session_id:
|
|
283
|
+
# Priority 1: Parent session (explicitly provided or from env var)
|
|
284
|
+
if hasattr(self, "_parent_session") and self._parent_session: # type: ignore[attr-defined]
|
|
285
|
+
session_id = self._parent_session # type: ignore[attr-defined]
|
|
286
|
+
else:
|
|
287
|
+
# Priority 2: Active session for this agent
|
|
288
|
+
active = self.session_manager.get_active_session(agent=self._agent_id)
|
|
289
|
+
if active:
|
|
290
|
+
session_id = active.id
|
|
291
|
+
else:
|
|
292
|
+
raise ValueError(
|
|
293
|
+
"No active session. Start one with sdk.start_session()"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Get parent activity ID from environment if not provided
|
|
297
|
+
if not parent_activity_id:
|
|
298
|
+
parent_activity_id = os.getenv("HTMLGRAPH_PARENT_ACTIVITY")
|
|
299
|
+
|
|
300
|
+
return self.session_manager.track_activity(
|
|
301
|
+
session_id=session_id,
|
|
302
|
+
tool=tool,
|
|
303
|
+
summary=summary,
|
|
304
|
+
file_paths=file_paths,
|
|
305
|
+
success=success,
|
|
306
|
+
feature_id=feature_id,
|
|
307
|
+
parent_activity_id=parent_activity_id,
|
|
308
|
+
payload=payload,
|
|
309
|
+
)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SessionManager accessor and session creation/validation for SDK.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from htmlgraph.session_manager import SessionManager
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SessionManagerMixin:
|
|
14
|
+
"""
|
|
15
|
+
Provides SessionManager accessor and session lifecycle operations.
|
|
16
|
+
|
|
17
|
+
Attributes accessed by mixins:
|
|
18
|
+
session_manager: SessionManager instance
|
|
19
|
+
_db: HtmlGraphDB instance
|
|
20
|
+
_agent_id: Agent identifier
|
|
21
|
+
_parent_session: Parent session ID (if nested)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
session_manager: SessionManager
|
|
25
|
+
|
|
26
|
+
def _ensure_session_exists(
|
|
27
|
+
self, session_id: str, parent_event_id: str | None = None
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Create a session record if it doesn't exist.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
session_id: Session ID to ensure exists
|
|
34
|
+
parent_event_id: Event that spawned this session (optional)
|
|
35
|
+
"""
|
|
36
|
+
if not self._db.connection: # type: ignore[attr-defined]
|
|
37
|
+
self._db.connect() # type: ignore[attr-defined]
|
|
38
|
+
|
|
39
|
+
cursor = self._db.connection.cursor() # type: ignore[attr-defined,union-attr]
|
|
40
|
+
cursor.execute(
|
|
41
|
+
"SELECT COUNT(*) FROM sessions WHERE session_id = ?", (session_id,)
|
|
42
|
+
)
|
|
43
|
+
exists = cursor.fetchone()[0] > 0
|
|
44
|
+
|
|
45
|
+
if not exists:
|
|
46
|
+
# Create session record
|
|
47
|
+
self._db.insert_session( # type: ignore[attr-defined]
|
|
48
|
+
session_id=session_id,
|
|
49
|
+
agent_assigned=self._agent_id, # type: ignore[attr-defined]
|
|
50
|
+
is_subagent=self._parent_session is not None, # type: ignore[attr-defined]
|
|
51
|
+
parent_session_id=self._parent_session, # type: ignore[attr-defined]
|
|
52
|
+
parent_event_id=parent_event_id,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def start_session(
|
|
56
|
+
self,
|
|
57
|
+
session_id: str | None = None,
|
|
58
|
+
title: str | None = None,
|
|
59
|
+
agent: str | None = None,
|
|
60
|
+
) -> Any:
|
|
61
|
+
"""
|
|
62
|
+
Start a new session.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
session_id: Optional session ID
|
|
66
|
+
title: Optional session title
|
|
67
|
+
agent: Optional agent override (defaults to SDK agent)
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
New Session instance
|
|
71
|
+
"""
|
|
72
|
+
return self.session_manager.start_session(
|
|
73
|
+
session_id=session_id,
|
|
74
|
+
agent=agent or self._agent_id or "cli", # type: ignore[attr-defined]
|
|
75
|
+
title=title,
|
|
76
|
+
parent_session_id=self._parent_session, # type: ignore[attr-defined]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def end_session(
|
|
80
|
+
self,
|
|
81
|
+
session_id: str,
|
|
82
|
+
handoff_notes: str | None = None,
|
|
83
|
+
recommended_next: str | None = None,
|
|
84
|
+
blockers: list[str] | None = None,
|
|
85
|
+
) -> Any:
|
|
86
|
+
"""
|
|
87
|
+
End a session.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
session_id: Session ID to end
|
|
91
|
+
handoff_notes: Optional handoff notes
|
|
92
|
+
recommended_next: Optional recommendations
|
|
93
|
+
blockers: Optional blockers
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Ended Session instance
|
|
97
|
+
"""
|
|
98
|
+
return self.session_manager.end_session(
|
|
99
|
+
session_id=session_id,
|
|
100
|
+
handoff_notes=handoff_notes,
|
|
101
|
+
recommended_next=recommended_next,
|
|
102
|
+
blockers=blockers,
|
|
103
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Strategic Analytics SDK Integration - Phase 3
|
|
3
|
+
|
|
4
|
+
Provides SDK mixin for accessing strategic analytics capabilities:
|
|
5
|
+
- Pattern detection and retrieval
|
|
6
|
+
- Suggestion generation
|
|
7
|
+
- Preference management
|
|
8
|
+
- Cost optimization
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from htmlgraph import SDK
|
|
12
|
+
|
|
13
|
+
sdk = SDK(agent="claude")
|
|
14
|
+
|
|
15
|
+
# Access strategic analytics
|
|
16
|
+
patterns = sdk.strategic.detect_patterns()
|
|
17
|
+
suggestions = sdk.strategic.get_suggestions()
|
|
18
|
+
preferences = sdk.strategic.get_preferences()
|
|
19
|
+
|
|
20
|
+
# Record feedback
|
|
21
|
+
sdk.strategic.record_feedback(suggestion_id, accepted=True)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from htmlgraph.sdk.strategic.mixin import StrategicAnalyticsMixin
|
|
25
|
+
|
|
26
|
+
__all__ = ["StrategicAnalyticsMixin"]
|