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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) 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 +51 -1
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +26 -10
  7. htmlgraph/agent_registry.py +2 -1
  8. htmlgraph/analytics/__init__.py +8 -1
  9. htmlgraph/analytics/cli.py +86 -20
  10. htmlgraph/analytics/cost_analyzer.py +391 -0
  11. htmlgraph/analytics/cost_monitor.py +664 -0
  12. htmlgraph/analytics/cost_reporter.py +675 -0
  13. htmlgraph/analytics/cross_session.py +617 -0
  14. htmlgraph/analytics/dependency.py +10 -6
  15. htmlgraph/analytics/pattern_learning.py +771 -0
  16. htmlgraph/analytics/session_graph.py +707 -0
  17. htmlgraph/analytics/strategic/__init__.py +80 -0
  18. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  19. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  20. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  21. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  22. htmlgraph/analytics/work_type.py +67 -27
  23. htmlgraph/analytics_index.py +53 -20
  24. htmlgraph/api/__init__.py +3 -0
  25. htmlgraph/api/cost_alerts_websocket.py +416 -0
  26. htmlgraph/api/main.py +2498 -0
  27. htmlgraph/api/static/htmx.min.js +1 -0
  28. htmlgraph/api/static/style-redesign.css +1344 -0
  29. htmlgraph/api/static/style.css +1079 -0
  30. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  31. htmlgraph/api/templates/dashboard.html +794 -0
  32. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  33. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  34. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  35. htmlgraph/api/templates/partials/agents.html +317 -0
  36. htmlgraph/api/templates/partials/event-traces.html +373 -0
  37. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  38. htmlgraph/api/templates/partials/features.html +578 -0
  39. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  40. htmlgraph/api/templates/partials/metrics.html +346 -0
  41. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  42. htmlgraph/api/templates/partials/orchestration.html +198 -0
  43. htmlgraph/api/templates/partials/spawners.html +375 -0
  44. htmlgraph/api/templates/partials/work-items.html +613 -0
  45. htmlgraph/api/websocket.py +538 -0
  46. htmlgraph/archive/__init__.py +24 -0
  47. htmlgraph/archive/bloom.py +234 -0
  48. htmlgraph/archive/fts.py +297 -0
  49. htmlgraph/archive/manager.py +583 -0
  50. htmlgraph/archive/search.py +244 -0
  51. htmlgraph/atomic_ops.py +560 -0
  52. htmlgraph/attribute_index.py +2 -1
  53. htmlgraph/bounded_paths.py +539 -0
  54. htmlgraph/builders/base.py +57 -2
  55. htmlgraph/builders/bug.py +19 -3
  56. htmlgraph/builders/chore.py +19 -3
  57. htmlgraph/builders/epic.py +19 -3
  58. htmlgraph/builders/feature.py +27 -3
  59. htmlgraph/builders/insight.py +2 -1
  60. htmlgraph/builders/metric.py +2 -1
  61. htmlgraph/builders/pattern.py +2 -1
  62. htmlgraph/builders/phase.py +19 -3
  63. htmlgraph/builders/spike.py +29 -3
  64. htmlgraph/builders/track.py +42 -1
  65. htmlgraph/cigs/__init__.py +81 -0
  66. htmlgraph/cigs/autonomy.py +385 -0
  67. htmlgraph/cigs/cost.py +475 -0
  68. htmlgraph/cigs/messages_basic.py +472 -0
  69. htmlgraph/cigs/messaging.py +365 -0
  70. htmlgraph/cigs/models.py +771 -0
  71. htmlgraph/cigs/pattern_storage.py +427 -0
  72. htmlgraph/cigs/patterns.py +503 -0
  73. htmlgraph/cigs/posttool_analyzer.py +234 -0
  74. htmlgraph/cigs/reporter.py +818 -0
  75. htmlgraph/cigs/tracker.py +317 -0
  76. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  77. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  78. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  79. htmlgraph/cli/__init__.py +42 -0
  80. htmlgraph/cli/__main__.py +6 -0
  81. htmlgraph/cli/analytics.py +1424 -0
  82. htmlgraph/cli/base.py +685 -0
  83. htmlgraph/cli/constants.py +206 -0
  84. htmlgraph/cli/core.py +954 -0
  85. htmlgraph/cli/main.py +147 -0
  86. htmlgraph/cli/models.py +475 -0
  87. htmlgraph/cli/templates/__init__.py +1 -0
  88. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  89. htmlgraph/cli/work/__init__.py +239 -0
  90. htmlgraph/cli/work/browse.py +115 -0
  91. htmlgraph/cli/work/features.py +568 -0
  92. htmlgraph/cli/work/orchestration.py +676 -0
  93. htmlgraph/cli/work/report.py +728 -0
  94. htmlgraph/cli/work/sessions.py +466 -0
  95. htmlgraph/cli/work/snapshot.py +559 -0
  96. htmlgraph/cli/work/tracks.py +486 -0
  97. htmlgraph/cli_commands/__init__.py +1 -0
  98. htmlgraph/cli_commands/feature.py +195 -0
  99. htmlgraph/cli_framework.py +115 -0
  100. htmlgraph/collections/__init__.py +2 -0
  101. htmlgraph/collections/base.py +197 -14
  102. htmlgraph/collections/bug.py +2 -1
  103. htmlgraph/collections/chore.py +2 -1
  104. htmlgraph/collections/epic.py +2 -1
  105. htmlgraph/collections/feature.py +2 -1
  106. htmlgraph/collections/insight.py +2 -1
  107. htmlgraph/collections/metric.py +2 -1
  108. htmlgraph/collections/pattern.py +2 -1
  109. htmlgraph/collections/phase.py +2 -1
  110. htmlgraph/collections/session.py +194 -0
  111. htmlgraph/collections/spike.py +13 -2
  112. htmlgraph/collections/task_delegation.py +241 -0
  113. htmlgraph/collections/todo.py +14 -1
  114. htmlgraph/collections/traces.py +487 -0
  115. htmlgraph/config/cost_models.json +56 -0
  116. htmlgraph/config.py +190 -0
  117. htmlgraph/context_analytics.py +2 -1
  118. htmlgraph/converter.py +116 -7
  119. htmlgraph/cost_analysis/__init__.py +5 -0
  120. htmlgraph/cost_analysis/analyzer.py +438 -0
  121. htmlgraph/dashboard.html +2246 -248
  122. htmlgraph/dashboard.html.backup +6592 -0
  123. htmlgraph/dashboard.html.bak +7181 -0
  124. htmlgraph/dashboard.html.bak2 +7231 -0
  125. htmlgraph/dashboard.html.bak3 +7232 -0
  126. htmlgraph/db/__init__.py +38 -0
  127. htmlgraph/db/queries.py +790 -0
  128. htmlgraph/db/schema.py +1788 -0
  129. htmlgraph/decorators.py +317 -0
  130. htmlgraph/dependency_models.py +2 -1
  131. htmlgraph/deploy.py +26 -27
  132. htmlgraph/docs/API_REFERENCE.md +841 -0
  133. htmlgraph/docs/HTTP_API.md +750 -0
  134. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  135. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  136. htmlgraph/docs/README.md +532 -0
  137. htmlgraph/docs/__init__.py +77 -0
  138. htmlgraph/docs/docs_version.py +55 -0
  139. htmlgraph/docs/metadata.py +93 -0
  140. htmlgraph/docs/migrations.py +232 -0
  141. htmlgraph/docs/template_engine.py +143 -0
  142. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  143. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  144. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  145. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  146. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  147. htmlgraph/docs/version_check.py +163 -0
  148. htmlgraph/edge_index.py +2 -1
  149. htmlgraph/error_handler.py +544 -0
  150. htmlgraph/event_log.py +86 -37
  151. htmlgraph/event_migration.py +2 -1
  152. htmlgraph/file_watcher.py +12 -8
  153. htmlgraph/find_api.py +2 -1
  154. htmlgraph/git_events.py +67 -9
  155. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  156. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  157. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  158. htmlgraph/hooks/__init__.py +8 -0
  159. htmlgraph/hooks/bootstrap.py +169 -0
  160. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  161. htmlgraph/hooks/concurrent_sessions.py +208 -0
  162. htmlgraph/hooks/context.py +350 -0
  163. htmlgraph/hooks/drift_handler.py +525 -0
  164. htmlgraph/hooks/event_tracker.py +790 -99
  165. htmlgraph/hooks/git_commands.py +175 -0
  166. htmlgraph/hooks/installer.py +5 -1
  167. htmlgraph/hooks/orchestrator.py +327 -76
  168. htmlgraph/hooks/orchestrator_reflector.py +31 -4
  169. htmlgraph/hooks/post_tool_use_failure.py +32 -7
  170. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  171. htmlgraph/hooks/posttooluse.py +92 -19
  172. htmlgraph/hooks/pretooluse.py +527 -7
  173. htmlgraph/hooks/prompt_analyzer.py +637 -0
  174. htmlgraph/hooks/session_handler.py +668 -0
  175. htmlgraph/hooks/session_summary.py +395 -0
  176. htmlgraph/hooks/state_manager.py +504 -0
  177. htmlgraph/hooks/subagent_detection.py +202 -0
  178. htmlgraph/hooks/subagent_stop.py +369 -0
  179. htmlgraph/hooks/task_enforcer.py +99 -4
  180. htmlgraph/hooks/validator.py +212 -91
  181. htmlgraph/ids.py +2 -1
  182. htmlgraph/learning.py +125 -100
  183. htmlgraph/mcp_server.py +2 -1
  184. htmlgraph/models.py +217 -18
  185. htmlgraph/operations/README.md +62 -0
  186. htmlgraph/operations/__init__.py +79 -0
  187. htmlgraph/operations/analytics.py +339 -0
  188. htmlgraph/operations/bootstrap.py +289 -0
  189. htmlgraph/operations/events.py +244 -0
  190. htmlgraph/operations/fastapi_server.py +231 -0
  191. htmlgraph/operations/hooks.py +350 -0
  192. htmlgraph/operations/initialization.py +597 -0
  193. htmlgraph/operations/initialization.py.backup +228 -0
  194. htmlgraph/operations/server.py +303 -0
  195. htmlgraph/orchestration/__init__.py +58 -0
  196. htmlgraph/orchestration/claude_launcher.py +179 -0
  197. htmlgraph/orchestration/command_builder.py +72 -0
  198. htmlgraph/orchestration/headless_spawner.py +281 -0
  199. htmlgraph/orchestration/live_events.py +377 -0
  200. htmlgraph/orchestration/model_selection.py +327 -0
  201. htmlgraph/orchestration/plugin_manager.py +140 -0
  202. htmlgraph/orchestration/prompts.py +137 -0
  203. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  204. htmlgraph/orchestration/spawners/__init__.py +16 -0
  205. htmlgraph/orchestration/spawners/base.py +194 -0
  206. htmlgraph/orchestration/spawners/claude.py +173 -0
  207. htmlgraph/orchestration/spawners/codex.py +435 -0
  208. htmlgraph/orchestration/spawners/copilot.py +294 -0
  209. htmlgraph/orchestration/spawners/gemini.py +471 -0
  210. htmlgraph/orchestration/subprocess_runner.py +36 -0
  211. htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
  212. htmlgraph/orchestration.md +563 -0
  213. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  214. htmlgraph/orchestrator.py +2 -1
  215. htmlgraph/orchestrator_config.py +357 -0
  216. htmlgraph/orchestrator_mode.py +115 -4
  217. htmlgraph/parallel.py +2 -1
  218. htmlgraph/parser.py +86 -6
  219. htmlgraph/path_query.py +608 -0
  220. htmlgraph/pattern_matcher.py +636 -0
  221. htmlgraph/pydantic_models.py +476 -0
  222. htmlgraph/quality_gates.py +350 -0
  223. htmlgraph/query_builder.py +2 -1
  224. htmlgraph/query_composer.py +509 -0
  225. htmlgraph/reflection.py +443 -0
  226. htmlgraph/refs.py +344 -0
  227. htmlgraph/repo_hash.py +512 -0
  228. htmlgraph/repositories/__init__.py +292 -0
  229. htmlgraph/repositories/analytics_repository.py +455 -0
  230. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  231. htmlgraph/repositories/feature_repository.py +581 -0
  232. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  233. htmlgraph/repositories/feature_repository_memory.py +607 -0
  234. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  235. htmlgraph/repositories/filter_service.py +620 -0
  236. htmlgraph/repositories/filter_service_standard.py +445 -0
  237. htmlgraph/repositories/shared_cache.py +621 -0
  238. htmlgraph/repositories/shared_cache_memory.py +395 -0
  239. htmlgraph/repositories/track_repository.py +552 -0
  240. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  241. htmlgraph/repositories/track_repository_memory.py +508 -0
  242. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  243. htmlgraph/sdk/__init__.py +398 -0
  244. htmlgraph/sdk/__init__.pyi +14 -0
  245. htmlgraph/sdk/analytics/__init__.py +19 -0
  246. htmlgraph/sdk/analytics/engine.py +155 -0
  247. htmlgraph/sdk/analytics/helpers.py +178 -0
  248. htmlgraph/sdk/analytics/registry.py +109 -0
  249. htmlgraph/sdk/base.py +484 -0
  250. htmlgraph/sdk/constants.py +216 -0
  251. htmlgraph/sdk/core.pyi +308 -0
  252. htmlgraph/sdk/discovery.py +120 -0
  253. htmlgraph/sdk/help/__init__.py +12 -0
  254. htmlgraph/sdk/help/mixin.py +699 -0
  255. htmlgraph/sdk/mixins/__init__.py +15 -0
  256. htmlgraph/sdk/mixins/attribution.py +113 -0
  257. htmlgraph/sdk/mixins/mixin.py +410 -0
  258. htmlgraph/sdk/operations/__init__.py +12 -0
  259. htmlgraph/sdk/operations/mixin.py +427 -0
  260. htmlgraph/sdk/orchestration/__init__.py +17 -0
  261. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  262. htmlgraph/sdk/orchestration/spawner.py +204 -0
  263. htmlgraph/sdk/planning/__init__.py +19 -0
  264. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  265. htmlgraph/sdk/planning/mixin.py +211 -0
  266. htmlgraph/sdk/planning/parallel.py +186 -0
  267. htmlgraph/sdk/planning/queue.py +210 -0
  268. htmlgraph/sdk/planning/recommendations.py +87 -0
  269. htmlgraph/sdk/planning/smart_planning.py +319 -0
  270. htmlgraph/sdk/session/__init__.py +19 -0
  271. htmlgraph/sdk/session/continuity.py +57 -0
  272. htmlgraph/sdk/session/handoff.py +110 -0
  273. htmlgraph/sdk/session/info.py +309 -0
  274. htmlgraph/sdk/session/manager.py +103 -0
  275. htmlgraph/sdk/strategic/__init__.py +26 -0
  276. htmlgraph/sdk/strategic/mixin.py +563 -0
  277. htmlgraph/server.py +295 -107
  278. htmlgraph/session_hooks.py +300 -0
  279. htmlgraph/session_manager.py +285 -3
  280. htmlgraph/session_registry.py +587 -0
  281. htmlgraph/session_state.py +436 -0
  282. htmlgraph/session_warning.py +2 -1
  283. htmlgraph/sessions/__init__.py +23 -0
  284. htmlgraph/sessions/handoff.py +756 -0
  285. htmlgraph/system_prompts.py +450 -0
  286. htmlgraph/templates/orchestration-view.html +350 -0
  287. htmlgraph/track_builder.py +33 -1
  288. htmlgraph/track_manager.py +38 -0
  289. htmlgraph/transcript.py +18 -5
  290. htmlgraph/validation.py +115 -0
  291. htmlgraph/watch.py +2 -1
  292. htmlgraph/work_type_utils.py +2 -1
  293. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
  294. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
  295. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  296. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  297. htmlgraph/cli.py +0 -4839
  298. htmlgraph/sdk.py +0 -2359
  299. htmlgraph-0.20.1.dist-info/RECORD +0 -118
  300. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  301. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  302. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  303. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  304. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,676 @@
