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,476 @@
1
+ """
2
+ Pydantic models for CLI input validation.
3
+
4
+ This module provides type-safe validation for all CLI commands using Pydantic v2.
5
+ Models ensure data integrity before being passed to SDK operations.
6
+ """
7
+
8
+ from typing import Literal
9
+
10
+ from pydantic import BaseModel, Field, field_validator
11
+
12
+
13
+ class CLIInputBase(BaseModel):
14
+ """Base class for all CLI input models with common configuration."""
15
+
16
+ class Config:
17
+ extra = "forbid" # No extra fields allowed
18
+ str_strip_whitespace = True
19
+
20
+
21
+ # =============================================================================
22
+ # Feature Commands
23
+ # =============================================================================
24
+
25
+
26
+ class FeatureCreateInput(CLIInputBase):
27
+ """Input model for 'feature create' command."""
28
+
29
+ title: str = Field(..., min_length=1, max_length=200, description="Feature title")
30
+ description: str | None = Field(
31
+ default=None, max_length=1000, description="Feature description"
32
+ )
33
+ priority: Literal["low", "medium", "high"] = Field(
34
+ default="medium", description="Feature priority level"
35
+ )
36
+ steps: int | None = Field(
37
+ default=None, ge=1, le=50, description="Number of implementation steps"
38
+ )
39
+ collection: str = Field(
40
+ default="features", description="Collection name (features, bugs, etc.)"
41
+ )
42
+
43
+ @field_validator("title")
44
+ @classmethod
45
+ def validate_title(cls, v: str) -> str:
46
+ """Ensure title is not empty or whitespace only."""
47
+ if not v.strip():
48
+ raise ValueError("Title cannot be empty or whitespace only")
49
+ return v.strip()
50
+
51
+ @field_validator("description")
52
+ @classmethod
53
+ def validate_description(cls, v: str | None) -> str | None:
54
+ """Ensure description is stripped if provided."""
55
+ if v is not None:
56
+ v = v.strip()
57
+ if not v:
58
+ return None
59
+ return v
60
+
61
+
62
+ class FeatureStartInput(CLIInputBase):
63
+ """Input model for 'feature start' command."""
64
+
65
+ feature_id: str = Field(..., min_length=1, description="Feature ID to start")
66
+ collection: str = Field(default="features", description="Collection name")
67
+
68
+ @field_validator("feature_id")
69
+ @classmethod
70
+ def validate_feature_id(cls, v: str) -> str:
71
+ """Ensure feature ID is not empty."""
72
+ if not v.strip():
73
+ raise ValueError("Feature ID cannot be empty")
74
+ return v.strip()
75
+
76
+
77
+ class FeatureCompleteInput(CLIInputBase):
78
+ """Input model for 'feature complete' command."""
79
+
80
+ feature_id: str = Field(..., min_length=1, description="Feature ID to complete")
81
+ collection: str = Field(default="features", description="Collection name")
82
+
83
+ @field_validator("feature_id")
84
+ @classmethod
85
+ def validate_feature_id(cls, v: str) -> str:
86
+ """Ensure feature ID is not empty."""
87
+ if not v.strip():
88
+ raise ValueError("Feature ID cannot be empty")
89
+ return v.strip()
90
+
91
+
92
+ class FeaturePrimaryInput(CLIInputBase):
93
+ """Input model for 'feature primary' command."""
94
+
95
+ feature_id: str = Field(
96
+ ..., min_length=1, description="Feature ID to set as primary"
97
+ )
98
+ collection: str = Field(default="features", description="Collection name")
99
+
100
+ @field_validator("feature_id")
101
+ @classmethod
102
+ def validate_feature_id(cls, v: str) -> str:
103
+ """Ensure feature ID is not empty."""
104
+ if not v.strip():
105
+ raise ValueError("Feature ID cannot be empty")
106
+ return v.strip()
107
+
108
+
109
+ class FeatureClaimInput(CLIInputBase):
110
+ """Input model for 'feature claim' command."""
111
+
112
+ feature_id: str = Field(..., min_length=1, description="Feature ID to claim")
113
+ collection: str = Field(default="features", description="Collection name")
114
+
115
+ @field_validator("feature_id")
116
+ @classmethod
117
+ def validate_feature_id(cls, v: str) -> str:
118
+ """Ensure feature ID is not empty."""
119
+ if not v.strip():
120
+ raise ValueError("Feature ID cannot be empty")
121
+ return v.strip()
122
+
123
+
124
+ class FeatureReleaseInput(CLIInputBase):
125
+ """Input model for 'feature release' command."""
126
+
127
+ feature_id: str = Field(..., min_length=1, description="Feature ID to release")
128
+ collection: str = Field(default="features", description="Collection name")
129
+
130
+ @field_validator("feature_id")
131
+ @classmethod
132
+ def validate_feature_id(cls, v: str) -> str:
133
+ """Ensure feature ID is not empty."""
134
+ if not v.strip():
135
+ raise ValueError("Feature ID cannot be empty")
136
+ return v.strip()
137
+
138
+
139
+ # =============================================================================
140
+ # Session Commands
141
+ # =============================================================================
142
+
143
+
144
+ class SessionStartInput(CLIInputBase):
145
+ """Input model for 'session start' command."""
146
+
147
+ session_id: str | None = Field(
148
+ default=None, description="Optional custom session ID"
149
+ )
150
+ title: str | None = Field(default=None, max_length=500, description="Session title")
151
+ agent: str | None = Field(default=None, description="Agent name")
152
+
153
+ @field_validator("session_id")
154
+ @classmethod
155
+ def validate_session_id(cls, v: str | None) -> str | None:
156
+ """Ensure session ID is not empty if provided."""
157
+ if v is not None and not v.strip():
158
+ raise ValueError("Session ID cannot be empty")
159
+ return v.strip() if v else None
160
+
161
+ @field_validator("title")
162
+ @classmethod
163
+ def validate_title(cls, v: str | None) -> str | None:
164
+ """Ensure title is stripped if provided."""
165
+ if v is not None:
166
+ v = v.strip()
167
+ if not v:
168
+ return None
169
+ return v
170
+
171
+
172
+ class SessionEndInput(CLIInputBase):
173
+ """Input model for 'session end' command."""
174
+
175
+ session_id: str = Field(..., min_length=1, description="Session ID to end")
176
+ notes: str | None = Field(
177
+ default=None, max_length=2000, description="Handoff notes"
178
+ )
179
+ recommend: str | None = Field(
180
+ default=None, max_length=500, description="Recommended next steps"
181
+ )
182
+ blocker: list[str] | None = Field(default=None, description="List of blockers")
183
+
184
+ @field_validator("session_id")
185
+ @classmethod
186
+ def validate_session_id(cls, v: str) -> str:
187
+ """Ensure session ID is not empty."""
188
+ if not v.strip():
189
+ raise ValueError("Session ID cannot be empty")
190
+ return v.strip()
191
+
192
+ @field_validator("notes")
193
+ @classmethod
194
+ def validate_notes(cls, v: str | None) -> str | None:
195
+ """Ensure notes are stripped if provided."""
196
+ if v is not None:
197
+ v = v.strip()
198
+ if not v:
199
+ return None
200
+ return v
201
+
202
+ @field_validator("recommend")
203
+ @classmethod
204
+ def validate_recommend(cls, v: str | None) -> str | None:
205
+ """Ensure recommendation is stripped if provided."""
206
+ if v is not None:
207
+ v = v.strip()
208
+ if not v:
209
+ return None
210
+ return v
211
+
212
+
213
+ class SessionHandoffInput(CLIInputBase):
214
+ """Input model for 'session handoff' command."""
215
+
216
+ session_id: str | None = Field(default=None, description="Session ID for handoff")
217
+ notes: str | None = Field(
218
+ default=None, max_length=2000, description="Handoff notes"
219
+ )
220
+ recommend: str | None = Field(
221
+ default=None, max_length=500, description="Recommended next steps"
222
+ )
223
+ blocker: list[str] | None = Field(default=None, description="List of blockers")
224
+ show: bool = Field(default=False, description="Show current handoff context")
225
+
226
+ @field_validator("session_id")
227
+ @classmethod
228
+ def validate_session_id(cls, v: str | None) -> str | None:
229
+ """Ensure session ID is not empty if provided."""
230
+ if v is not None and not v.strip():
231
+ raise ValueError("Session ID cannot be empty")
232
+ return v.strip() if v else None
233
+
234
+
235
+ class SessionListInput(CLIInputBase):
236
+ """Input model for 'session list' command."""
237
+
238
+ status: Literal["active", "ended"] | None = Field(
239
+ default=None, description="Filter by session status"
240
+ )
241
+ limit: int = Field(
242
+ default=20, ge=1, le=100, description="Maximum results to return"
243
+ )
244
+ offset: int = Field(default=0, ge=0, description="Result offset for pagination")
245
+
246
+
247
+ # =============================================================================
248
+ # Track/Activity Commands
249
+ # =============================================================================
250
+
251
+
252
+ class ActivityTrackInput(CLIInputBase):
253
+ """Input model for 'activity track' command."""
254
+
255
+ tool: str = Field(
256
+ ...,
257
+ min_length=1,
258
+ max_length=100,
259
+ description="Tool name (e.g., 'Bash', 'Read')",
260
+ )
261
+ summary: str = Field(
262
+ ..., min_length=1, max_length=500, description="Activity summary"
263
+ )
264
+ files: list[str] | None = Field(
265
+ default=None, description="Files involved in the activity"
266
+ )
267
+ session: str | None = Field(
268
+ default=None, description="Session ID (auto-detected if not provided)"
269
+ )
270
+ failed: bool = Field(default=False, description="Mark as failed")
271
+
272
+ @field_validator("tool")
273
+ @classmethod
274
+ def validate_tool(cls, v: str) -> str:
275
+ """Ensure tool name is stripped and valid."""
276
+ if not v.strip():
277
+ raise ValueError("Tool name cannot be empty")
278
+ return v.strip()
279
+
280
+ @field_validator("summary")
281
+ @classmethod
282
+ def validate_summary(cls, v: str) -> str:
283
+ """Ensure summary is stripped and not empty."""
284
+ if not v.strip():
285
+ raise ValueError("Summary cannot be empty")
286
+ return v.strip()
287
+
288
+
289
+ # =============================================================================
290
+ # Spike Commands
291
+ # =============================================================================
292
+
293
+
294
+ class SpikeCreateInput(CLIInputBase):
295
+ """Input model for 'spike create' command."""
296
+
297
+ title: str = Field(..., min_length=1, max_length=200, description="Spike title")
298
+ findings: str | None = Field(
299
+ default=None, max_length=5000, description="Spike findings"
300
+ )
301
+ priority: Literal["low", "medium", "high"] = Field(
302
+ default="medium", description="Spike priority"
303
+ )
304
+
305
+ @field_validator("title")
306
+ @classmethod
307
+ def validate_title(cls, v: str) -> str:
308
+ """Ensure title is not empty."""
309
+ if not v.strip():
310
+ raise ValueError("Title cannot be empty or whitespace only")
311
+ return v.strip()
312
+
313
+ @field_validator("findings")
314
+ @classmethod
315
+ def validate_findings(cls, v: str | None) -> str | None:
316
+ """Ensure findings are stripped if provided."""
317
+ if v is not None:
318
+ v = v.strip()
319
+ if not v:
320
+ return None
321
+ return v
322
+
323
+
324
+ # =============================================================================
325
+ # Documentation Commands
326
+ # =============================================================================
327
+
328
+
329
+ class DocsGenerateInput(CLIInputBase):
330
+ """Input model for 'docs generate' command."""
331
+
332
+ output_dir: str | None = Field(
333
+ default=None, description="Output directory for generated docs"
334
+ )
335
+ format: Literal["markdown", "html"] = Field(
336
+ default="markdown", description="Output format"
337
+ )
338
+ include_api: bool = Field(default=True, description="Include API documentation")
339
+ include_examples: bool = Field(default=True, description="Include usage examples")
340
+
341
+
342
+ # =============================================================================
343
+ # Track Planning Commands (NEW)
344
+ # =============================================================================
345
+
346
+
347
+ class TrackCreateInput(CLIInputBase):
348
+ """Input model for 'track create' command."""
349
+
350
+ title: str = Field(..., min_length=1, max_length=200, description="Track title")
351
+ priority: Literal["low", "medium", "high"] = Field(
352
+ default="medium", description="Track priority"
353
+ )
354
+ description: str | None = Field(
355
+ default=None, max_length=1000, description="Track description"
356
+ )
357
+
358
+ @field_validator("title")
359
+ @classmethod
360
+ def validate_title(cls, v: str) -> str:
361
+ """Ensure title is not empty."""
362
+ if not v.strip():
363
+ raise ValueError("Title cannot be empty or whitespace only")
364
+ return v.strip()
365
+
366
+ @field_validator("description")
367
+ @classmethod
368
+ def validate_description(cls, v: str | None) -> str | None:
369
+ """Ensure description is stripped if provided."""
370
+ if v is not None:
371
+ v = v.strip()
372
+ if not v:
373
+ return None
374
+ return v
375
+
376
+
377
+ class TrackSpecInput(CLIInputBase):
378
+ """Input model for 'track spec' command."""
379
+
380
+ track_id: str = Field(..., min_length=1, description="Track ID")
381
+ title: str = Field(..., min_length=1, max_length=200, description="Spec title")
382
+ content: str | None = Field(
383
+ default=None, max_length=5000, description="Spec content"
384
+ )
385
+
386
+ @field_validator("track_id")
387
+ @classmethod
388
+ def validate_track_id(cls, v: str) -> str:
389
+ """Ensure track ID is not empty."""
390
+ if not v.strip():
391
+ raise ValueError("Track ID cannot be empty")
392
+ return v.strip()
393
+
394
+ @field_validator("title")
395
+ @classmethod
396
+ def validate_title(cls, v: str) -> str:
397
+ """Ensure title is not empty."""
398
+ if not v.strip():
399
+ raise ValueError("Title cannot be empty")
400
+ return v.strip()
401
+
402
+
403
+ class TrackPlanInput(CLIInputBase):
404
+ """Input model for 'track plan' command."""
405
+
406
+ track_id: str = Field(..., min_length=1, description="Track ID")
407
+ title: str = Field(..., min_length=1, max_length=200, description="Plan title")
408
+ content: str | None = Field(
409
+ default=None, max_length=5000, description="Plan content"
410
+ )
411
+
412
+ @field_validator("track_id")
413
+ @classmethod
414
+ def validate_track_id(cls, v: str) -> str:
415
+ """Ensure track ID is not empty."""
416
+ if not v.strip():
417
+ raise ValueError("Track ID cannot be empty")
418
+ return v.strip()
419
+
420
+ @field_validator("title")
421
+ @classmethod
422
+ def validate_title(cls, v: str) -> str:
423
+ """Ensure title is not empty."""
424
+ if not v.strip():
425
+ raise ValueError("Title cannot be empty")
426
+ return v.strip()
427
+
428
+
429
+ class TrackDeleteInput(CLIInputBase):
430
+ """Input model for 'track delete' command."""
431
+
432
+ track_id: str = Field(..., min_length=1, description="Track ID to delete")
433
+ force: bool = Field(
434
+ default=False, description="Force deletion without confirmation"
435
+ )
436
+
437
+ @field_validator("track_id")
438
+ @classmethod
439
+ def validate_track_id(cls, v: str) -> str:
440
+ """Ensure track ID is not empty."""
441
+ if not v.strip():
442
+ raise ValueError("Track ID cannot be empty")
443
+ return v.strip()
444
+
445
+
446
+ # =============================================================================
447
+ # Archive Commands
448
+ # =============================================================================
449
+
450
+
451
+ class ArchiveCreateInput(CLIInputBase):
452
+ """Input model for 'archive create' command."""
453
+
454
+ title: str = Field(..., min_length=1, max_length=200, description="Archive title")
455
+ items: list[str] | None = Field(default=None, description="Items to archive")
456
+ description: str | None = Field(
457
+ default=None, max_length=1000, description="Archive description"
458
+ )
459
+
460
+ @field_validator("title")
461
+ @classmethod
462
+ def validate_title(cls, v: str) -> str:
463
+ """Ensure title is not empty."""
464
+ if not v.strip():
465
+ raise ValueError("Title cannot be empty or whitespace only")
466
+ return v.strip()
467
+
468
+ @field_validator("description")
469
+ @classmethod
470
+ def validate_description(cls, v: str | None) -> str | None:
471
+ """Ensure description is stripped if provided."""
472
+ if v is not None:
473
+ v = v.strip()
474
+ if not v:
475
+ return None
476
+ return v