@trac3er/oh-my-god 2.0.0-beta.2 → 2.1.0-alpha

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 (47) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/OMG-setup.sh +33 -0
  4. package/README.md +43 -0
  5. package/claude_experimental/__init__.py +27 -0
  6. package/claude_experimental/_compat.py +12 -0
  7. package/claude_experimental/_degradation.py +210 -0
  8. package/claude_experimental/_flags.py +35 -0
  9. package/claude_experimental/_lifecycle.py +122 -0
  10. package/claude_experimental/integration/__init__.py +16 -0
  11. package/claude_experimental/integration/_placeholder.py +7 -0
  12. package/claude_experimental/integration/autotuner.py +182 -0
  13. package/claude_experimental/integration/checkpoints.py +327 -0
  14. package/claude_experimental/integration/experiments.py +302 -0
  15. package/claude_experimental/integration/openapi_gen.py +428 -0
  16. package/claude_experimental/integration/streaming.py +131 -0
  17. package/claude_experimental/integration/telemetry.py +287 -0
  18. package/claude_experimental/memory/__init__.py +16 -0
  19. package/claude_experimental/memory/_placeholder.py +7 -0
  20. package/claude_experimental/memory/api.py +434 -0
  21. package/claude_experimental/memory/augmented_generation.py +142 -0
  22. package/claude_experimental/memory/episodic.py +190 -0
  23. package/claude_experimental/memory/failure_learning.py +183 -0
  24. package/claude_experimental/memory/migrate.py +169 -0
  25. package/claude_experimental/memory/procedural.py +212 -0
  26. package/claude_experimental/memory/semantic.py +376 -0
  27. package/claude_experimental/memory/store.py +310 -0
  28. package/claude_experimental/parallel/__init__.py +17 -0
  29. package/claude_experimental/parallel/_placeholder.py +7 -0
  30. package/claude_experimental/parallel/aggregation.py +131 -0
  31. package/claude_experimental/parallel/api.py +244 -0
  32. package/claude_experimental/parallel/executor.py +110 -0
  33. package/claude_experimental/parallel/ralph_bridge.py +164 -0
  34. package/claude_experimental/parallel/sandbox.py +314 -0
  35. package/claude_experimental/parallel/scaling.py +171 -0
  36. package/claude_experimental/parallel/ultraworker.py +285 -0
  37. package/claude_experimental/patterns/__init__.py +16 -0
  38. package/claude_experimental/patterns/_placeholder.py +7 -0
  39. package/claude_experimental/patterns/antipatterns.py +454 -0
  40. package/claude_experimental/patterns/api.py +196 -0
  41. package/claude_experimental/patterns/extractor.py +163 -0
  42. package/claude_experimental/patterns/mining.py +219 -0
  43. package/claude_experimental/patterns/refactoring.py +234 -0
  44. package/hooks/_common.py +3 -1
  45. package/package.json +1 -1
  46. package/runtime/subagent_dispatcher.py +109 -9
  47. package/settings.json +6 -1
