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,620 @@
1
+ """
2
+ FilterService - Unified interface for all filtering operations.
3
+
4
+ Consolidates 39+ filter duplications across CLI, SDK, and Collections.
5
+ Single source of truth for all filter logic.
6
+
7
+ Provides:
8
+ - Atomic filter creation and composition
9
+ - Standard filters (status, priority, assigned_to, dates)
10
+ - Custom predicates and complex queries
11
+ - Filter compilation for performance
12
+ - Boolean combinations (AND, OR, NOT)
13
+
14
+ All implementations MUST pass FilterServiceComplianceTests.
15
+ """
16
+
17
+ from abc import ABC, abstractmethod
18
+ from collections.abc import Callable
19
+ from dataclasses import dataclass
20
+ from datetime import datetime
21
+ from enum import Enum
22
+ from typing import Any
23
+
24
+
25
+ class FilterOperator(Enum):
26
+ """Standard filter operators."""
27
+
28
+ EQUALS = "=="
29
+ NOT_EQUALS = "!="
30
+ GREATER_THAN = ">"
31
+ GREATER_EQUAL = ">="
32
+ LESS_THAN = "<"
33
+ LESS_EQUAL = "<="
34
+ IN = "in"
35
+ NOT_IN = "not_in"
36
+ CONTAINS = "contains"
37
+ STARTS_WITH = "starts_with"
38
+ ENDS_WITH = "ends_with"
39
+
40
+
41
+ class FilterLogic(Enum):
42
+ """Combination logic for multiple filters."""
43
+
44
+ AND = "and"
45
+ OR = "or"
46
+ NOT = "not"
47
+
48
+
49
+ @dataclass
50
+ class Filter:
51
+ """
52
+ Atomic filter with field, operator, and value.
53
+
54
+ Supports:
55
+ - Single field filtering (status == 'todo')
56
+ - Range filtering (priority >= 'high')
57
+ - List membership (assigned_to in ['alice', 'bob'])
58
+ - Custom predicates (lambda f: len(f.title) > 10)
59
+ """
60
+
61
+ field: str # Attribute name or None for custom
62
+ operator: FilterOperator | str # Filter operator
63
+ value: Any # Value to filter by
64
+ predicate: Callable[[Any], bool] | None = None # Custom predicate function
65
+ logic: FilterLogic | None = None # Combination logic (for compound filters)
66
+ sub_filters: list["Filter"] | None = None # For compound filters
67
+
68
+ def __call__(self, item: Any) -> bool:
69
+ """Apply filter to item. Allows: filter(item) syntax."""
70
+ raise NotImplementedError("Use compiled filter or apply() method")
71
+
72
+ @property
73
+ def is_custom(self) -> bool:
74
+ """True if this is a custom predicate filter."""
75
+ return self.predicate is not None
76
+
77
+ @property
78
+ def is_compound(self) -> bool:
79
+ """True if this combines multiple filters."""
80
+ return self.logic is not None and self.sub_filters is not None
81
+
82
+
83
+ class FilterServiceError(Exception):
84
+ """Base exception for filter operations."""
85
+
86
+ pass
87
+
88
+
89
+ class InvalidFilterError(FilterServiceError):
90
+ """Raised when filter configuration is invalid."""
91
+
92
+ pass
93
+
94
+
95
+ class FilterService(ABC):
96
+ """
97
+ Unified interface for all filtering operations.
98
+
99
+ Consolidates duplicated filter logic from:
100
+ - CLI commands (filter by status, priority, etc.)
101
+ - SDK collections (multiple filtering methods)
102
+ - Work item queries
103
+ - Analytics recommendations
104
+
105
+ CONTRACT:
106
+ 1. **Correctness**: Filters return exactly matching items
107
+ 2. **Consistency**: Same filter always returns same results
108
+ 3. **Completeness**: All standard filters supported
109
+ 4. **Composability**: Filters can be combined safely
110
+ 5. **Performance**: Filters compiled once, applied efficiently
111
+
112
+ FILTER TYPES:
113
+ 1. **Atomic**: Single field filter (status == 'todo')
114
+ 2. **Standard**: Pre-built common filters (status_is, priority_gte, etc.)
115
+ 3. **Custom**: User-provided predicates (lambda f: f.title.startswith('API'))
116
+ 4. **Compound**: Multiple filters combined (AND, OR, NOT)
117
+
118
+ PERFORMANCE:
119
+ - create_filter(): O(1) validation
120
+ - compile(): O(1) filter->callable conversion
121
+ - apply(): O(n) where n = items
122
+ - apply() with compiled filter: O(n*k) where k = filter complexity
123
+ - apply() with index: O(log n) if available
124
+
125
+ THREAD SAFETY:
126
+ - Filters are immutable after creation
127
+ - Safe to share compiled filters across threads
128
+ - apply() thread-safe on separate item lists
129
+ """
130
+
131
+ # ===== ATOMIC FILTER CREATION =====
132
+
133
+ @abstractmethod
134
+ def create_filter(
135
+ self, field: str, operator: FilterOperator | str, value: Any
136
+ ) -> Filter:
137
+ """
138
+ Create atomic filter for single field.
139
+
140
+ Args:
141
+ field: Attribute name to filter by
142
+ operator: FilterOperator or string operator ("==", "!=", ">", etc.)
143
+ value: Value to compare against
144
+
145
+ Returns:
146
+ Filter object ready to apply or combine
147
+
148
+ Raises:
149
+ InvalidFilterError: If operator or field invalid
150
+
151
+ Performance: O(1)
152
+
153
+ Examples:
154
+ >>> filter1 = service.create_filter("status", "==", "todo")
155
+ >>> filter2 = service.create_filter("priority", ">=", "high")
156
+ >>> filter3 = service.create_filter("assigned_to", "in", ["alice", "bob"])
157
+ """
158
+ ...
159
+
160
+ @abstractmethod
161
+ def custom(self, predicate: Callable[[Any], bool]) -> Filter:
162
+ """
163
+ Create custom filter with arbitrary predicate.
164
+
165
+ For filtering not covered by standard filters.
166
+
167
+ Args:
168
+ predicate: Function taking item, returning True if matches
169
+
170
+ Returns:
171
+ Custom Filter object
172
+
173
+ Examples:
174
+ >>> by_length = service.custom(lambda f: len(f.title) > 10)
175
+ >>> by_date = service.custom(lambda f: (datetime.now() - f.created).days < 7)
176
+ >>> by_keyword = service.custom(lambda f: "auth" in f.title.lower())
177
+ """
178
+ ...
179
+
180
+ # ===== STANDARD FILTERS (COMMON PATTERNS) =====
181
+
182
+ @abstractmethod
183
+ def status_is(self, status: str) -> Filter:
184
+ """
185
+ Filter by exact status match.
186
+
187
+ Args:
188
+ status: Status value ('todo', 'in-progress', 'done', etc.)
189
+
190
+ Returns:
191
+ Filter matching items with exact status
192
+
193
+ Examples:
194
+ >>> repo.apply(items, service.status_is("todo"))
195
+ """
196
+ ...
197
+
198
+ @abstractmethod
199
+ def priority_gte(self, priority: str) -> Filter:
200
+ """
201
+ Filter by priority >= threshold.
202
+
203
+ Args:
204
+ priority: Minimum priority ('low', 'medium', 'high', 'critical')
205
+
206
+ Returns:
207
+ Filter matching items with priority >= threshold
208
+
209
+ Examples:
210
+ >>> high_priority = service.priority_gte("high")
211
+ >>> items = repo.apply(all_items, high_priority)
212
+ """
213
+ ...
214
+
215
+ @abstractmethod
216
+ def priority_lte(self, priority: str) -> Filter:
217
+ """
218
+ Filter by priority <= threshold.
219
+
220
+ Args:
221
+ priority: Maximum priority ('low', 'medium', 'high', 'critical')
222
+
223
+ Returns:
224
+ Filter matching items with priority <= threshold
225
+ """
226
+ ...
227
+
228
+ @abstractmethod
229
+ def assigned_to(self, agent: str) -> Filter:
230
+ """
231
+ Filter by assignment to specific agent.
232
+
233
+ Args:
234
+ agent: Agent ID (e.g., 'claude', 'gpt4')
235
+
236
+ Returns:
237
+ Filter matching items assigned to agent
238
+
239
+ Examples:
240
+ >>> my_work = service.assigned_to("claude")
241
+ """
242
+ ...
243
+
244
+ @abstractmethod
245
+ def created_after(self, date: datetime) -> Filter:
246
+ """
247
+ Filter by creation date after threshold.
248
+
249
+ Args:
250
+ date: Cutoff datetime
251
+
252
+ Returns:
253
+ Filter matching items created > date
254
+
255
+ Examples:
256
+ >>> this_week = service.created_after(datetime.now() - timedelta(days=7))
257
+ """
258
+ ...
259
+
260
+ @abstractmethod
261
+ def created_before(self, date: datetime) -> Filter:
262
+ """
263
+ Filter by creation date before threshold.
264
+
265
+ Args:
266
+ date: Cutoff datetime
267
+
268
+ Returns:
269
+ Filter matching items created < date
270
+ """
271
+ ...
272
+
273
+ @abstractmethod
274
+ def updated_after(self, date: datetime) -> Filter:
275
+ """
276
+ Filter by last update after threshold.
277
+
278
+ Args:
279
+ date: Cutoff datetime
280
+
281
+ Returns:
282
+ Filter matching items updated > date
283
+ """
284
+ ...
285
+
286
+ @abstractmethod
287
+ def updated_before(self, date: datetime) -> Filter:
288
+ """
289
+ Filter by last update before threshold.
290
+
291
+ Args:
292
+ date: Cutoff datetime
293
+
294
+ Returns:
295
+ Filter matching items updated < date
296
+ """
297
+ ...
298
+
299
+ @abstractmethod
300
+ def any_of(self, field: str, values: list[Any]) -> Filter:
301
+ """
302
+ Filter where field value is in set (IN operator).
303
+
304
+ Args:
305
+ field: Attribute to check
306
+ values: List of acceptable values
307
+
308
+ Returns:
309
+ Filter matching items where field in values
310
+
311
+ Examples:
312
+ >>> statuses = service.any_of("status", ["todo", "in-progress"])
313
+ >>> teams = service.any_of("assigned_to", ["team-a", "team-b"])
314
+ """
315
+ ...
316
+
317
+ @abstractmethod
318
+ def none_of(self, field: str, values: list[Any]) -> Filter:
319
+ """
320
+ Filter where field value is NOT in set (NOT IN operator).
321
+
322
+ Args:
323
+ field: Attribute to check
324
+ values: List of excluded values
325
+
326
+ Returns:
327
+ Filter matching items where field not in values
328
+
329
+ Examples:
330
+ >>> exclude_done = service.none_of("status", ["done"])
331
+ """
332
+ ...
333
+
334
+ @abstractmethod
335
+ def text_contains(self, field: str, text: str) -> Filter:
336
+ """
337
+ Filter where text field contains substring.
338
+
339
+ Args:
340
+ field: Attribute to check
341
+ text: Substring to search for
342
+
343
+ Returns:
344
+ Filter matching items where field contains text
345
+
346
+ Examples:
347
+ >>> auth_related = service.text_contains("title", "auth")
348
+ """
349
+ ...
350
+
351
+ @abstractmethod
352
+ def text_starts_with(self, field: str, prefix: str) -> Filter:
353
+ """
354
+ Filter where text field starts with prefix.
355
+
356
+ Args:
357
+ field: Attribute to check
358
+ prefix: Required prefix
359
+
360
+ Returns:
361
+ Filter matching items where field starts with prefix
362
+ """
363
+ ...
364
+
365
+ @abstractmethod
366
+ def range(
367
+ self, field: str, min_value: Any | None = None, max_value: Any | None = None
368
+ ) -> Filter:
369
+ """
370
+ Filter where numeric field is in range.
371
+
372
+ Args:
373
+ field: Numeric attribute to check
374
+ min_value: Minimum value (inclusive, None = no minimum)
375
+ max_value: Maximum value (inclusive, None = no maximum)
376
+
377
+ Returns:
378
+ Filter matching items in range
379
+
380
+ Examples:
381
+ >>> high_impact = service.range("impact_score", min_value=0.8)
382
+ >>> this_quarter = service.range("priority_score", min_value=0.5, max_value=1.0)
383
+ """
384
+ ...
385
+
386
+ # ===== FILTER COMPOSITION =====
387
+
388
+ @abstractmethod
389
+ def combine(
390
+ self, filters: list[Filter], logic: FilterLogic | str = FilterLogic.AND
391
+ ) -> Filter:
392
+ """
393
+ Combine multiple filters with boolean logic.
394
+
395
+ Args:
396
+ filters: List of Filter objects to combine
397
+ logic: Combination logic ('and', 'or', 'not')
398
+
399
+ Returns:
400
+ Compound Filter that applies all filters
401
+
402
+ Raises:
403
+ InvalidFilterError: If incompatible filters
404
+
405
+ Examples:
406
+ >>> status_todo = service.status_is("todo")
407
+ >>> high_priority = service.priority_gte("high")
408
+ >>> combined = service.combine([status_todo, high_priority])
409
+ >>> important_work = repo.apply(items, combined)
410
+
411
+ >>> recent = service.created_after(datetime.now() - timedelta(days=7))
412
+ >>> exclude_done = service.none_of("status", ["done"])
413
+ >>> recent_active = service.combine([recent, exclude_done])
414
+ """
415
+ ...
416
+
417
+ @abstractmethod
418
+ def all_of(self, *filters: Filter) -> Filter:
419
+ """
420
+ Shorthand for combine(filters, AND).
421
+
422
+ Filters must ALL match for item to pass.
423
+
424
+ Examples:
425
+ >>> f1 = service.status_is("todo")
426
+ >>> f2 = service.priority_gte("high")
427
+ >>> f3 = service.assigned_to("claude")
428
+ >>> result = service.all_of(f1, f2, f3)
429
+ """
430
+ ...
431
+
432
+ @abstractmethod
433
+ def any(self, *filters: Filter) -> Filter:
434
+ """
435
+ Shorthand for combine(filters, OR).
436
+
437
+ If ANY filter matches, item passes.
438
+
439
+ Examples:
440
+ >>> f1 = service.status_is("done")
441
+ >>> f2 = service.assigned_to("alice")
442
+ >>> result = service.any(f1, f2) # Done items OR Alice's items
443
+ """
444
+ ...
445
+
446
+ @abstractmethod
447
+ def not_filter(self, filter: Filter) -> Filter:
448
+ """
449
+ Negate a filter (logical NOT).
450
+
451
+ Args:
452
+ filter: Filter to negate
453
+
454
+ Returns:
455
+ Filter that matches items NOT matching input filter
456
+
457
+ Examples:
458
+ >>> not_done = service.not_filter(service.status_is("done"))
459
+ >>> not_critical = service.not_filter(service.priority_gte("critical"))
460
+ """
461
+ ...
462
+
463
+ # ===== FILTER VALIDATION & COMPILATION =====
464
+
465
+ @abstractmethod
466
+ def validate(self, filter: Filter) -> bool:
467
+ """
468
+ Validate filter is well-formed and applicable.
469
+
470
+ Checks:
471
+ - Valid operators
472
+ - Valid field names (if known)
473
+ - Compatible value types
474
+
475
+ Args:
476
+ filter: Filter to validate
477
+
478
+ Returns:
479
+ True if valid, False if invalid
480
+
481
+ Examples:
482
+ >>> f = service.create_filter("status", "==", "todo")
483
+ >>> assert service.validate(f)
484
+ """
485
+ ...
486
+
487
+ @abstractmethod
488
+ def compile(self, filter: Filter) -> Callable[[Any], bool]:
489
+ """
490
+ Pre-compile filter to fast callable.
491
+
492
+ Optimization: convert filter to native Python function
493
+ for faster application. Can cache result.
494
+
495
+ Args:
496
+ filter: Filter to compile
497
+
498
+ Returns:
499
+ Callable that takes item and returns True/False
500
+
501
+ Raises:
502
+ InvalidFilterError: If filter invalid
503
+
504
+ Performance: O(1) compilation, O(1) per application
505
+
506
+ Examples:
507
+ >>> f = service.create_filter("status", "==", "todo")
508
+ >>> compiled = service.compile(f)
509
+ >>> matching = [item for item in items if compiled(item)]
510
+ """
511
+ ...
512
+
513
+ # ===== FILTER APPLICATION =====
514
+
515
+ @abstractmethod
516
+ def apply(
517
+ self, items: list[Any], filter: Filter, limit: int | None = None
518
+ ) -> list[Any]:
519
+ """
520
+ Apply filter to item list.
521
+
522
+ Args:
523
+ items: List of items to filter
524
+ filter: Filter to apply
525
+ limit: Max results to return (None = all)
526
+
527
+ Returns:
528
+ List of items matching filter
529
+
530
+ Performance: O(n) worst case, O(k) best case with early termination
531
+
532
+ Examples:
533
+ >>> todo_items = service.apply(all_items, service.status_is("todo"))
534
+ >>> top_10 = service.apply(all_items, service.priority_gte("high"), limit=10)
535
+ """
536
+ ...
537
+
538
+ @abstractmethod
539
+ def apply_compiled(
540
+ self,
541
+ items: list[Any],
542
+ compiled_filter: Callable[[Any], bool],
543
+ limit: int | None = None,
544
+ ) -> list[Any]:
545
+ """
546
+ Apply pre-compiled filter to items.
547
+
548
+ More efficient than apply() when using same filter multiple times.
549
+
550
+ Args:
551
+ items: List of items to filter
552
+ compiled_filter: Pre-compiled filter from compile()
553
+ limit: Max results (None = all)
554
+
555
+ Returns:
556
+ List of matching items
557
+
558
+ Performance: O(n) with minimal overhead
559
+
560
+ Examples:
561
+ >>> compiled = service.compile(service.status_is("todo"))
562
+ >>> batch1 = service.apply_compiled(items1, compiled)
563
+ >>> batch2 = service.apply_compiled(items2, compiled)
564
+ """
565
+ ...
566
+
567
+ @abstractmethod
568
+ def filter_count(self, items: list[Any], filter: Filter) -> int:
569
+ """
570
+ Count items matching filter without materializing list.
571
+
572
+ Args:
573
+ items: List of items to count
574
+ filter: Filter to apply
575
+
576
+ Returns:
577
+ Number of matching items
578
+
579
+ Performance: O(n) but avoids list allocation
580
+
581
+ Examples:
582
+ >>> todo_count = service.filter_count(items, service.status_is("todo"))
583
+ """
584
+ ...
585
+
586
+ # ===== UTILITY METHODS =====
587
+
588
+ @abstractmethod
589
+ def describe(self, filter: Filter) -> str:
590
+ """
591
+ Get human-readable description of filter.
592
+
593
+ Useful for logging, UI display, debugging.
594
+
595
+ Args:
596
+ filter: Filter to describe
597
+
598
+ Returns:
599
+ Human-readable string
600
+
601
+ Examples:
602
+ >>> f = service.create_filter("status", "==", "todo")
603
+ >>> print(service.describe(f)) # "status is 'todo'"
604
+ """
605
+ ...
606
+
607
+ @abstractmethod
608
+ def get_standard_filters(self) -> dict[str, Callable]:
609
+ """
610
+ Get all available standard filters.
611
+
612
+ Returns:
613
+ Dict of filter_name -> filter_factory_method
614
+
615
+ Examples:
616
+ >>> filters = service.get_standard_filters()
617
+ >>> for name in filters.keys():
618
+ ... print(f"Available: {name}")
619
+ """
620
+ ...