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
htmlgraph/sdk.py
DELETED
|
@@ -1,709 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
HtmlGraph SDK - AI-Friendly Interface
|
|
3
|
-
|
|
4
|
-
Provides a fluent, ergonomic API for AI agents with:
|
|
5
|
-
- Auto-discovery of .htmlgraph directory
|
|
6
|
-
- Method chaining for all operations
|
|
7
|
-
- Context managers for auto-save
|
|
8
|
-
- Batch operations
|
|
9
|
-
- Minimal boilerplate
|
|
10
|
-
|
|
11
|
-
Example:
|
|
12
|
-
from htmlgraph import SDK
|
|
13
|
-
|
|
14
|
-
# Auto-discovers .htmlgraph directory
|
|
15
|
-
sdk = SDK(agent="claude")
|
|
16
|
-
|
|
17
|
-
# Fluent feature creation
|
|
18
|
-
feature = sdk.features.create(
|
|
19
|
-
title="User Authentication",
|
|
20
|
-
track="auth"
|
|
21
|
-
).add_steps([
|
|
22
|
-
"Create login endpoint",
|
|
23
|
-
"Add JWT middleware",
|
|
24
|
-
"Write tests"
|
|
25
|
-
]).set_priority("high").save()
|
|
26
|
-
|
|
27
|
-
# Work on a feature
|
|
28
|
-
with sdk.features.get("feature-001") as feature:
|
|
29
|
-
feature.start()
|
|
30
|
-
feature.complete_step(0)
|
|
31
|
-
# Auto-saves on exit
|
|
32
|
-
|
|
33
|
-
# Query
|
|
34
|
-
todos = sdk.features.where(status="todo", priority="high")
|
|
35
|
-
|
|
36
|
-
# Batch operations
|
|
37
|
-
sdk.features.mark_done(["feat-001", "feat-002", "feat-003"])
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
from __future__ import annotations
|
|
41
|
-
from datetime import datetime
|
|
42
|
-
from pathlib import Path
|
|
43
|
-
from typing import Any
|
|
44
|
-
|
|
45
|
-
from htmlgraph.models import Node, Step
|
|
46
|
-
from htmlgraph.graph import HtmlGraph
|
|
47
|
-
from htmlgraph.agents import AgentInterface
|
|
48
|
-
from htmlgraph.track_builder import TrackCollection
|
|
49
|
-
from htmlgraph.collections import BaseCollection, FeatureCollection, SpikeCollection
|
|
50
|
-
from htmlgraph.analytics import Analytics, DependencyAnalytics
|
|
51
|
-
from htmlgraph.session_manager import SessionManager
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class SDK:
|
|
55
|
-
"""
|
|
56
|
-
Main SDK interface for AI agents.
|
|
57
|
-
|
|
58
|
-
Auto-discovers .htmlgraph directory and provides fluent API for all collections.
|
|
59
|
-
|
|
60
|
-
Available Collections:
|
|
61
|
-
- features: Feature work items with builder support
|
|
62
|
-
- bugs: Bug reports
|
|
63
|
-
- chores: Maintenance and chore tasks
|
|
64
|
-
- spikes: Investigation and research spikes
|
|
65
|
-
- epics: Large bodies of work
|
|
66
|
-
- phases: Project phases
|
|
67
|
-
- sessions: Agent sessions
|
|
68
|
-
- tracks: Work tracks
|
|
69
|
-
- agents: Agent information
|
|
70
|
-
|
|
71
|
-
Example:
|
|
72
|
-
sdk = SDK(agent="claude")
|
|
73
|
-
|
|
74
|
-
# Work with features (has builder support)
|
|
75
|
-
feature = sdk.features.create("User Auth")
|
|
76
|
-
.set_priority("high")
|
|
77
|
-
.add_steps(["Login", "Logout"])
|
|
78
|
-
.save()
|
|
79
|
-
|
|
80
|
-
# Work with bugs
|
|
81
|
-
high_bugs = sdk.bugs.where(status="todo", priority="high")
|
|
82
|
-
with sdk.bugs.edit("bug-001") as bug:
|
|
83
|
-
bug.status = "in-progress"
|
|
84
|
-
|
|
85
|
-
# Work with any collection
|
|
86
|
-
all_spikes = sdk.spikes.all()
|
|
87
|
-
sdk.chores.mark_done(["chore-001", "chore-002"])
|
|
88
|
-
sdk.epics.assign(["epic-001"], agent="claude")
|
|
89
|
-
"""
|
|
90
|
-
|
|
91
|
-
def __init__(
|
|
92
|
-
self,
|
|
93
|
-
directory: Path | str | None = None,
|
|
94
|
-
agent: str | None = None
|
|
95
|
-
):
|
|
96
|
-
"""
|
|
97
|
-
Initialize SDK.
|
|
98
|
-
|
|
99
|
-
Args:
|
|
100
|
-
directory: Path to .htmlgraph directory (auto-discovered if not provided)
|
|
101
|
-
agent: Agent identifier for operations
|
|
102
|
-
"""
|
|
103
|
-
if directory is None:
|
|
104
|
-
directory = self._discover_htmlgraph()
|
|
105
|
-
|
|
106
|
-
self._directory = Path(directory)
|
|
107
|
-
self._agent_id = agent
|
|
108
|
-
|
|
109
|
-
# Initialize SessionManager for smart tracking and attribution
|
|
110
|
-
self.session_manager = SessionManager(self._directory)
|
|
111
|
-
|
|
112
|
-
# Initialize underlying components (for backward compatibility)
|
|
113
|
-
self._graph = HtmlGraph(self._directory / "features")
|
|
114
|
-
self._agent_interface = AgentInterface(
|
|
115
|
-
self._directory / "features",
|
|
116
|
-
agent_id=agent
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
# Collection interfaces - all work item types
|
|
120
|
-
self.features = FeatureCollection(self)
|
|
121
|
-
self.bugs = BaseCollection(self, "bugs", "bug")
|
|
122
|
-
self.chores = BaseCollection(self, "chores", "chore")
|
|
123
|
-
self.spikes = SpikeCollection(self)
|
|
124
|
-
self.epics = BaseCollection(self, "epics", "epic")
|
|
125
|
-
self.phases = BaseCollection(self, "phases", "phase")
|
|
126
|
-
|
|
127
|
-
# Non-work collections
|
|
128
|
-
self.sessions = BaseCollection(self, "sessions", "session")
|
|
129
|
-
self.tracks = TrackCollection(self) # Use specialized collection with builder support
|
|
130
|
-
self.agents = BaseCollection(self, "agents", "agent")
|
|
131
|
-
|
|
132
|
-
# Analytics interface (Phase 2: Work Type Analytics)
|
|
133
|
-
self.analytics = Analytics(self)
|
|
134
|
-
|
|
135
|
-
# Dependency analytics interface (Advanced graph analytics)
|
|
136
|
-
self.dep_analytics = DependencyAnalytics(self._graph)
|
|
137
|
-
|
|
138
|
-
@staticmethod
|
|
139
|
-
def _discover_htmlgraph() -> Path:
|
|
140
|
-
"""
|
|
141
|
-
Auto-discover .htmlgraph directory.
|
|
142
|
-
|
|
143
|
-
Searches current directory and parents.
|
|
144
|
-
"""
|
|
145
|
-
current = Path.cwd()
|
|
146
|
-
|
|
147
|
-
# Check current directory
|
|
148
|
-
if (current / ".htmlgraph").exists():
|
|
149
|
-
return current / ".htmlgraph"
|
|
150
|
-
|
|
151
|
-
# Check parent directories
|
|
152
|
-
for parent in current.parents:
|
|
153
|
-
if (parent / ".htmlgraph").exists():
|
|
154
|
-
return parent / ".htmlgraph"
|
|
155
|
-
|
|
156
|
-
# Default to current directory
|
|
157
|
-
return current / ".htmlgraph"
|
|
158
|
-
|
|
159
|
-
@property
|
|
160
|
-
def agent(self) -> str | None:
|
|
161
|
-
"""Get current agent ID."""
|
|
162
|
-
return self._agent_id
|
|
163
|
-
|
|
164
|
-
def reload(self) -> None:
|
|
165
|
-
"""Reload all data from disk."""
|
|
166
|
-
self._graph.reload()
|
|
167
|
-
self._agent_interface.reload()
|
|
168
|
-
# SessionManager reloads implicitly on access via its converters/graphs
|
|
169
|
-
|
|
170
|
-
def summary(self, max_items: int = 10) -> str:
|
|
171
|
-
"""
|
|
172
|
-
Get project summary.
|
|
173
|
-
|
|
174
|
-
Returns:
|
|
175
|
-
Compact overview for AI agent orientation
|
|
176
|
-
"""
|
|
177
|
-
return self._agent_interface.get_summary(max_items)
|
|
178
|
-
|
|
179
|
-
def my_work(self) -> dict[str, Any]:
|
|
180
|
-
"""
|
|
181
|
-
Get current agent's workload.
|
|
182
|
-
|
|
183
|
-
Returns:
|
|
184
|
-
Dict with in_progress, completed counts
|
|
185
|
-
"""
|
|
186
|
-
if not self._agent_id:
|
|
187
|
-
raise ValueError("No agent ID set")
|
|
188
|
-
return self._agent_interface.get_workload(self._agent_id)
|
|
189
|
-
|
|
190
|
-
def next_task(
|
|
191
|
-
self,
|
|
192
|
-
priority: str | None = None,
|
|
193
|
-
auto_claim: bool = True
|
|
194
|
-
) -> Node | None:
|
|
195
|
-
"""
|
|
196
|
-
Get next available task for this agent.
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
priority: Optional priority filter
|
|
200
|
-
auto_claim: Automatically claim the task
|
|
201
|
-
|
|
202
|
-
Returns:
|
|
203
|
-
Next available Node or None
|
|
204
|
-
"""
|
|
205
|
-
return self._agent_interface.get_next_task(
|
|
206
|
-
agent_id=self._agent_id,
|
|
207
|
-
priority=priority,
|
|
208
|
-
node_type="feature",
|
|
209
|
-
auto_claim=auto_claim
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
def set_session_handoff(
|
|
213
|
-
self,
|
|
214
|
-
handoff_notes: str | None = None,
|
|
215
|
-
recommended_next: str | None = None,
|
|
216
|
-
blockers: list[str] | None = None,
|
|
217
|
-
session_id: str | None = None,
|
|
218
|
-
):
|
|
219
|
-
"""
|
|
220
|
-
Set handoff context on a session.
|
|
221
|
-
|
|
222
|
-
Args:
|
|
223
|
-
handoff_notes: Notes for next session/agent
|
|
224
|
-
recommended_next: Suggested next steps
|
|
225
|
-
blockers: List of blockers
|
|
226
|
-
session_id: Specific session ID (defaults to active session)
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
Updated Session or None if not found
|
|
230
|
-
"""
|
|
231
|
-
if not session_id:
|
|
232
|
-
if self._agent_id:
|
|
233
|
-
active = self.session_manager.get_active_session_for_agent(self._agent_id)
|
|
234
|
-
else:
|
|
235
|
-
active = self.session_manager.get_active_session()
|
|
236
|
-
if not active:
|
|
237
|
-
return None
|
|
238
|
-
session_id = active.id
|
|
239
|
-
|
|
240
|
-
return self.session_manager.set_session_handoff(
|
|
241
|
-
session_id=session_id,
|
|
242
|
-
handoff_notes=handoff_notes,
|
|
243
|
-
recommended_next=recommended_next,
|
|
244
|
-
blockers=blockers,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
def start_session(
|
|
248
|
-
self,
|
|
249
|
-
session_id: str | None = None,
|
|
250
|
-
title: str | None = None,
|
|
251
|
-
agent: str | None = None
|
|
252
|
-
) -> Any:
|
|
253
|
-
"""
|
|
254
|
-
Start a new session.
|
|
255
|
-
|
|
256
|
-
Args:
|
|
257
|
-
session_id: Optional session ID
|
|
258
|
-
title: Optional session title
|
|
259
|
-
agent: Optional agent override (defaults to SDK agent)
|
|
260
|
-
|
|
261
|
-
Returns:
|
|
262
|
-
New Session instance
|
|
263
|
-
"""
|
|
264
|
-
return self.session_manager.start_session(
|
|
265
|
-
session_id=session_id,
|
|
266
|
-
agent=agent or self._agent_id or "cli",
|
|
267
|
-
title=title
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
def end_session(
|
|
271
|
-
self,
|
|
272
|
-
session_id: str,
|
|
273
|
-
handoff_notes: str | None = None,
|
|
274
|
-
recommended_next: str | None = None,
|
|
275
|
-
blockers: list[str] | None = None,
|
|
276
|
-
) -> Any:
|
|
277
|
-
"""
|
|
278
|
-
End a session.
|
|
279
|
-
|
|
280
|
-
Args:
|
|
281
|
-
session_id: Session ID to end
|
|
282
|
-
handoff_notes: Optional handoff notes
|
|
283
|
-
recommended_next: Optional recommendations
|
|
284
|
-
blockers: Optional blockers
|
|
285
|
-
|
|
286
|
-
Returns:
|
|
287
|
-
Ended Session instance
|
|
288
|
-
"""
|
|
289
|
-
return self.session_manager.end_session(
|
|
290
|
-
session_id=session_id,
|
|
291
|
-
handoff_notes=handoff_notes,
|
|
292
|
-
recommended_next=recommended_next,
|
|
293
|
-
blockers=blockers
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
def get_status(self) -> dict[str, Any]:
|
|
297
|
-
"""
|
|
298
|
-
Get project status.
|
|
299
|
-
|
|
300
|
-
Returns:
|
|
301
|
-
Dict with status metrics (WIP, counts, etc.)
|
|
302
|
-
"""
|
|
303
|
-
return self.session_manager.get_status()
|
|
304
|
-
|
|
305
|
-
def dedupe_sessions(
|
|
306
|
-
self,
|
|
307
|
-
max_events: int = 1,
|
|
308
|
-
move_dir_name: str = "_orphans",
|
|
309
|
-
dry_run: bool = False,
|
|
310
|
-
stale_extra_active: bool = True,
|
|
311
|
-
) -> dict[str, int]:
|
|
312
|
-
"""
|
|
313
|
-
Move low-signal sessions (e.g. SessionStart-only) out of the main sessions dir.
|
|
314
|
-
|
|
315
|
-
Args:
|
|
316
|
-
max_events: Maximum events threshold (sessions with <= this many events are moved)
|
|
317
|
-
move_dir_name: Directory name to move orphaned sessions to
|
|
318
|
-
dry_run: If True, only report what would be done without actually moving files
|
|
319
|
-
stale_extra_active: If True, also mark extra active sessions as stale
|
|
320
|
-
|
|
321
|
-
Returns:
|
|
322
|
-
Dict with counts: {"scanned": int, "moved": int, "missing": int, "staled_active": int, "kept_active": int}
|
|
323
|
-
|
|
324
|
-
Example:
|
|
325
|
-
>>> sdk = SDK(agent="claude")
|
|
326
|
-
>>> result = sdk.dedupe_sessions(max_events=1, dry_run=False)
|
|
327
|
-
>>> print(f"Scanned: {result['scanned']}, Moved: {result['moved']}")
|
|
328
|
-
"""
|
|
329
|
-
return self.session_manager.dedupe_orphan_sessions(
|
|
330
|
-
max_events=max_events,
|
|
331
|
-
move_dir_name=move_dir_name,
|
|
332
|
-
dry_run=dry_run,
|
|
333
|
-
stale_extra_active=stale_extra_active,
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
def track_activity(
|
|
337
|
-
self,
|
|
338
|
-
tool: str,
|
|
339
|
-
summary: str,
|
|
340
|
-
file_paths: list[str] | None = None,
|
|
341
|
-
success: bool = True,
|
|
342
|
-
feature_id: str | None = None,
|
|
343
|
-
session_id: str | None = None,
|
|
344
|
-
parent_activity_id: str | None = None,
|
|
345
|
-
payload: dict[str, Any] | None = None,
|
|
346
|
-
) -> Any:
|
|
347
|
-
"""
|
|
348
|
-
Track an activity in the current or specified session.
|
|
349
|
-
|
|
350
|
-
Args:
|
|
351
|
-
tool: Tool name (Edit, Bash, Read, etc.)
|
|
352
|
-
summary: Human-readable summary of the activity
|
|
353
|
-
file_paths: Files involved in this activity
|
|
354
|
-
success: Whether the tool call succeeded
|
|
355
|
-
feature_id: Explicit feature ID (skips attribution if provided)
|
|
356
|
-
session_id: Session ID (defaults to active session for current agent)
|
|
357
|
-
parent_activity_id: ID of parent activity (e.g., Skill/Task invocation)
|
|
358
|
-
payload: Optional rich payload data
|
|
359
|
-
|
|
360
|
-
Returns:
|
|
361
|
-
Created ActivityEntry with attribution
|
|
362
|
-
|
|
363
|
-
Example:
|
|
364
|
-
>>> sdk = SDK(agent="claude")
|
|
365
|
-
>>> entry = sdk.track_activity(
|
|
366
|
-
... tool="CustomTool",
|
|
367
|
-
... summary="Performed custom analysis",
|
|
368
|
-
... file_paths=["src/main.py"],
|
|
369
|
-
... success=True
|
|
370
|
-
... )
|
|
371
|
-
>>> print(f"Tracked: [{entry.tool}] {entry.summary}")
|
|
372
|
-
"""
|
|
373
|
-
# Find active session if not specified
|
|
374
|
-
if not session_id:
|
|
375
|
-
active = self.session_manager.get_active_session(agent=self._agent_id)
|
|
376
|
-
if not active:
|
|
377
|
-
raise ValueError("No active session. Start one with sdk.start_session()")
|
|
378
|
-
session_id = active.id
|
|
379
|
-
|
|
380
|
-
return self.session_manager.track_activity(
|
|
381
|
-
session_id=session_id,
|
|
382
|
-
tool=tool,
|
|
383
|
-
summary=summary,
|
|
384
|
-
file_paths=file_paths,
|
|
385
|
-
success=success,
|
|
386
|
-
feature_id=feature_id,
|
|
387
|
-
parent_activity_id=parent_activity_id,
|
|
388
|
-
payload=payload,
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
# =========================================================================
|
|
392
|
-
# Strategic Planning & Analytics (Agent-Friendly Interface)
|
|
393
|
-
# =========================================================================
|
|
394
|
-
|
|
395
|
-
def find_bottlenecks(self, top_n: int = 5) -> list[dict[str, Any]]:
|
|
396
|
-
"""
|
|
397
|
-
Identify tasks blocking the most downstream work.
|
|
398
|
-
|
|
399
|
-
Args:
|
|
400
|
-
top_n: Maximum number of bottlenecks to return
|
|
401
|
-
|
|
402
|
-
Returns:
|
|
403
|
-
List of bottleneck tasks with impact metrics
|
|
404
|
-
|
|
405
|
-
Example:
|
|
406
|
-
>>> sdk = SDK(agent="claude")
|
|
407
|
-
>>> bottlenecks = sdk.find_bottlenecks(top_n=3)
|
|
408
|
-
>>> for bn in bottlenecks:
|
|
409
|
-
... print(f"{bn['title']} blocks {bn['blocks_count']} tasks")
|
|
410
|
-
"""
|
|
411
|
-
return self._agent_interface.find_bottlenecks(top_n=top_n)
|
|
412
|
-
|
|
413
|
-
def get_parallel_work(self, max_agents: int = 5) -> dict[str, Any]:
|
|
414
|
-
"""
|
|
415
|
-
Find tasks that can be worked on simultaneously.
|
|
416
|
-
|
|
417
|
-
Args:
|
|
418
|
-
max_agents: Maximum number of parallel agents to plan for
|
|
419
|
-
|
|
420
|
-
Returns:
|
|
421
|
-
Dict with parallelization opportunities
|
|
422
|
-
|
|
423
|
-
Example:
|
|
424
|
-
>>> sdk = SDK(agent="claude")
|
|
425
|
-
>>> parallel = sdk.get_parallel_work(max_agents=3)
|
|
426
|
-
>>> print(f"Can work on {parallel['max_parallelism']} tasks at once")
|
|
427
|
-
>>> print(f"Ready now: {parallel['ready_now']}")
|
|
428
|
-
"""
|
|
429
|
-
return self._agent_interface.get_parallel_work(max_agents=max_agents)
|
|
430
|
-
|
|
431
|
-
def recommend_next_work(self, agent_count: int = 1) -> list[dict[str, Any]]:
|
|
432
|
-
"""
|
|
433
|
-
Get smart recommendations for what to work on next.
|
|
434
|
-
|
|
435
|
-
Considers priority, dependencies, and transitive impact.
|
|
436
|
-
|
|
437
|
-
Args:
|
|
438
|
-
agent_count: Number of agents/tasks to recommend
|
|
439
|
-
|
|
440
|
-
Returns:
|
|
441
|
-
List of recommended tasks with reasoning
|
|
442
|
-
|
|
443
|
-
Example:
|
|
444
|
-
>>> sdk = SDK(agent="claude")
|
|
445
|
-
>>> recs = sdk.recommend_next_work(agent_count=3)
|
|
446
|
-
>>> for rec in recs:
|
|
447
|
-
... print(f"{rec['title']} (score: {rec['score']})")
|
|
448
|
-
... print(f" Reasons: {rec['reasons']}")
|
|
449
|
-
"""
|
|
450
|
-
return self._agent_interface.recommend_next_work(agent_count=agent_count)
|
|
451
|
-
|
|
452
|
-
def assess_risks(self) -> dict[str, Any]:
|
|
453
|
-
"""
|
|
454
|
-
Assess dependency-related risks in the project.
|
|
455
|
-
|
|
456
|
-
Identifies single points of failure, circular dependencies,
|
|
457
|
-
and orphaned tasks.
|
|
458
|
-
|
|
459
|
-
Returns:
|
|
460
|
-
Dict with risk assessment results
|
|
461
|
-
|
|
462
|
-
Example:
|
|
463
|
-
>>> sdk = SDK(agent="claude")
|
|
464
|
-
>>> risks = sdk.assess_risks()
|
|
465
|
-
>>> if risks['high_risk_count'] > 0:
|
|
466
|
-
... print(f"Warning: {risks['high_risk_count']} high-risk tasks")
|
|
467
|
-
"""
|
|
468
|
-
return self._agent_interface.assess_risks()
|
|
469
|
-
|
|
470
|
-
def analyze_impact(self, node_id: str) -> dict[str, Any]:
|
|
471
|
-
"""
|
|
472
|
-
Analyze the impact of completing a specific task.
|
|
473
|
-
|
|
474
|
-
Args:
|
|
475
|
-
node_id: Task to analyze
|
|
476
|
-
|
|
477
|
-
Returns:
|
|
478
|
-
Dict with impact analysis
|
|
479
|
-
|
|
480
|
-
Example:
|
|
481
|
-
>>> sdk = SDK(agent="claude")
|
|
482
|
-
>>> impact = sdk.analyze_impact("feature-001")
|
|
483
|
-
>>> print(f"Completing this unlocks {impact['unlocks_count']} tasks")
|
|
484
|
-
"""
|
|
485
|
-
return self._agent_interface.analyze_impact(node_id)
|
|
486
|
-
|
|
487
|
-
# =========================================================================
|
|
488
|
-
# Planning Workflow Integration
|
|
489
|
-
# =========================================================================
|
|
490
|
-
|
|
491
|
-
def start_planning_spike(
|
|
492
|
-
self,
|
|
493
|
-
title: str,
|
|
494
|
-
context: str = "",
|
|
495
|
-
timebox_hours: float = 4.0,
|
|
496
|
-
auto_start: bool = True
|
|
497
|
-
) -> Node:
|
|
498
|
-
"""
|
|
499
|
-
Create a planning spike to research and design before implementation.
|
|
500
|
-
|
|
501
|
-
This is for timeboxed investigation before creating a full track.
|
|
502
|
-
|
|
503
|
-
Args:
|
|
504
|
-
title: Spike title (e.g., "Plan User Authentication System")
|
|
505
|
-
context: Background information
|
|
506
|
-
timebox_hours: Time limit for spike (default: 4 hours)
|
|
507
|
-
auto_start: Automatically start the spike (default: True)
|
|
508
|
-
|
|
509
|
-
Returns:
|
|
510
|
-
Created spike Node
|
|
511
|
-
|
|
512
|
-
Example:
|
|
513
|
-
>>> sdk = SDK(agent="claude")
|
|
514
|
-
>>> spike = sdk.start_planning_spike(
|
|
515
|
-
... "Plan Real-time Notifications",
|
|
516
|
-
... context="Users need live updates. Research options.",
|
|
517
|
-
... timebox_hours=3.0
|
|
518
|
-
... )
|
|
519
|
-
"""
|
|
520
|
-
from htmlgraph.models import Spike, SpikeType
|
|
521
|
-
from htmlgraph.ids import generate_id
|
|
522
|
-
|
|
523
|
-
# Create spike directly (SpikeBuilder doesn't exist yet)
|
|
524
|
-
spike_id = generate_id(node_type="spike", title=title)
|
|
525
|
-
spike = Spike(
|
|
526
|
-
id=spike_id,
|
|
527
|
-
title=title,
|
|
528
|
-
type="spike",
|
|
529
|
-
status="in-progress" if auto_start and self._agent_id else "todo",
|
|
530
|
-
spike_type=SpikeType.ARCHITECTURAL,
|
|
531
|
-
timebox_hours=int(timebox_hours),
|
|
532
|
-
agent_assigned=self._agent_id if auto_start and self._agent_id else None,
|
|
533
|
-
steps=[
|
|
534
|
-
Step(description="Research existing solutions and patterns"),
|
|
535
|
-
Step(description="Define requirements and constraints"),
|
|
536
|
-
Step(description="Design high-level architecture"),
|
|
537
|
-
Step(description="Identify dependencies and risks"),
|
|
538
|
-
Step(description="Create implementation plan")
|
|
539
|
-
],
|
|
540
|
-
content=f"<p>{context}</p>" if context else "",
|
|
541
|
-
edges={},
|
|
542
|
-
properties={}
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
self._graph.add(spike)
|
|
546
|
-
return spike
|
|
547
|
-
|
|
548
|
-
def create_track_from_plan(
|
|
549
|
-
self,
|
|
550
|
-
title: str,
|
|
551
|
-
description: str,
|
|
552
|
-
spike_id: str | None = None,
|
|
553
|
-
priority: str = "high",
|
|
554
|
-
requirements: list[str | tuple[str, str]] | None = None,
|
|
555
|
-
phases: list[tuple[str, list[str]]] | None = None
|
|
556
|
-
) -> dict[str, Any]:
|
|
557
|
-
"""
|
|
558
|
-
Create a track with spec and plan from planning results.
|
|
559
|
-
|
|
560
|
-
Args:
|
|
561
|
-
title: Track title
|
|
562
|
-
description: Track description
|
|
563
|
-
spike_id: Optional spike ID that led to this track
|
|
564
|
-
priority: Track priority (default: "high")
|
|
565
|
-
requirements: List of requirements (strings or (req, priority) tuples)
|
|
566
|
-
phases: List of (phase_name, tasks) tuples for the plan
|
|
567
|
-
|
|
568
|
-
Returns:
|
|
569
|
-
Dict with track, spec, and plan details
|
|
570
|
-
|
|
571
|
-
Example:
|
|
572
|
-
>>> sdk = SDK(agent="claude")
|
|
573
|
-
>>> track_info = sdk.create_track_from_plan(
|
|
574
|
-
... title="User Authentication System",
|
|
575
|
-
... description="OAuth 2.0 with JWT tokens",
|
|
576
|
-
... requirements=[
|
|
577
|
-
... ("OAuth 2.0 integration", "must-have"),
|
|
578
|
-
... ("JWT token management", "must-have"),
|
|
579
|
-
... "Password reset flow"
|
|
580
|
-
... ],
|
|
581
|
-
... phases=[
|
|
582
|
-
... ("Phase 1: OAuth", ["Setup providers (2h)", "Callback (2h)"]),
|
|
583
|
-
... ("Phase 2: JWT", ["Token signing (2h)", "Refresh (1.5h)"])
|
|
584
|
-
... ]
|
|
585
|
-
... )
|
|
586
|
-
"""
|
|
587
|
-
from htmlgraph.track_builder import TrackBuilder
|
|
588
|
-
|
|
589
|
-
builder = self.tracks.builder() \
|
|
590
|
-
.title(title) \
|
|
591
|
-
.description(description) \
|
|
592
|
-
.priority(priority)
|
|
593
|
-
|
|
594
|
-
# Add reference to planning spike if provided
|
|
595
|
-
if spike_id:
|
|
596
|
-
builder._data["properties"]["planning_spike"] = spike_id
|
|
597
|
-
|
|
598
|
-
# Add spec if requirements provided
|
|
599
|
-
if requirements:
|
|
600
|
-
# Convert simple strings to (requirement, "must-have") tuples
|
|
601
|
-
req_list = []
|
|
602
|
-
for req in requirements:
|
|
603
|
-
if isinstance(req, str):
|
|
604
|
-
req_list.append((req, "must-have"))
|
|
605
|
-
else:
|
|
606
|
-
req_list.append(req)
|
|
607
|
-
|
|
608
|
-
builder.with_spec(
|
|
609
|
-
overview=description,
|
|
610
|
-
context=f"Track created from planning spike: {spike_id}" if spike_id else "",
|
|
611
|
-
requirements=req_list,
|
|
612
|
-
acceptance_criteria=[]
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
# Add plan if phases provided
|
|
616
|
-
if phases:
|
|
617
|
-
builder.with_plan_phases(phases)
|
|
618
|
-
|
|
619
|
-
track = builder.create()
|
|
620
|
-
|
|
621
|
-
return {
|
|
622
|
-
"track_id": track.id,
|
|
623
|
-
"title": track.title,
|
|
624
|
-
"has_spec": bool(requirements),
|
|
625
|
-
"has_plan": bool(phases),
|
|
626
|
-
"spike_id": spike_id,
|
|
627
|
-
"priority": priority
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
def smart_plan(
|
|
631
|
-
self,
|
|
632
|
-
description: str,
|
|
633
|
-
create_spike: bool = True,
|
|
634
|
-
timebox_hours: float = 4.0
|
|
635
|
-
) -> dict[str, Any]:
|
|
636
|
-
"""
|
|
637
|
-
Smart planning workflow: analyzes project context and creates spike or track.
|
|
638
|
-
|
|
639
|
-
This is the main entry point for planning new work. It:
|
|
640
|
-
1. Checks current project state
|
|
641
|
-
2. Provides context from strategic analytics
|
|
642
|
-
3. Creates a planning spike or track as appropriate
|
|
643
|
-
|
|
644
|
-
Args:
|
|
645
|
-
description: What you want to plan (e.g., "User authentication system")
|
|
646
|
-
create_spike: Create a spike for research (default: True)
|
|
647
|
-
timebox_hours: If creating spike, time limit (default: 4 hours)
|
|
648
|
-
|
|
649
|
-
Returns:
|
|
650
|
-
Dict with planning context and created spike/track info
|
|
651
|
-
|
|
652
|
-
Example:
|
|
653
|
-
>>> sdk = SDK(agent="claude")
|
|
654
|
-
>>> plan = sdk.smart_plan(
|
|
655
|
-
... "Real-time notifications system",
|
|
656
|
-
... create_spike=True
|
|
657
|
-
... )
|
|
658
|
-
>>> print(f"Created: {plan['spike_id']}")
|
|
659
|
-
>>> print(f"Context: {plan['project_context']}")
|
|
660
|
-
"""
|
|
661
|
-
# Get project context from strategic analytics
|
|
662
|
-
bottlenecks = self.find_bottlenecks(top_n=3)
|
|
663
|
-
risks = self.assess_risks()
|
|
664
|
-
parallel = self.get_parallel_work(max_agents=5)
|
|
665
|
-
|
|
666
|
-
context = {
|
|
667
|
-
"bottlenecks_count": len(bottlenecks),
|
|
668
|
-
"high_risk_count": risks["high_risk_count"],
|
|
669
|
-
"parallel_capacity": parallel["max_parallelism"],
|
|
670
|
-
"description": description
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
if create_spike:
|
|
674
|
-
spike = self.start_planning_spike(
|
|
675
|
-
title=f"Plan: {description}",
|
|
676
|
-
context=f"Project context:\n- {len(bottlenecks)} bottlenecks\n- {risks['high_risk_count']} high-risk items\n- {parallel['max_parallelism']} parallel capacity",
|
|
677
|
-
timebox_hours=timebox_hours
|
|
678
|
-
)
|
|
679
|
-
|
|
680
|
-
return {
|
|
681
|
-
"type": "spike",
|
|
682
|
-
"spike_id": spike.id,
|
|
683
|
-
"title": spike.title,
|
|
684
|
-
"status": spike.status,
|
|
685
|
-
"timebox_hours": timebox_hours,
|
|
686
|
-
"project_context": context,
|
|
687
|
-
"next_steps": [
|
|
688
|
-
"Research and design the solution",
|
|
689
|
-
"Complete spike steps",
|
|
690
|
-
"Use SDK.create_track_from_plan() to create track"
|
|
691
|
-
]
|
|
692
|
-
}
|
|
693
|
-
else:
|
|
694
|
-
# Direct track creation (for when you already know what to do)
|
|
695
|
-
track_info = self.create_track_from_plan(
|
|
696
|
-
title=description,
|
|
697
|
-
description=f"Planned with context: {context}"
|
|
698
|
-
)
|
|
699
|
-
|
|
700
|
-
return {
|
|
701
|
-
"type": "track",
|
|
702
|
-
**track_info,
|
|
703
|
-
"project_context": context,
|
|
704
|
-
"next_steps": [
|
|
705
|
-
"Create features from track plan",
|
|
706
|
-
"Link features to track",
|
|
707
|
-
"Start implementation"
|
|
708
|
-
]
|
|
709
|
-
}
|