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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +173 -17
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +127 -0
  7. htmlgraph/agent_registry.py +45 -30
  8. htmlgraph/agents.py +160 -107
  9. htmlgraph/analytics/__init__.py +9 -2
  10. htmlgraph/analytics/cli.py +190 -51
  11. htmlgraph/analytics/cost_analyzer.py +391 -0
  12. htmlgraph/analytics/cost_monitor.py +664 -0
  13. htmlgraph/analytics/cost_reporter.py +675 -0
  14. htmlgraph/analytics/cross_session.py +617 -0
  15. htmlgraph/analytics/dependency.py +192 -100
  16. htmlgraph/analytics/pattern_learning.py +771 -0
  17. htmlgraph/analytics/session_graph.py +707 -0
  18. htmlgraph/analytics/strategic/__init__.py +80 -0
  19. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  20. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  21. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  22. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  23. htmlgraph/analytics/work_type.py +190 -14
  24. htmlgraph/analytics_index.py +135 -51
  25. htmlgraph/api/__init__.py +3 -0
  26. htmlgraph/api/cost_alerts_websocket.py +416 -0
  27. htmlgraph/api/main.py +2498 -0
  28. htmlgraph/api/static/htmx.min.js +1 -0
  29. htmlgraph/api/static/style-redesign.css +1344 -0
  30. htmlgraph/api/static/style.css +1079 -0
  31. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  32. htmlgraph/api/templates/dashboard.html +794 -0
  33. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  34. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  35. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  36. htmlgraph/api/templates/partials/agents.html +317 -0
  37. htmlgraph/api/templates/partials/event-traces.html +373 -0
  38. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  39. htmlgraph/api/templates/partials/features.html +578 -0
  40. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  41. htmlgraph/api/templates/partials/metrics.html +346 -0
  42. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  43. htmlgraph/api/templates/partials/orchestration.html +198 -0
  44. htmlgraph/api/templates/partials/spawners.html +375 -0
  45. htmlgraph/api/templates/partials/work-items.html +613 -0
  46. htmlgraph/api/websocket.py +538 -0
  47. htmlgraph/archive/__init__.py +24 -0
  48. htmlgraph/archive/bloom.py +234 -0
  49. htmlgraph/archive/fts.py +297 -0
  50. htmlgraph/archive/manager.py +583 -0
  51. htmlgraph/archive/search.py +244 -0
  52. htmlgraph/atomic_ops.py +560 -0
  53. htmlgraph/attribute_index.py +208 -0
  54. htmlgraph/bounded_paths.py +539 -0
  55. htmlgraph/builders/__init__.py +14 -0
  56. htmlgraph/builders/base.py +118 -29
  57. htmlgraph/builders/bug.py +150 -0
  58. htmlgraph/builders/chore.py +119 -0
  59. htmlgraph/builders/epic.py +150 -0
  60. htmlgraph/builders/feature.py +31 -6
  61. htmlgraph/builders/insight.py +195 -0
  62. htmlgraph/builders/metric.py +217 -0
  63. htmlgraph/builders/pattern.py +202 -0
  64. htmlgraph/builders/phase.py +162 -0
  65. htmlgraph/builders/spike.py +52 -19
  66. htmlgraph/builders/track.py +148 -72
  67. htmlgraph/cigs/__init__.py +81 -0
  68. htmlgraph/cigs/autonomy.py +385 -0
  69. htmlgraph/cigs/cost.py +475 -0
  70. htmlgraph/cigs/messages_basic.py +472 -0
  71. htmlgraph/cigs/messaging.py +365 -0
  72. htmlgraph/cigs/models.py +771 -0
  73. htmlgraph/cigs/pattern_storage.py +427 -0
  74. htmlgraph/cigs/patterns.py +503 -0
  75. htmlgraph/cigs/posttool_analyzer.py +234 -0
  76. htmlgraph/cigs/reporter.py +818 -0
  77. htmlgraph/cigs/tracker.py +317 -0
  78. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  79. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  80. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  81. htmlgraph/cli/__init__.py +42 -0
  82. htmlgraph/cli/__main__.py +6 -0
  83. htmlgraph/cli/analytics.py +1424 -0
  84. htmlgraph/cli/base.py +685 -0
  85. htmlgraph/cli/constants.py +206 -0
  86. htmlgraph/cli/core.py +954 -0
  87. htmlgraph/cli/main.py +147 -0
  88. htmlgraph/cli/models.py +475 -0
  89. htmlgraph/cli/templates/__init__.py +1 -0
  90. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  91. htmlgraph/cli/work/__init__.py +239 -0
  92. htmlgraph/cli/work/browse.py +115 -0
  93. htmlgraph/cli/work/features.py +568 -0
  94. htmlgraph/cli/work/orchestration.py +676 -0
  95. htmlgraph/cli/work/report.py +728 -0
  96. htmlgraph/cli/work/sessions.py +466 -0
  97. htmlgraph/cli/work/snapshot.py +559 -0
  98. htmlgraph/cli/work/tracks.py +486 -0
  99. htmlgraph/cli_commands/__init__.py +1 -0
  100. htmlgraph/cli_commands/feature.py +195 -0
  101. htmlgraph/cli_framework.py +115 -0
  102. htmlgraph/collections/__init__.py +18 -0
  103. htmlgraph/collections/base.py +415 -98
  104. htmlgraph/collections/bug.py +53 -0
  105. htmlgraph/collections/chore.py +53 -0
  106. htmlgraph/collections/epic.py +53 -0
  107. htmlgraph/collections/feature.py +12 -26
  108. htmlgraph/collections/insight.py +100 -0
  109. htmlgraph/collections/metric.py +92 -0
  110. htmlgraph/collections/pattern.py +97 -0
  111. htmlgraph/collections/phase.py +53 -0
  112. htmlgraph/collections/session.py +194 -0
  113. htmlgraph/collections/spike.py +56 -16
  114. htmlgraph/collections/task_delegation.py +241 -0
  115. htmlgraph/collections/todo.py +511 -0
  116. htmlgraph/collections/traces.py +487 -0
  117. htmlgraph/config/cost_models.json +56 -0
  118. htmlgraph/config.py +190 -0
  119. htmlgraph/context_analytics.py +344 -0
  120. htmlgraph/converter.py +216 -28
  121. htmlgraph/cost_analysis/__init__.py +5 -0
  122. htmlgraph/cost_analysis/analyzer.py +438 -0
  123. htmlgraph/dashboard.html +2406 -307
  124. htmlgraph/dashboard.html.backup +6592 -0
  125. htmlgraph/dashboard.html.bak +7181 -0
  126. htmlgraph/dashboard.html.bak2 +7231 -0
  127. htmlgraph/dashboard.html.bak3 +7232 -0
  128. htmlgraph/db/__init__.py +38 -0
  129. htmlgraph/db/queries.py +790 -0
  130. htmlgraph/db/schema.py +1788 -0
  131. htmlgraph/decorators.py +317 -0
  132. htmlgraph/dependency_models.py +19 -2
  133. htmlgraph/deploy.py +142 -125
  134. htmlgraph/deployment_models.py +474 -0
  135. htmlgraph/docs/API_REFERENCE.md +841 -0
  136. htmlgraph/docs/HTTP_API.md +750 -0
  137. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  138. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  139. htmlgraph/docs/README.md +532 -0
  140. htmlgraph/docs/__init__.py +77 -0
  141. htmlgraph/docs/docs_version.py +55 -0
  142. htmlgraph/docs/metadata.py +93 -0
  143. htmlgraph/docs/migrations.py +232 -0
  144. htmlgraph/docs/template_engine.py +143 -0
  145. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  146. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  147. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  148. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  149. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  150. htmlgraph/docs/version_check.py +163 -0
  151. htmlgraph/edge_index.py +182 -27
  152. htmlgraph/error_handler.py +544 -0
  153. htmlgraph/event_log.py +100 -52
  154. htmlgraph/event_migration.py +13 -4
  155. htmlgraph/exceptions.py +49 -0
  156. htmlgraph/file_watcher.py +101 -28
  157. htmlgraph/find_api.py +75 -63
  158. htmlgraph/git_events.py +145 -63
  159. htmlgraph/graph.py +1122 -106
  160. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  161. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  162. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  163. htmlgraph/hooks/__init__.py +45 -0
  164. htmlgraph/hooks/bootstrap.py +169 -0
  165. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  166. htmlgraph/hooks/concurrent_sessions.py +208 -0
  167. htmlgraph/hooks/context.py +350 -0
  168. htmlgraph/hooks/drift_handler.py +525 -0
  169. htmlgraph/hooks/event_tracker.py +1314 -0
  170. htmlgraph/hooks/git_commands.py +175 -0
  171. htmlgraph/hooks/hooks-config.example.json +12 -0
  172. htmlgraph/hooks/installer.py +343 -0
  173. htmlgraph/hooks/orchestrator.py +674 -0
  174. htmlgraph/hooks/orchestrator_reflector.py +223 -0
  175. htmlgraph/hooks/post-checkout.sh +28 -0
  176. htmlgraph/hooks/post-commit.sh +24 -0
  177. htmlgraph/hooks/post-merge.sh +26 -0
  178. htmlgraph/hooks/post_tool_use_failure.py +273 -0
  179. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  180. htmlgraph/hooks/posttooluse.py +408 -0
  181. htmlgraph/hooks/pre-commit.sh +94 -0
  182. htmlgraph/hooks/pre-push.sh +28 -0
  183. htmlgraph/hooks/pretooluse.py +819 -0
  184. htmlgraph/hooks/prompt_analyzer.py +637 -0
  185. htmlgraph/hooks/session_handler.py +668 -0
  186. htmlgraph/hooks/session_summary.py +395 -0
  187. htmlgraph/hooks/state_manager.py +504 -0
  188. htmlgraph/hooks/subagent_detection.py +202 -0
  189. htmlgraph/hooks/subagent_stop.py +369 -0
  190. htmlgraph/hooks/task_enforcer.py +255 -0
  191. htmlgraph/hooks/task_validator.py +177 -0
  192. htmlgraph/hooks/validator.py +628 -0
  193. htmlgraph/ids.py +41 -27
  194. htmlgraph/index.d.ts +286 -0
  195. htmlgraph/learning.py +767 -0
  196. htmlgraph/mcp_server.py +69 -23
  197. htmlgraph/models.py +1586 -87
  198. htmlgraph/operations/README.md +62 -0
  199. htmlgraph/operations/__init__.py +79 -0
  200. htmlgraph/operations/analytics.py +339 -0
  201. htmlgraph/operations/bootstrap.py +289 -0
  202. htmlgraph/operations/events.py +244 -0
  203. htmlgraph/operations/fastapi_server.py +231 -0
  204. htmlgraph/operations/hooks.py +350 -0
  205. htmlgraph/operations/initialization.py +597 -0
  206. htmlgraph/operations/initialization.py.backup +228 -0
  207. htmlgraph/operations/server.py +303 -0
  208. htmlgraph/orchestration/__init__.py +58 -0
  209. htmlgraph/orchestration/claude_launcher.py +179 -0
  210. htmlgraph/orchestration/command_builder.py +72 -0
  211. htmlgraph/orchestration/headless_spawner.py +281 -0
  212. htmlgraph/orchestration/live_events.py +377 -0
  213. htmlgraph/orchestration/model_selection.py +327 -0
  214. htmlgraph/orchestration/plugin_manager.py +140 -0
  215. htmlgraph/orchestration/prompts.py +137 -0
  216. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  217. htmlgraph/orchestration/spawners/__init__.py +16 -0
  218. htmlgraph/orchestration/spawners/base.py +194 -0
  219. htmlgraph/orchestration/spawners/claude.py +173 -0
  220. htmlgraph/orchestration/spawners/codex.py +435 -0
  221. htmlgraph/orchestration/spawners/copilot.py +294 -0
  222. htmlgraph/orchestration/spawners/gemini.py +471 -0
  223. htmlgraph/orchestration/subprocess_runner.py +36 -0
  224. htmlgraph/orchestration/task_coordination.py +343 -0
  225. htmlgraph/orchestration.md +563 -0
  226. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  227. htmlgraph/orchestrator.py +669 -0
  228. htmlgraph/orchestrator_config.py +357 -0
  229. htmlgraph/orchestrator_mode.py +328 -0
  230. htmlgraph/orchestrator_validator.py +133 -0
  231. htmlgraph/parallel.py +646 -0
  232. htmlgraph/parser.py +160 -35
  233. htmlgraph/path_query.py +608 -0
  234. htmlgraph/pattern_matcher.py +636 -0
  235. htmlgraph/planning.py +147 -52
  236. htmlgraph/pydantic_models.py +476 -0
  237. htmlgraph/quality_gates.py +350 -0
  238. htmlgraph/query_builder.py +109 -72
  239. htmlgraph/query_composer.py +509 -0
  240. htmlgraph/reflection.py +443 -0
  241. htmlgraph/refs.py +344 -0
  242. htmlgraph/repo_hash.py +512 -0
  243. htmlgraph/repositories/__init__.py +292 -0
  244. htmlgraph/repositories/analytics_repository.py +455 -0
  245. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  246. htmlgraph/repositories/feature_repository.py +581 -0
  247. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  248. htmlgraph/repositories/feature_repository_memory.py +607 -0
  249. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  250. htmlgraph/repositories/filter_service.py +620 -0
  251. htmlgraph/repositories/filter_service_standard.py +445 -0
  252. htmlgraph/repositories/shared_cache.py +621 -0
  253. htmlgraph/repositories/shared_cache_memory.py +395 -0
  254. htmlgraph/repositories/track_repository.py +552 -0
  255. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  256. htmlgraph/repositories/track_repository_memory.py +508 -0
  257. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  258. htmlgraph/routing.py +8 -19
  259. htmlgraph/scripts/deploy.py +1 -2
  260. htmlgraph/sdk/__init__.py +398 -0
  261. htmlgraph/sdk/__init__.pyi +14 -0
  262. htmlgraph/sdk/analytics/__init__.py +19 -0
  263. htmlgraph/sdk/analytics/engine.py +155 -0
  264. htmlgraph/sdk/analytics/helpers.py +178 -0
  265. htmlgraph/sdk/analytics/registry.py +109 -0
  266. htmlgraph/sdk/base.py +484 -0
  267. htmlgraph/sdk/constants.py +216 -0
  268. htmlgraph/sdk/core.pyi +308 -0
  269. htmlgraph/sdk/discovery.py +120 -0
  270. htmlgraph/sdk/help/__init__.py +12 -0
  271. htmlgraph/sdk/help/mixin.py +699 -0
  272. htmlgraph/sdk/mixins/__init__.py +15 -0
  273. htmlgraph/sdk/mixins/attribution.py +113 -0
  274. htmlgraph/sdk/mixins/mixin.py +410 -0
  275. htmlgraph/sdk/operations/__init__.py +12 -0
  276. htmlgraph/sdk/operations/mixin.py +427 -0
  277. htmlgraph/sdk/orchestration/__init__.py +17 -0
  278. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  279. htmlgraph/sdk/orchestration/spawner.py +204 -0
  280. htmlgraph/sdk/planning/__init__.py +19 -0
  281. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  282. htmlgraph/sdk/planning/mixin.py +211 -0
  283. htmlgraph/sdk/planning/parallel.py +186 -0
  284. htmlgraph/sdk/planning/queue.py +210 -0
  285. htmlgraph/sdk/planning/recommendations.py +87 -0
  286. htmlgraph/sdk/planning/smart_planning.py +319 -0
  287. htmlgraph/sdk/session/__init__.py +19 -0
  288. htmlgraph/sdk/session/continuity.py +57 -0
  289. htmlgraph/sdk/session/handoff.py +110 -0
  290. htmlgraph/sdk/session/info.py +309 -0
  291. htmlgraph/sdk/session/manager.py +103 -0
  292. htmlgraph/sdk/strategic/__init__.py +26 -0
  293. htmlgraph/sdk/strategic/mixin.py +563 -0
  294. htmlgraph/server.py +685 -180
  295. htmlgraph/services/__init__.py +10 -0
  296. htmlgraph/services/claiming.py +199 -0
  297. htmlgraph/session_hooks.py +300 -0
  298. htmlgraph/session_manager.py +1392 -175
  299. htmlgraph/session_registry.py +587 -0
  300. htmlgraph/session_state.py +436 -0
  301. htmlgraph/session_warning.py +201 -0
  302. htmlgraph/sessions/__init__.py +23 -0
  303. htmlgraph/sessions/handoff.py +756 -0
  304. htmlgraph/setup.py +34 -17
  305. htmlgraph/spike_index.py +143 -0
  306. htmlgraph/sync_docs.py +12 -15
  307. htmlgraph/system_prompts.py +450 -0
  308. htmlgraph/templates/AGENTS.md.template +366 -0
  309. htmlgraph/templates/CLAUDE.md.template +97 -0
  310. htmlgraph/templates/GEMINI.md.template +87 -0
  311. htmlgraph/templates/orchestration-view.html +350 -0
  312. htmlgraph/track_builder.py +146 -15
  313. htmlgraph/track_manager.py +69 -21
  314. htmlgraph/transcript.py +890 -0
  315. htmlgraph/transcript_analytics.py +699 -0
  316. htmlgraph/types.py +323 -0
  317. htmlgraph/validation.py +115 -0
  318. htmlgraph/watch.py +8 -5
  319. htmlgraph/work_type_utils.py +3 -2
  320. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
  321. htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
  322. htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
  323. htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
  324. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
  325. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  326. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  327. htmlgraph/cli.py +0 -2688
  328. htmlgraph/sdk.py +0 -709
  329. htmlgraph-0.9.3.dist-info/RECORD +0 -61
  330. {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  331. {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,669 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ SubagentOrchestrator for context-preserving delegation.
5
+
6
+ IMPERATIVE USAGE INSTRUCTIONS
7
+ =============================
8
+
9
+ As an orchestrator, you MUST follow these steps:
10
+
11
+ 1. INITIALIZE
12
+ ```python
13
+ from htmlgraph import SDK
14
+ sdk = SDK(agent="claude")
15
+ ```
16
+
17
+ 2. SPAWN EXPLORER (for codebase discovery)
18
+ ```python
19
+ explorer = sdk.spawn_explorer(
20
+ task="Find all API endpoints",
21
+ scope="src/api/"
22
+ )
23
+ # Use with Task tool:
24
+ # Task(prompt=explorer["prompt"], subagent_type=explorer["subagent_type"])
25
+ ```
26
+
27
+ 3. SPAWN CODER (for implementation)
28
+ ```python
29
+ coder = sdk.spawn_coder(
30
+ feature_id="feat-123",
31
+ context="Explorer found endpoints in src/api/routes.py",
32
+ test_command="uv run pytest"
33
+ )
34
+ # Use with Task tool:
35
+ # Task(prompt=coder["prompt"], subagent_type=coder["subagent_type"])
36
+ ```
37
+
38
+ 4. FULL ORCHESTRATION (explore + implement)
39
+ ```python
40
+ prompts = sdk.orchestrate(
41
+ feature_id="feat-123",
42
+ exploration_scope="src/",
43
+ test_command="uv run pytest"
44
+ )
45
+ # Returns: {"explorer": {...}, "coder": {...}}
46
+ ```
47
+
48
+ DECISION GUIDE
49
+ ==============
50
+
51
+ | Scenario | Method |
52
+ |----------|--------|
53
+ | Unknown codebase | spawn_explorer first, then spawn_coder |
54
+ | Known codebase | spawn_coder directly |
55
+ | Complex feature | orchestrate for full workflow |
56
+ | Multiple features | spawn_coder in parallel |
57
+
58
+ ANTI-PATTERNS
59
+ =============
60
+
61
+ NEVER:
62
+ - Implement without exploration on unknown codebases
63
+ - Spawn coder without feature_id (create feature first!)
64
+ - Edit code yourself when you should delegate
65
+
66
+ ALWAYS:
67
+ - Create work item before spawning coder
68
+ - Pass explorer context to coder
69
+ - Let subagents do the heavy lifting
70
+
71
+ Available Classes
72
+ =================
73
+
74
+ SubagentType: Enum of subagent types (EXPLORER, CODER, REVIEWER, TESTER)
75
+ SubagentPrompt: Prepared prompt for spawning subagent via Task tool
76
+ SubagentResult: Parsed results from subagent execution
77
+ SubagentOrchestrator: Main orchestration class
78
+
79
+ Key Patterns
80
+ ============
81
+
82
+ 1. Two-phase workflow: Explorer discovers → Coder implements
83
+ 2. Stateless subagents: Each spawned agent is ephemeral and task-focused
84
+ 3. Context efficiency: Main session reserves context for orchestration
85
+ 4. Parallel execution: Multiple subagents can work simultaneously
86
+ """
87
+
88
+
89
+ from dataclasses import dataclass, field
90
+ from datetime import datetime
91
+ from enum import Enum
92
+ from typing import TYPE_CHECKING, Any
93
+
94
+ if TYPE_CHECKING:
95
+ from htmlgraph.sdk import SDK
96
+
97
+
98
+ class SubagentType(Enum):
99
+ """Types of specialized subagents."""
100
+
101
+ EXPLORER = "explorer"
102
+ CODER = "coder"
103
+ REVIEWER = "reviewer"
104
+ TESTER = "tester"
105
+
106
+
107
+ @dataclass
108
+ class SubagentPrompt:
109
+ """A prepared prompt for spawning a subagent via Task tool."""
110
+
111
+ prompt: str
112
+ description: str
113
+ subagent_type: str
114
+ task_id: str | None = None
115
+
116
+ # Expected outputs
117
+ expected_sections: list[str] = field(default_factory=list)
118
+
119
+ # Retry configuration
120
+ max_retries: int = 2
121
+ retry_delay_seconds: int = 5
122
+
123
+ def to_task_kwargs(self) -> dict[str, Any]:
124
+ """Convert to kwargs for Task tool invocation."""
125
+ return {
126
+ "prompt": self.prompt,
127
+ "description": self.description,
128
+ "subagent_type": self.subagent_type,
129
+ }
130
+
131
+
132
+ @dataclass
133
+ class SubagentResult:
134
+ """Parsed result from a subagent execution."""
135
+
136
+ subagent_type: SubagentType
137
+ task_id: str | None
138
+ success: bool
139
+
140
+ # Parsed outputs
141
+ summary: str = ""
142
+ files_found: list[str] = field(default_factory=list)
143
+ files_modified: list[str] = field(default_factory=list)
144
+ patterns_discovered: dict[str, list[str]] = field(default_factory=dict)
145
+ blockers: list[str] = field(default_factory=list)
146
+ recommendations: list[str] = field(default_factory=list)
147
+
148
+ # Raw output for debugging
149
+ raw_output: str = ""
150
+
151
+ # Metrics
152
+ duration_seconds: float = 0.0
153
+ tool_calls: int = 0
154
+
155
+
156
+ class SubagentOrchestrator:
157
+ """
158
+ Orchestrates specialized subagents for exploration and coding tasks.
159
+
160
+ Benefits:
161
+ - Main session context reserved for orchestration decisions
162
+ - Parallel exploration and coding via ephemeral subagents
163
+ - Better context efficiency (subagents are stateless)
164
+ - More tasks completed before main context fills up
165
+
166
+ Example:
167
+ >>> orchestrator = SubagentOrchestrator(sdk)
168
+ >>>
169
+ >>> # Phase 1: Explore codebase
170
+ >>> explorer = orchestrator.spawn_explorer(
171
+ ... task="Map the authentication system",
172
+ ... scope="src/auth/",
173
+ ... )
174
+ >>> # Execute with Task tool, get results
175
+ >>>
176
+ >>> # Phase 2: Implement changes
177
+ >>> coder = orchestrator.spawn_coder(
178
+ ... feature_id="feat-auth-fix",
179
+ ... context=explorer_results,
180
+ ... )
181
+ """
182
+
183
+ def __init__(self, sdk: SDK):
184
+ """
185
+ Initialize orchestrator.
186
+
187
+ Args:
188
+ sdk: Parent SDK instance for accessing features and tracking
189
+ """
190
+ self.sdk = sdk
191
+ self._directory = sdk._directory
192
+
193
+ def spawn_explorer(
194
+ self,
195
+ task: str,
196
+ scope: str | None = None,
197
+ patterns: list[str] | None = None,
198
+ questions: list[str] | None = None,
199
+ max_files: int = 50,
200
+ include_tests: bool = False,
201
+ ) -> SubagentPrompt:
202
+ """
203
+ Spawn an explorer subagent for codebase discovery and analysis.
204
+
205
+ Explorer agents are optimized for:
206
+ - Finding files matching patterns
207
+ - Searching for code patterns
208
+ - Mapping dependencies and relationships
209
+ - Answering architectural questions
210
+
211
+ Args:
212
+ task: What to explore/discover
213
+ scope: Directory scope (e.g., "src/", "tests/")
214
+ patterns: Glob patterns to focus on (e.g., ["**/*.py"])
215
+ questions: Specific questions to answer
216
+ max_files: Maximum files to read in detail
217
+ include_tests: Whether to include test files
218
+
219
+ Returns:
220
+ SubagentPrompt ready for Task tool
221
+
222
+ Example:
223
+ >>> prompt = orchestrator.spawn_explorer(
224
+ ... task="Find all database models and their relationships",
225
+ ... scope="src/models/",
226
+ ... patterns=["**/*.py"],
227
+ ... questions=["What ORM is used?", "How are relationships defined?"]
228
+ ... )
229
+ """
230
+ # Build scope directive
231
+ scope_directive = ""
232
+ if scope:
233
+ scope_directive = f"Focus on: {scope}"
234
+ if patterns:
235
+ scope_directive += f"\nPatterns: {', '.join(patterns)}"
236
+
237
+ # Build questions section
238
+ questions_section = ""
239
+ if questions:
240
+ questions_section = "## Questions to Answer\n" + "\n".join(
241
+ f"- {q}" for q in questions
242
+ )
243
+
244
+ prompt = f"""# Explorer Task: {task}
245
+
246
+ You are an EXPLORER subagent. Your job is to discover and analyze code, NOT modify it.
247
+
248
+ ## Scope
249
+ {scope_directive or "Entire codebase"}
250
+ Max files to read in detail: {max_files}
251
+ Include tests: {include_tests}
252
+
253
+ {questions_section}
254
+
255
+ ## Efficient Exploration Strategy
256
+
257
+ 1. **Start with Glob** to find relevant files:
258
+ - Use Glob with patterns like "{patterns[0] if patterns else "**/*.py"}"
259
+ - This is faster than recursive directory exploration
260
+
261
+ 2. **Use Grep for targeted search**:
262
+ - Search for keywords, class names, function signatures
263
+ - This finds exact locations without reading entire files
264
+
265
+ 3. **Read strategically**:
266
+ - Only read files that Grep identified as relevant
267
+ - Read imports and class definitions first
268
+ - Skip boilerplate and generated code
269
+
270
+ 4. **Map relationships**:
271
+ - Note imports and dependencies
272
+ - Identify inheritance and composition
273
+ - Document API boundaries
274
+
275
+ ## Output Format
276
+
277
+ Return your findings in this exact format:
278
+
279
+ ## Summary
280
+ [2-3 sentence overview of what you found]
281
+
282
+ ## Files Found
283
+ - file1.py: [brief description]
284
+ - file2.py: [brief description]
285
+ ...
286
+
287
+ ## Key Patterns
288
+ ### Pattern Name
289
+ - Description of pattern
290
+ - Where it's used
291
+ - Example code
292
+
293
+ ## Answers
294
+ [Answer each question from Questions to Answer section]
295
+
296
+ ## Recommendations
297
+ - [Suggestion for the coder agent]
298
+ - [Potential issues to watch for]
299
+ """
300
+
301
+ return SubagentPrompt(
302
+ prompt=prompt,
303
+ description=f"Explore: {task[:40]}",
304
+ subagent_type="Explore", # Use the Explore agent type
305
+ expected_sections=["Summary", "Files Found", "Key Patterns"],
306
+ max_retries=1,
307
+ )
308
+
309
+ def spawn_coder(
310
+ self,
311
+ feature_id: str,
312
+ context: str | SubagentResult | None = None,
313
+ files_to_modify: list[str] | None = None,
314
+ test_command: str | None = None,
315
+ style_guide: str | None = None,
316
+ ) -> SubagentPrompt:
317
+ """
318
+ Spawn a coder subagent for implementing changes.
319
+
320
+ Coder agents are optimized for:
321
+ - Reading and modifying specific files
322
+ - Following patterns from exploration
323
+ - Running and fixing tests
324
+ - Maintaining code quality
325
+
326
+ Args:
327
+ feature_id: Feature being implemented
328
+ context: Results from explorer (string or SubagentResult)
329
+ files_to_modify: Specific files to change
330
+ test_command: Command to verify changes
331
+ style_guide: Code style guidelines
332
+
333
+ Returns:
334
+ SubagentPrompt ready for Task tool
335
+
336
+ Example:
337
+ >>> prompt = orchestrator.spawn_coder(
338
+ ... feature_id="feat-add-auth",
339
+ ... context=explorer_results,
340
+ ... test_command="uv run pytest tests/auth/"
341
+ ... )
342
+ """
343
+ # Get feature details
344
+ feature = self.sdk.features.get(feature_id)
345
+ if not feature:
346
+ raise ValueError(f"Feature not found: {feature_id}")
347
+
348
+ # Build context section
349
+ context_section = ""
350
+ if isinstance(context, SubagentResult):
351
+ context_section = f"""## Context from Explorer
352
+
353
+ ### Summary
354
+ {context.summary}
355
+
356
+ ### Files to Consider
357
+ {chr(10).join(f"- {f}" for f in context.files_found[:20])}
358
+
359
+ ### Patterns Discovered
360
+ {chr(10).join(f"- {k}: {v}" for k, v in list(context.patterns_discovered.items())[:5])}
361
+
362
+ ### Recommendations
363
+ {chr(10).join(f"- {r}" for r in context.recommendations)}
364
+ """
365
+ elif isinstance(context, str):
366
+ context_section = f"## Context from Explorer\n\n{context}"
367
+
368
+ # Build files section
369
+ files_section = ""
370
+ if files_to_modify:
371
+ files_section = "## Target Files\n" + "\n".join(
372
+ f"- {f}" for f in files_to_modify
373
+ )
374
+
375
+ # Build test section
376
+ test_section = ""
377
+ if test_command:
378
+ test_section = f"""## Testing
379
+
380
+ Run tests after changes:
381
+ ```bash
382
+ {test_command}
383
+ ```
384
+
385
+ If tests fail, fix the issues before completing.
386
+ """
387
+
388
+ # Build style section
389
+ style_section = ""
390
+ if style_guide:
391
+ style_section = f"## Style Guide\n\n{style_guide}"
392
+
393
+ # Get feature steps
394
+ steps_section = ""
395
+ if feature.steps:
396
+ steps_lines = []
397
+ for i, step in enumerate(feature.steps, 1):
398
+ status = "✅" if step.completed else "⏳"
399
+ steps_lines.append(f"{i}. {status} {step.description}")
400
+ steps_section = "## Implementation Steps\n" + "\n".join(steps_lines)
401
+
402
+ prompt = f"""# Coder Task: {feature.title}
403
+
404
+ You are a CODER subagent. Your job is to implement changes efficiently.
405
+
406
+ Feature ID: {feature_id}
407
+ Priority: {feature.priority}
408
+ Status: {feature.status}
409
+
410
+ {context_section}
411
+
412
+ {files_section}
413
+
414
+ {steps_section}
415
+
416
+ {style_section}
417
+
418
+ ## Efficient Implementation Strategy
419
+
420
+ 1. **Read before Edit**:
421
+ - Read the target file first
422
+ - Understand existing patterns
423
+ - Plan your changes
424
+
425
+ 2. **Batch Edits**:
426
+ - Make multiple related changes in sequence
427
+ - Don't switch between files unnecessarily
428
+
429
+ 3. **Test incrementally**:
430
+ - Run tests after significant changes
431
+ - Fix issues immediately
432
+
433
+ 4. **Update feature tracking**:
434
+ - Mark steps as complete as you go
435
+ - Note any blockers
436
+
437
+ {test_section}
438
+
439
+ ## Output Format
440
+
441
+ Return your results in this exact format:
442
+
443
+ ## Summary
444
+ [What was implemented]
445
+
446
+ ## Files Modified
447
+ - file1.py: [what changed]
448
+ - file2.py: [what changed]
449
+
450
+ ## Tests
451
+ [Test results - PASS/FAIL with details]
452
+
453
+ ## Blockers
454
+ [Any issues preventing completion, or "None"]
455
+
456
+ ## Status
457
+ [COMPLETE or IN_PROGRESS with next steps]
458
+ """
459
+
460
+ return SubagentPrompt(
461
+ prompt=prompt,
462
+ description=f"Code: {feature.title[:40]}",
463
+ subagent_type="general-purpose",
464
+ task_id=feature_id,
465
+ expected_sections=["Summary", "Files Modified", "Status"],
466
+ max_retries=2,
467
+ )
468
+
469
+ def parse_explorer_result(self, output: str) -> SubagentResult:
470
+ """
471
+ Parse the output from an explorer subagent.
472
+
473
+ Args:
474
+ output: Raw output text from the explorer
475
+
476
+ Returns:
477
+ Structured SubagentResult
478
+ """
479
+ result = SubagentResult(
480
+ subagent_type=SubagentType.EXPLORER,
481
+ task_id=None,
482
+ success=True,
483
+ raw_output=output,
484
+ )
485
+
486
+ # Parse sections
487
+ current_section = ""
488
+ current_content: list[str] = []
489
+
490
+ for line in output.split("\n"):
491
+ if line.startswith("## "):
492
+ # Save previous section
493
+ if current_section == "Summary":
494
+ result.summary = "\n".join(current_content).strip()
495
+ elif current_section == "Files Found":
496
+ result.files_found = [
497
+ l.split(":")[0].strip("- ").strip()
498
+ for l in current_content
499
+ if l.strip().startswith("-")
500
+ ]
501
+ elif current_section == "Recommendations":
502
+ result.recommendations = [
503
+ l.strip("- ").strip()
504
+ for l in current_content
505
+ if l.strip().startswith("-")
506
+ ]
507
+
508
+ # Start new section
509
+ current_section = line[3:].strip()
510
+ current_content = []
511
+ else:
512
+ current_content.append(line)
513
+
514
+ # Don't forget last section
515
+ if current_section == "Summary":
516
+ result.summary = "\n".join(current_content).strip()
517
+ elif current_section == "Files Found":
518
+ result.files_found = [
519
+ l.split(":")[0].strip("- ").strip()
520
+ for l in current_content
521
+ if l.strip().startswith("-")
522
+ ]
523
+ elif current_section == "Recommendations":
524
+ result.recommendations = [
525
+ l.strip("- ").strip()
526
+ for l in current_content
527
+ if l.strip().startswith("-")
528
+ ]
529
+
530
+ return result
531
+
532
+ def parse_coder_result(self, output: str) -> SubagentResult:
533
+ """
534
+ Parse the output from a coder subagent.
535
+
536
+ Args:
537
+ output: Raw output text from the coder
538
+
539
+ Returns:
540
+ Structured SubagentResult
541
+ """
542
+ result = SubagentResult(
543
+ subagent_type=SubagentType.CODER,
544
+ task_id=None,
545
+ success="COMPLETE" in output.upper(),
546
+ raw_output=output,
547
+ )
548
+
549
+ # Parse sections
550
+ current_section = ""
551
+ current_content: list[str] = []
552
+
553
+ for line in output.split("\n"):
554
+ if line.startswith("## "):
555
+ # Save previous section
556
+ if current_section == "Summary":
557
+ result.summary = "\n".join(current_content).strip()
558
+ elif current_section == "Files Modified":
559
+ result.files_modified = [
560
+ l.split(":")[0].strip("- ").strip()
561
+ for l in current_content
562
+ if l.strip().startswith("-")
563
+ ]
564
+ elif current_section == "Blockers":
565
+ blockers = "\n".join(current_content).strip()
566
+ if blockers.lower() != "none":
567
+ result.blockers = [blockers]
568
+ result.success = False
569
+
570
+ # Start new section
571
+ current_section = line[3:].strip()
572
+ current_content = []
573
+ else:
574
+ current_content.append(line)
575
+
576
+ return result
577
+
578
+ def update_feature_from_result(
579
+ self,
580
+ feature_id: str,
581
+ result: SubagentResult,
582
+ ) -> None:
583
+ """
584
+ Update a feature based on coder subagent results.
585
+
586
+ Args:
587
+ feature_id: Feature to update
588
+ result: Result from coder subagent
589
+ """
590
+ feature = self.sdk.features.get(feature_id)
591
+ if not feature:
592
+ return
593
+
594
+ with self.sdk.features.edit(feature_id) as f:
595
+ # Update status based on result
596
+ if result.success:
597
+ f.status = "done"
598
+ f.properties["completed_at"] = datetime.now().isoformat()
599
+ elif result.blockers:
600
+ f.status = "blocked"
601
+ f.properties["blockers"] = result.blockers
602
+ else:
603
+ f.status = "in-progress"
604
+
605
+ # Store implementation details
606
+ f.properties["files_modified"] = result.files_modified
607
+ f.properties["implementation_summary"] = result.summary
608
+
609
+ def orchestrate_feature(
610
+ self,
611
+ feature_id: str,
612
+ exploration_scope: str | None = None,
613
+ test_command: str | None = None,
614
+ ) -> dict[str, SubagentPrompt]:
615
+ """
616
+ Generate prompts for full feature orchestration (explore then code).
617
+
618
+ This is a convenience method that creates both explorer and coder
619
+ prompts for a complete feature implementation workflow.
620
+
621
+ Args:
622
+ feature_id: Feature to implement
623
+ exploration_scope: Directory to explore (optional)
624
+ test_command: Test command for verification
625
+
626
+ Returns:
627
+ Dict with 'explorer' and 'coder' prompts
628
+
629
+ Example:
630
+ >>> prompts = orchestrator.orchestrate_feature(
631
+ ... "feat-add-caching",
632
+ ... exploration_scope="src/cache/",
633
+ ... test_command="uv run pytest tests/cache/"
634
+ ... )
635
+ >>> # Execute explorer first
636
+ >>> explorer_result = execute_task(prompts['explorer'])
637
+ >>> # Then execute coder with explorer results
638
+ >>> coder_prompt = orchestrator.spawn_coder(
639
+ ... feature_id,
640
+ ... context=explorer_result
641
+ ... )
642
+ """
643
+ feature = self.sdk.features.get(feature_id)
644
+ if not feature:
645
+ raise ValueError(f"Feature not found: {feature_id}")
646
+
647
+ # Generate explorer prompt
648
+ explorer = self.spawn_explorer(
649
+ task=f"Explore codebase for: {feature.title}",
650
+ scope=exploration_scope,
651
+ questions=[
652
+ "What existing code is relevant?",
653
+ "What patterns should be followed?",
654
+ "What files need modification?",
655
+ ],
656
+ )
657
+
658
+ # Note: coder prompt should be generated after explorer results
659
+ # We provide a placeholder that can be used once explorer completes
660
+ coder = self.spawn_coder(
661
+ feature_id=feature_id,
662
+ context="[Insert explorer results here]",
663
+ test_command=test_command,
664
+ )
665
+
666
+ return {
667
+ "explorer": explorer,
668
+ "coder": coder,
669
+ }