1
+ from __future__ import annotations
2
+
3
+ """HtmlGraph CLI - Orchestration commands (Archive, Orchestrator, Claude)."""
4
+
5
+
6
+ import argparse
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+
11
+ from rich.console import Console
12
+
13
+ from htmlgraph.cli.base import BaseCommand, CommandError, CommandResult
14
+ from htmlgraph.cli.constants import DEFAULT_GRAPH_DIR
15
+
16
+ if TYPE_CHECKING:
17
+ from argparse import _SubParsersAction
18
+
19
+ console = Console()
20
+
21
+
22
+ def register_archive_commands(subparsers: _SubParsersAction) -> None:
23
+ """Register archive management commands."""
24
+ archive_parser = subparsers.add_parser("archive", help="Archive management")
25
+ archive_subparsers = archive_parser.add_subparsers(
26
+ dest="archive_command", help="Archive command"
27
+ )
28
+
29
+ # archive create
30
+ archive_create = archive_subparsers.add_parser("create", help="Create archive")
31
+ archive_create.add_argument("entity_id", help="Entity ID to archive")
32
+ archive_create.add_argument(
33
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
34
+ )
35
+ archive_create.add_argument(
36
+ "--format", choices=["json", "text"], default="text", help="Output format"
37
+ )
38
+ archive_create.set_defaults(func=ArchiveCreateCommand.from_args)
39
+
40
+ # archive list
41
+ archive_list = archive_subparsers.add_parser("list", help="List archives")
42
+ archive_list.add_argument(
43
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
44
+ )
45
+ archive_list.add_argument(
46
+ "--format", choices=["json", "text"], default="text", help="Output format"
47
+ )
48
+ archive_list.set_defaults(func=ArchiveListCommand.from_args)
49
+
50
+
51
+ def register_orchestrator_commands(subparsers: _SubParsersAction) -> None:
52
+ """Register orchestrator commands."""
53
+ orchestrator_parser = subparsers.add_parser(
54
+ "orchestrator", help="Orchestrator management"
55
+ )
56
+ orchestrator_subparsers = orchestrator_parser.add_subparsers(
57
+ dest="orchestrator_command", help="Orchestrator command"
58
+ )
59
+
60
+ # orchestrator enable
61
+ orch_enable = orchestrator_subparsers.add_parser(
62
+ "enable", help="Enable orchestrator mode"
63
+ )
64
+ orch_enable.add_argument(
65
+ "--level",
66
+ "-l",
67
+ choices=["strict", "guidance"],
68
+ default="strict",
69
+ help="Enforcement level (default: strict)",
70
+ )
71
+ orch_enable.add_argument(
72
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
73
+ )
74
+ orch_enable.set_defaults(func=OrchestratorEnableCommand.from_args)
75
+
76
+ # orchestrator disable
77
+ orch_disable = orchestrator_subparsers.add_parser(
78
+ "disable", help="Disable orchestrator mode"
79
+ )
80
+ orch_disable.add_argument(
81
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
82
+ )
83
+ orch_disable.set_defaults(func=OrchestratorDisableCommand.from_args)
84
+
85
+ # orchestrator status
86
+ orch_status = orchestrator_subparsers.add_parser(
87
+ "status", help="Show orchestrator status"
88
+ )
89
+ orch_status.add_argument(
90
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
91
+ )
92
+ orch_status.add_argument(
93
+ "--format", choices=["json", "text"], default="text", help="Output format"
94
+ )
95
+ orch_status.set_defaults(func=OrchestratorStatusCommand.from_args)
96
+
97
+ # orchestrator config show
98
+ config_show = orchestrator_subparsers.add_parser(
99
+ "config-show", help="Show orchestrator configuration"
100
+ )
101
+ config_show.add_argument(
102
+ "--format", choices=["json", "text"], default="text", help="Output format"
103
+ )
104
+ config_show.set_defaults(func=OrchestratorConfigShowCommand.from_args)
105
+
106
+ # orchestrator config set
107
+ config_set = orchestrator_subparsers.add_parser(
108
+ "config-set", help="Set a configuration value"
109
+ )
110
+ config_set.add_argument(
111
+ "key", help="Config key (e.g., thresholds.exploration_calls)"
112
+ )
113
+ config_set.add_argument("value", type=int, help="New value")
114
+ config_set.add_argument(
115
+ "--format", choices=["json", "text"], default="text", help="Output format"
116
+ )
117
+ config_set.set_defaults(func=OrchestratorConfigSetCommand.from_args)
118
+
119
+ # orchestrator config reset
120
+ config_reset = orchestrator_subparsers.add_parser(
121
+ "config-reset", help="Reset configuration to defaults"
122
+ )
123
+ config_reset.add_argument(
124
+ "--format", choices=["json", "text"], default="text", help="Output format"
125
+ )
126
+ config_reset.set_defaults(func=OrchestratorConfigResetCommand.from_args)
127
+
128
+ # orchestrator reset-violations
129
+ reset_violations = orchestrator_subparsers.add_parser(
130
+ "reset-violations", help="Reset violation counter"
131
+ )
132
+ reset_violations.add_argument(
133
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
134
+ )
135
+ reset_violations.set_defaults(func=OrchestratorResetViolationsCommand.from_args)
136
+
137
+ # orchestrator set-level
138
+ set_level = orchestrator_subparsers.add_parser(
139
+ "set-level", help="Set enforcement level"
140
+ )
141
+ set_level.add_argument(
142
+ "level", choices=["strict", "guidance"], help="Enforcement level to set"
143
+ )
144
+ set_level.add_argument(
145
+ "--graph-dir", "-g", default=DEFAULT_GRAPH_DIR, help="Graph directory"
146
+ )
147
+ set_level.set_defaults(func=OrchestratorSetLevelCommand.from_args)
148
+
149
+
150
+ def register_claude_commands(subparsers: _SubParsersAction) -> None:
151
+ """Register Claude Code launcher commands."""
152
+ claude_parser = subparsers.add_parser(
153
+ "claude", help="Launch Claude Code with HtmlGraph integration"
154
+ )
155
+ claude_parser.add_argument(
156
+ "--init",
157
+ action="store_true",
158
+ help="Launch with orchestrator prompt and plugin installation",
159
+ )
160
+ claude_parser.add_argument(
161
+ "--continue",
162
+ dest="continue_session",
163
+ action="store_true",
164
+ help="Resume last session with orchestrator rules",
165
+ )
166
+ claude_parser.add_argument(
167
+ "--dev",
168
+ action="store_true",
169
+ help="Launch with local plugin for development",
170
+ )
171
+ claude_parser.set_defaults(func=ClaudeCommand.from_args)
172
+
173
+
174
+ # ============================================================================
175
+ # Archive Commands
176
+ # ============================================================================
177
+
178
+
179
+ class ArchiveCreateCommand(BaseCommand):
180
+ """Create an archive."""
181
+
182
+ def __init__(self, *, entity_id: str) -> None:
183
+ super().__init__()
184
+ self.entity_id = entity_id
185
+
186
+ @classmethod
187
+ def from_args(cls, args: argparse.Namespace) -> ArchiveCreateCommand:
188
+ return cls(entity_id=args.entity_id)
189
+
190
+ def execute(self) -> CommandResult:
191
+ """Create an archive."""
192
+ from htmlgraph.archive import ArchiveManager
193
+
194
+ if self.graph_dir is None:
195
+ raise CommandError("Missing graph directory")
196
+
197
+ htmlgraph_dir = Path(self.graph_dir).resolve()
198
+
199
+ if not htmlgraph_dir.exists():
200
+ raise CommandError(f"Directory not found: {htmlgraph_dir}")
201
+
202
+ with console.status("[blue]Initializing archive manager...", spinner="dots"):
203
+ manager = ArchiveManager(htmlgraph_dir)
204
+
205
+ try:
206
+ # Archive the entity
207
+ with console.status(f"[blue]Archiving {self.entity_id}...", spinner="dots"):
208
+ # For now, we'll use the older_than_days parameter with 0 to archive immediately
209
+ result = manager.archive_entities(older_than_days=0, dry_run=False)
210
+
211
+ from htmlgraph.cli.base import TextOutputBuilder
212
+
213
+ output = TextOutputBuilder()
214
+ output.add_success(f"Archived: {self.entity_id}")
215
+ output.add_field(
216
+ "Created", f"{len(result['archive_files'])} archive file(s)"
217
+ )
218
+ output.add_field("Total entities archived", result["archived_count"])
219
+
220
+ json_data = {
221
+ "entity_id": self.entity_id,
222
+ "archived": True,
223
+ "archive_files": result["archive_files"],
224
+ "count": result["archived_count"],
225
+ }
226
+
227
+ return CommandResult(
228
+ text=output.build(),
229
+ json_data=json_data,
230
+ )
231
+ finally:
232
+ manager.close()
233
+
234
+
235
+ class ArchiveListCommand(BaseCommand):
236
+ """List all archives."""
237
+
238
+ @classmethod
239
+ def from_args(cls, args: argparse.Namespace) -> ArchiveListCommand:
240
+ return cls()
241
+
242
+ def execute(self) -> CommandResult:
243
+ """List all archives."""
244
+ if self.graph_dir is None:
245
+ raise CommandError("Missing graph directory")
246
+
247
+ htmlgraph_dir = Path(self.graph_dir).resolve()
248
+
249
+ if not htmlgraph_dir.exists():
250
+ raise CommandError(f"Directory not found: {htmlgraph_dir}")
251
+
252
+ archive_dir = htmlgraph_dir / "archives"
253
+
254
+ if not archive_dir.exists():
255
+ from htmlgraph.cli.base import TextOutputBuilder
256
+
257
+ output = TextOutputBuilder()
258
+ output.add_warning("No archives found.")
259
+ return CommandResult(
260
+ text=output.build(),
261
+ json_data={"archives": []},
262
+ )
263
+
264
+ archive_files = sorted(archive_dir.glob("*.html"))
265
+
266
+ if not archive_files:
267
+ from htmlgraph.cli.base import TextOutputBuilder
268
+
269
+ output = TextOutputBuilder()
270
+ output.add_warning("No archives found.")
271
+ return CommandResult(
272
+ text=output.build(),
273
+ json_data={"archives": []},
274
+ )
275
+
276
+ # Create Rich table
277
+ from htmlgraph.cli.base import TableBuilder
278
+
279
+ builder = TableBuilder.create_list_table(
280
+ f"Archive Files ({len(archive_files)})"
281
+ )
282
+ builder.add_column("Filename", style="cyan", no_wrap=False)
283
+ builder.add_numeric_column("Size (KB)", style="yellow", width=12)
284
+ builder.add_timestamp_column("Modified", width=16)
285
+
286
+ file_list = []
287
+ for f in archive_files:
288
+ size_kb = f.stat().st_size / 1024
289
+ modified = datetime.fromtimestamp(f.stat().st_mtime)
290
+ modified_str = modified.strftime("%Y-%m-%d %H:%M")
291
+
292
+ builder.add_row(f.name, f"{size_kb:.1f}", modified_str)
293
+
294
+ file_list.append(
295
+ {
296
+ "filename": f.name,
297
+ "size_kb": size_kb,
298
+ "modified": modified.isoformat(),
299
+ }
300
+ )
301
+
302
+ # Return table object directly - TextFormatter will print it properly
303
+ return CommandResult(
304
+ data=builder.table,
305
+ json_data={"archives": file_list},
306
+ )
307
+
308
+
309
+ # ============================================================================
310
+ # Orchestrator Commands
311
+ # ============================================================================
312
+
313
+
314
+ class OrchestratorEnableCommand(BaseCommand):
315
+ """Enable orchestrator mode."""
316
+
317
+ def __init__(self, *, level: str = "strict") -> None:
318
+ super().__init__()
319
+ self.level: str = level
320
+
321
+ @classmethod
322
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorEnableCommand:
323
+ return cls(level=getattr(args, "level", "strict"))
324
+
325
+ def execute(self) -> CommandResult:
326
+ """Enable orchestrator mode."""
327
+
328
+ from htmlgraph.orchestrator_mode import OrchestratorModeManager
329
+
330
+ if self.graph_dir is None:
331
+ raise CommandError("Missing graph directory")
332
+
333
+ manager = OrchestratorModeManager(self.graph_dir)
334
+ manager.enable(level=self.level) # type: ignore[arg-type]
335
+ status = manager.status()
336
+
337
+ from htmlgraph.cli.base import TextOutputBuilder
338
+
339
+ output = TextOutputBuilder()
340
+ if self.level == "strict":
341
+ output.add_success("Orchestrator mode enabled (strict enforcement)")
342
+ else:
343
+ output.add_success("Orchestrator mode enabled (guidance mode)")
344
+ output.add_field("Level", self.level)
345
+ if status.get("activated_at"):
346
+ output.add_field("Activated at", status["activated_at"])
347
+
348
+ return CommandResult(
349
+ text=output.build(),
350
+ json_data=status,
351
+ )
352
+
353
+
354
+ class OrchestratorDisableCommand(BaseCommand):
355
+ """Disable orchestrator mode."""
356
+
357
+ @classmethod
358
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorDisableCommand:
359
+ return cls()
360
+
361
+ def execute(self) -> CommandResult:
362
+ """Disable orchestrator mode."""
363
+ from htmlgraph.orchestrator_mode import OrchestratorModeManager
364
+
365
+ if self.graph_dir is None:
366
+ raise CommandError("Missing graph directory")
367
+
368
+ manager = OrchestratorModeManager(self.graph_dir)
369
+ manager.disable(by_user=True)
370
+ status = manager.status()
371
+
372
+ from htmlgraph.cli.base import TextOutputBuilder
373
+
374
+ output = TextOutputBuilder()
375
+ output.add_success("Orchestrator mode disabled")
376
+ output.add_field("Status", "Disabled by user (auto-activation prevented)")
377
+
378
+ return CommandResult(
379
+ text=output.build(),
380
+ json_data=status,
381
+ )
382
+
383
+
384
+ class OrchestratorStatusCommand(BaseCommand):
385
+ """Show orchestrator status."""
386
+
387
+ @classmethod
388
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorStatusCommand:
389
+ return cls()
390
+
391
+ def execute(self) -> CommandResult:
392
+ """Show orchestrator status."""
393
+ from htmlgraph.orchestrator_mode import OrchestratorModeManager
394
+
395
+ if self.graph_dir is None:
396
+ raise CommandError("Missing graph directory")
397
+
398
+ manager = OrchestratorModeManager(self.graph_dir)
399
+ mode = manager.load()
400
+ status = manager.status()
401
+
402
+ from htmlgraph.cli.base import TextOutputBuilder
403
+
404
+ output = TextOutputBuilder()
405
+ if status.get("enabled"):
406
+ if status.get("enforcement_level") == "strict":
407
+ output.add_line("Orchestrator mode: enabled (strict enforcement)")
408
+ else:
409
+ output.add_line("Orchestrator mode: enabled (guidance mode)")
410
+ else:
411
+ output.add_line("Orchestrator mode: disabled")
412
+ if mode.disabled_by_user:
413
+ output.add_field(
414
+ "Status", "Disabled by user (auto-activation prevented)"
415
+ )
416
+
417
+ if status.get("activated_at"):
418
+ output.add_field("Activated at", status["activated_at"])
419
+ if status.get("violations") is not None:
420
+ output.add_field("Violations", f"{status['violations']}/3")
421
+ if status.get("circuit_breaker_triggered"):
422
+ output.add_field("Circuit breaker", "TRIGGERED")
423
+
424
+ return CommandResult(
425
+ data=status,
426
+ text=output.build(),
427
+ json_data=status,
428
+ )
429
+
430
+
431
+ class OrchestratorConfigShowCommand(BaseCommand):
432
+ """Show orchestrator configuration."""
433
+
434
+ @classmethod
435
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorConfigShowCommand:
436
+ return cls()
437
+
438
+ def execute(self) -> CommandResult:
439
+ """Show orchestrator configuration."""
440
+ from htmlgraph.orchestrator_config import (
441
+ format_config_display,
442
+ load_orchestrator_config,
443
+ )
444
+
445
+ config = load_orchestrator_config()
446
+ text_output = format_config_display(config)
447
+
448
+ return CommandResult(
449
+ text=text_output,
450
+ json_data=config.model_dump(),
451
+ )
452
+
453
+
454
+ class OrchestratorConfigSetCommand(BaseCommand):
455
+ """Set a configuration value."""
456
+
457
+ def __init__(self, *, key: str, value: int) -> None:
458
+ super().__init__()
459
+ self.key = key
460
+ self.value = value
461
+
462
+ @classmethod
463
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorConfigSetCommand:
464
+ return cls(key=args.key, value=args.value)
465
+
466
+ def execute(self) -> CommandResult:
467
+ """Set a configuration value."""
468
+ from htmlgraph.orchestrator_config import (
469
+ get_config_paths,
470
+ load_orchestrator_config,
471
+ save_orchestrator_config,
472
+ set_config_value,
473
+ )
474
+
475
+ # Load current config
476
+ config = load_orchestrator_config()
477
+
478
+ try:
479
+ # Set the value
480
+ set_config_value(config, self.key, self.value)
481
+
482
+ # Save to first config path (project-specific)
483
+ config_path = get_config_paths()[0]
484
+ save_orchestrator_config(config, config_path)
485
+
486
+ from htmlgraph.cli.base import TextOutputBuilder
487
+
488
+ output = TextOutputBuilder()
489
+ output.add_success(f"Configuration updated: {self.key} = {self.value}")
490
+ output.add_field("Config file", str(config_path))
491
+
492
+ return CommandResult(
493
+ text=output.build(),
494
+ json_data={
495
+ "key": self.key,
496
+ "value": self.value,
497
+ "path": str(config_path),
498
+ },
499
+ )
500
+ except KeyError as e:
501
+ raise CommandError(f"Invalid config key: {e}")
502
+
503
+
504
+ class OrchestratorConfigResetCommand(BaseCommand):
505
+ """Reset configuration to defaults."""
506
+
507
+ @classmethod
508
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorConfigResetCommand:
509
+ return cls()
510
+
511
+ def execute(self) -> CommandResult:
512
+ """Reset configuration to defaults."""
513
+ from htmlgraph.orchestrator_config import (
514
+ OrchestratorConfig,
515
+ get_config_paths,
516
+ save_orchestrator_config,
517
+ )
518
+
519
+ # Create default config
520
+ config = OrchestratorConfig()
521
+
522
+ # Save to first config path (project-specific)
523
+ config_path = get_config_paths()[0]
524
+ save_orchestrator_config(config, config_path)
525
+
526
+ from htmlgraph.cli.base import TextOutputBuilder
527
+
528
+ output = TextOutputBuilder()
529
+ output.add_success("Configuration reset to defaults")
530
+ output.add_field("Config file", str(config_path))
531
+ output.add_field("Exploration calls", config.thresholds.exploration_calls)
532
+ output.add_field(
533
+ "Circuit breaker", config.thresholds.circuit_breaker_violations
534
+ )
535
+ output.add_field(
536
+ "Violation decay", f"{config.thresholds.violation_decay_seconds}s"
537
+ )
538
+
539
+ return CommandResult(
540
+ text=output.build(),
541
+ json_data=config.model_dump(),
542
+ )
543
+
544
+
545
+ class OrchestratorResetViolationsCommand(BaseCommand):
546
+ """Reset violation counter."""
547
+
548
+ @classmethod
549
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorResetViolationsCommand:
550
+ return cls()
551
+
552
+ def execute(self) -> CommandResult:
553
+ """Reset violation counter."""
554
+ from htmlgraph.orchestrator_mode import OrchestratorModeManager
555
+
556
+ if self.graph_dir is None:
557
+ raise CommandError("Missing graph directory")
558
+
559
+ manager = OrchestratorModeManager(self.graph_dir)
560
+
561
+ if not manager.status().get("enabled"):
562
+ console.print("[yellow]Orchestrator mode is not enabled[/yellow]")
563
+ return CommandResult(
564
+ text="Orchestrator mode is not enabled",
565
+ json_data={"success": False, "message": "not enabled"},
566
+ )
567
+
568
+ manager.reset_violations()
569
+ status = manager.status()
570
+
571
+ from htmlgraph.cli.base import TextOutputBuilder
572
+
573
+ output = TextOutputBuilder()
574
+ output.add_success("Violations reset")
575
+ output.add_field("Violation count", status.get("violations", 0))
576
+ output.add_field(
577
+ "Circuit breaker",
578
+ "Normal" if not status.get("circuit_breaker_triggered") else "TRIGGERED",
579
+ )
580
+
581
+ return CommandResult(
582
+ text=output.build(),
583
+ json_data=status,
584
+ )
585
+
586
+
587
+ class OrchestratorSetLevelCommand(BaseCommand):
588
+ """Set enforcement level."""
589
+
590
+ def __init__(self, *, level: str) -> None:
591
+ super().__init__()
592
+ self.level: str = level
593
+
594
+ @classmethod
595
+ def from_args(cls, args: argparse.Namespace) -> OrchestratorSetLevelCommand:
596
+ return cls(level=args.level)
597
+
598
+ def execute(self) -> CommandResult:
599
+ """Set enforcement level."""
600
+ from htmlgraph.orchestrator_mode import OrchestratorModeManager
601
+
602
+ if self.graph_dir is None:
603
+ raise CommandError("Missing graph directory")
604
+
605
+ manager = OrchestratorModeManager(self.graph_dir)
606
+ manager.set_level(self.level) # type: ignore[arg-type]
607
+ status = manager.status()
608
+
609
+ from htmlgraph.cli.base import TextOutputBuilder
610
+
611
+ output = TextOutputBuilder()
612
+ output.add_success(f"Enforcement level changed to '{self.level}'")
613
+ if self.level == "strict":
614
+ output.add_field("Mode", "Strict enforcement")
615
+ else:
616
+ output.add_field("Mode", "Guidance mode")
617
+
618
+ return CommandResult(
619
+ text=output.build(),
620
+ json_data=status,
621
+ )
622
+
623
+
624
+ # ============================================================================
625
+ # Claude Code Launcher Commands
626
+ # ============================================================================
627
+
628
+
629
+ class ClaudeCommand(BaseCommand):
630
+ """Launch Claude Code with HtmlGraph integration."""
631
+
632
+ def __init__(
633
+ self,
634
+ *,
635
+ init: bool,
636
+ continue_session: bool,
637
+ dev: bool,
638
+ quiet: bool,
639
+ format: str,
640
+ ) -> None:
641
+ super().__init__()
642
+ self.init = init
643
+ self.continue_session = continue_session
644
+ self.dev = dev
645
+ self.quiet = quiet
646
+ self.format = format
647
+
648
+ @classmethod
649
+ def from_args(cls, args: argparse.Namespace) -> ClaudeCommand:
650
+ return cls(
651
+ init=getattr(args, "init", False),
652
+ continue_session=getattr(args, "continue_session", False),
653
+ dev=getattr(args, "dev", False),
654
+ quiet=getattr(args, "quiet", False),
655
+ format=getattr(args, "format", "text"),
656
+ )
657
+
658
+ def execute(self) -> CommandResult:
659
+ """Launch Claude Code."""
660
+ from htmlgraph.orchestration.claude_launcher import ClaudeLauncher
661
+
662
+ # Create args namespace for launcher
663
+ launcher_args = argparse.Namespace(
664
+ init=self.init,
665
+ continue_session=self.continue_session,
666
+ dev=self.dev,
667
+ quiet=self.quiet,
668
+ format=self.format,
669
+ )
670
+
671
+ # Launch Claude Code
672
+ launcher = ClaudeLauncher(launcher_args)
673
+ launcher.launch()
674
+
675
+ # This won't be reached because launcher.launch() calls subprocess
676
+ return CommandResult(text="Claude Code launched")