@@ -0,0 +1,287 @@
1
+ """TelemetryCollector - Lightweight local-only metrics collection and aggregation.
2
+
3
+ Design decisions:
4
+ - WAL mode for concurrent multi-session access
5
+ - Connection-per-invocation (hooks are short-lived subprocesses, no pooling)
6
+ - Separate SQLite DB from memory store (.omg/state/telemetry.db)
7
+ - Privacy-first: only numeric metrics, no content capture
8
+ - Schema versioning from day one
9
+ - Feature-gated via ADVANCED_INTEGRATION flag
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import os
15
+ import sqlite3
16
+ from datetime import datetime, timedelta, timezone
17
+ from typing import cast
18
+
19
+ SCHEMA_VERSION = 1
20
+
21
+ _VALID_METRIC_TYPES = frozenset({"counter", "gauge", "histogram"})
22
+
23
+
24
+ def _default_db_path() -> str:
25
+ """Return default telemetry DB path."""
26
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
27
+ return os.path.join(project_dir, ".omg", "state", "telemetry.db")
28
+
29
+
30
+ class TelemetryCollector:
31
+ """Local-only metrics collection with SQLite storage.
32
+
33
+ Supports counter, gauge, and histogram metric types with time-based
34
+ querying, aggregation, and automatic rotation of old data.
35
+
36
+ Usage:
37
+ collector = TelemetryCollector()
38
+ collector.record_counter("api.requests", tags={"endpoint": "/search"})
39
+ collector.record_gauge("memory.usage_mb", 256.5)
40
+ collector.record_histogram("response.latency_ms", 42.3)
41
+
42
+ metrics = collector.query("api.requests", since_minutes=60)
43
+ agg = collector.aggregate("api.requests", period="minute")
44
+ deleted = collector.rotate_old_data(days=30)
45
+ """
46
+
47
+ def __init__(self, db_path: str | None = None):
48
+ from claude_experimental.integration import _require_enabled
49
+ _require_enabled()
50
+
51
+ self.db_path: str = db_path or _default_db_path()
52
+ self._ensure_dir()
53
+
54
+ def _ensure_dir(self) -> None:
55
+ if self.db_path != ":memory:":
56
+ os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
57
+
58
+ def _connect(self) -> sqlite3.Connection:
59
+ """Open a new connection and initialize the schema if needed."""
60
+ conn = sqlite3.connect(self.db_path, check_same_thread=False)
61
+ conn.row_factory = sqlite3.Row
62
+ self._init_schema(conn)
63
+ return conn
64
+
65
+ def _init_schema(self, conn: sqlite3.Connection) -> None:
66
+ """Create schema if not exists."""
67
+ _ = conn.executescript("""
68
+ PRAGMA journal_mode=WAL;
69
+ PRAGMA busy_timeout=5000;
70
+
71
+ CREATE TABLE IF NOT EXISTS schema_info (
72
+ key TEXT PRIMARY KEY,
73
+ value TEXT NOT NULL
74
+ );
75
+
76
+ CREATE TABLE IF NOT EXISTS metrics (
77
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
78
+ name TEXT NOT NULL,
79
+ metric_type TEXT NOT NULL,
80
+ value REAL NOT NULL,
81
+ tags TEXT NOT NULL DEFAULT '{}',
82
+ recorded_at TEXT NOT NULL,
83
+ schema_version INTEGER NOT NULL DEFAULT 1
84
+ );
85
+
86
+ CREATE INDEX IF NOT EXISTS idx_metrics_name ON metrics(name);
87
+ CREATE INDEX IF NOT EXISTS idx_metrics_type ON metrics(metric_type);
88
+ CREATE INDEX IF NOT EXISTS idx_metrics_recorded ON metrics(recorded_at);
89
+ CREATE INDEX IF NOT EXISTS idx_metrics_name_type ON metrics(name, metric_type);
90
+ """)
91
+
92
+ # Set schema version if not present
93
+ row = cast(
94
+ sqlite3.Row | None,
95
+ conn.execute(
96
+ "SELECT value FROM schema_info WHERE key='schema_version'"
97
+ ).fetchone(),
98
+ )
99
+ if row is None:
100
+ _ = conn.execute(
101
+ "INSERT OR REPLACE INTO schema_info (key, value) VALUES ('schema_version', ?)",
102
+ (str(SCHEMA_VERSION),),
103
+ )
104
+ conn.commit()
105
+
106
+ def _record(self, name: str, metric_type: str, value: float, tags: dict[str, str] | None) -> None:
107
+ """Internal: insert a single metric row."""
108
+ now = datetime.now(timezone.utc).isoformat()
109
+ conn = self._connect()
110
+ try:
111
+ _ = conn.execute(
112
+ "INSERT INTO metrics (name, metric_type, value, tags, recorded_at, schema_version) VALUES (?, ?, ?, ?, ?, ?)",
113
+ (name, metric_type, value, json.dumps(tags or {}), now, SCHEMA_VERSION),
114
+ )
115
+ conn.commit()
116
+ finally:
117
+ conn.close()
118
+
119
+ def record_counter(self, name: str, value: float = 1, tags: dict[str, str] | None = None) -> None:
120
+ """Record a counter metric (monotonically increasing value).
121
+
122
+ Args:
123
+ name: Metric name (e.g. "api.requests").
124
+ value: Increment amount (default 1).
125
+ tags: Optional key-value tags for filtering.
126
+ """
127
+ from claude_experimental.integration import _require_enabled
128
+ _require_enabled()
129
+ self._record(name, "counter", value, tags)
130
+
131
+ def record_gauge(self, name: str, value: float, tags: dict[str, str] | None = None) -> None:
132
+ """Record a gauge metric (point-in-time value).
133
+
134
+ Args:
135
+ name: Metric name (e.g. "memory.usage_mb").
136
+ value: Current value.
137
+ tags: Optional key-value tags for filtering.
138
+ """
139
+ from claude_experimental.integration import _require_enabled
140
+ _require_enabled()
141
+ self._record(name, "gauge", value, tags)
142
+
143
+ def record_histogram(self, name: str, value: float, tags: dict[str, str] | None = None) -> None:
144
+ """Record a histogram metric (distribution of values).
145
+
146
+ Args:
147
+ name: Metric name (e.g. "response.latency_ms").
148
+ value: Observed value.
149
+ tags: Optional key-value tags for filtering.
150
+ """
151
+ from claude_experimental.integration import _require_enabled
152
+ _require_enabled()
153
+ self._record(name, "histogram", value, tags)
154
+
155
+ def query(
156
+ self,
157
+ name: str,
158
+ metric_type: str | None = None,
159
+ since_minutes: int = 60,
160
+ ) -> list[dict[str, object]]:
161
+ """Query metrics by name within a time window.
162
+
163
+ Args:
164
+ name: Metric name to query.
165
+ metric_type: Optional filter by type (counter/gauge/histogram).
166
+ since_minutes: Look back this many minutes (default 60).
167
+
168
+ Returns:
169
+ List of metric dicts with id, name, metric_type, value, tags, recorded_at.
170
+ """
171
+ from claude_experimental.integration import _require_enabled
172
+ _require_enabled()
173
+
174
+ cutoff = (datetime.now(timezone.utc) - timedelta(minutes=since_minutes)).isoformat()
175
+
176
+ conn = self._connect()
177
+ try:
178
+ sql = "SELECT id, name, metric_type, value, tags, recorded_at FROM metrics WHERE name = ? AND recorded_at >= ?"
179
+ params: list[object] = [name, cutoff]
180
+
181
+ if metric_type is not None:
182
+ sql += " AND metric_type = ?"
183
+ params.append(metric_type)
184
+
185
+ sql += " ORDER BY recorded_at DESC"
186
+
187
+ rows = cast(list[sqlite3.Row], conn.execute(sql, params).fetchall())
188
+ result: list[dict[str, object]] = []
189
+ for r in rows:
190
+ d = dict(r)
191
+ # Parse tags JSON back to dict
192
+ tags_raw = d.get("tags")
193
+ if isinstance(tags_raw, str):
194
+ d["tags"] = json.loads(tags_raw)
195
+ result.append(d)
196
+ return result
197
+ finally:
198
+ conn.close()
199
+
200
+ def aggregate(
201
+ self,
202
+ name: str,
203
+ period: str = "minute",
204
+ ) -> dict[str, object]:
205
+ """Aggregate metrics by time period.
206
+
207
+ Args:
208
+ name: Metric name to aggregate.
209
+ period: Aggregation period - 'minute', 'hour', or 'day'.
210
+
211
+ Returns:
212
+ Dict with keys: name, period, buckets (list of dicts with
213
+ period_key, sum, count, min, max, avg).
214
+ """
215
+ from claude_experimental.integration import _require_enabled
216
+ _require_enabled()
217
+
218
+ # Map period to strftime format for SQLite grouping
219
+ period_formats = {
220
+ "minute": "%Y-%m-%dT%H:%M",
221
+ "hour": "%Y-%m-%dT%H",
222
+ "day": "%Y-%m-%d",
223
+ }
224
+ fmt = period_formats.get(period)
225
+ if fmt is None:
226
+ raise ValueError(f"Invalid period '{period}'. Must be 'minute', 'hour', or 'day'.")
227
+
228
+ conn = self._connect()
229
+ try:
230
+ # Use substr to extract the period key from ISO8601 recorded_at
231
+ key_len = len(fmt.replace("%Y", "2026").replace("%m", "03").replace("%d", "05")
232
+ .replace("%H", "12").replace("%M", "30"))
233
+ sql = (
234
+ "SELECT substr(recorded_at, 1, ?) as period_key, "
235
+ "SUM(value) as total, COUNT(*) as cnt, "
236
+ "MIN(value) as min_val, MAX(value) as max_val, AVG(value) as avg_val "
237
+ "FROM metrics WHERE name = ? "
238
+ "GROUP BY period_key ORDER BY period_key"
239
+ )
240
+ rows = cast(
241
+ list[sqlite3.Row],
242
+ conn.execute(sql, (key_len, name)).fetchall(),
243
+ )
244
+
245
+ buckets: list[dict[str, object]] = []
246
+ for r in rows:
247
+ buckets.append({
248
+ "period_key": r["period_key"],
249
+ "sum": r["total"],
250
+ "count": r["cnt"],
251
+ "min": r["min_val"],
252
+ "max": r["max_val"],
253
+ "avg": r["avg_val"],
254
+ })
255
+
256
+ return {
257
+ "name": name,
258
+ "period": period,
259
+ "buckets": buckets,
260
+ }
261
+ finally:
262
+ conn.close()
263
+
264
+ def rotate_old_data(self, days: int = 30) -> int:
265
+ """Delete metrics older than the specified number of days.
266
+
267
+ Args:
268
+ days: Delete data older than this many days (default 30).
269
+
270
+ Returns:
271
+ Number of rows deleted.
272
+ """
273
+ from claude_experimental.integration import _require_enabled
274
+ _require_enabled()
275
+
276
+ cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
277
+
278
+ conn = self._connect()
279
+ try:
280
+ cursor = conn.execute(
281
+ "DELETE FROM metrics WHERE recorded_at < ?",
282
+ (cutoff,),
283
+ )
284
+ conn.commit()
285
+ return cursor.rowcount
286
+ finally:
287
+ conn.close()
@@ -0,0 +1,16 @@
1
+ """claude_experimental.memory — Tier-2: SQLite-backed episodic/semantic/procedural memory."""
2
+ from __future__ import annotations
3
+
4
+
5
+ def is_available() -> bool:
6
+ from claude_experimental._flags import get_feature_flag
7
+ return get_feature_flag("EXPERIMENTAL_MEMORY", default=False)
8
+
9
+
10
+ def _require_enabled() -> None:
11
+ if not is_available():
12
+ raise RuntimeError(
13
+ "Tier-2 experimental memory is disabled. "
14
+ "Enable with: OMG_EXPERIMENTAL_MEMORY_ENABLED=1 or "
15
+ "settings.json _omg.features.EXPERIMENTAL_MEMORY: true"
16
+ )
@@ -0,0 +1,7 @@
1
+ """Placeholder — implementation pending for this tier."""
2
+
3
+ def _not_implemented(*args, **kwargs):
4
+ raise NotImplementedError(
5
+ "This tier's implementation is pending. "
6
+ "Check the claude_experimental package for available features."
7
+ )