@trac3er/oh-my-god 2.0.0 → 2.0.2
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.
- package/.claude-plugin/marketplace.json +8 -8
- package/.claude-plugin/plugin.json +5 -4
- package/.claude-plugin/scripts/uninstall.sh +74 -3
- package/.claude-plugin/scripts/update.sh +78 -3
- package/.coveragerc +26 -0
- package/.mcp.json +4 -4
- package/CHANGELOG.md +14 -0
- package/CODE_OF_CONDUCT.md +27 -0
- package/CONTRIBUTING.md +62 -0
- package/OMG-setup.sh +1201 -355
- package/README.md +77 -56
- package/SECURITY.md +25 -0
- package/agents/__init__.py +1 -0
- package/agents/model_roles.py +196 -0
- package/agents/omg-architect-mode.md +3 -5
- package/agents/omg-backend-engineer.md +3 -5
- package/agents/omg-database-engineer.md +3 -5
- package/agents/omg-frontend-designer.md +4 -5
- package/agents/omg-implement-mode.md +4 -5
- package/agents/omg-infra-engineer.md +3 -5
- package/agents/omg-research-mode.md +4 -6
- package/agents/omg-security-auditor.md +3 -5
- package/agents/omg-testing-engineer.md +3 -5
- package/build/lib/yaml.py +321 -0
- package/commands/OMG:ai-commit.md +101 -14
- package/commands/OMG:arch.md +302 -19
- package/commands/OMG:ccg.md +12 -7
- package/commands/OMG:compat.md +25 -17
- package/commands/OMG:cost.md +173 -13
- package/commands/OMG:crazy.md +1 -1
- package/commands/OMG:create-agent.md +170 -20
- package/commands/OMG:deps.md +235 -17
- package/commands/OMG:domain-init.md +1 -1
- package/commands/OMG:escalate.md +41 -12
- package/commands/OMG:health-check.md +37 -13
- package/commands/OMG:init.md +122 -14
- package/commands/OMG:project-init.md +1 -1
- package/commands/OMG:session-branch.md +76 -9
- package/commands/OMG:session-fork.md +42 -5
- package/commands/OMG:session-merge.md +124 -8
- package/commands/OMG:setup.md +69 -12
- package/commands/OMG:stats.md +215 -14
- package/commands/OMG:teams.md +19 -10
- package/config/lsp_languages.yaml +8 -0
- package/hooks/__init__.py +0 -0
- package/hooks/_agent_registry.py +423 -0
- package/hooks/_analytics.py +291 -0
- package/hooks/_budget.py +31 -0
- package/hooks/_common.py +569 -0
- package/hooks/_compression_optimizer.py +119 -0
- package/hooks/_cost_ledger.py +176 -0
- package/hooks/_learnings.py +126 -0
- package/hooks/_memory.py +103 -0
- package/hooks/_protected_context.py +150 -0
- package/hooks/_token_counter.py +221 -0
- package/hooks/branch_manager.py +236 -0
- package/hooks/budget_governor.py +232 -0
- package/hooks/circuit-breaker.py +270 -0
- package/hooks/compression_feedback.py +254 -0
- package/hooks/config-guard.py +216 -0
- package/hooks/context_pressure.py +53 -0
- package/hooks/credential_store.py +1020 -0
- package/hooks/fetch-rate-limits.py +212 -0
- package/hooks/firewall.py +48 -0
- package/hooks/hashline-formatter-bridge.py +224 -0
- package/hooks/hashline-injector.py +273 -0
- package/hooks/hashline-validator.py +216 -0
- package/hooks/idle-detector.py +95 -0
- package/hooks/intentgate-keyword-detector.py +188 -0
- package/hooks/magic-keyword-router.py +195 -0
- package/hooks/policy_engine.py +505 -0
- package/hooks/post-tool-failure.py +19 -0
- package/hooks/post-write.py +219 -0
- package/hooks/post_write.py +46 -0
- package/hooks/pre-compact.py +398 -0
- package/hooks/pre-tool-inject.py +98 -0
- package/hooks/prompt-enhancer.py +672 -0
- package/hooks/quality-runner.py +191 -0
- package/hooks/query.py +512 -0
- package/hooks/secret-guard.py +61 -0
- package/hooks/secret_audit.py +144 -0
- package/hooks/session-end-capture.py +137 -0
- package/hooks/session-start.py +277 -0
- package/hooks/setup_wizard.py +582 -0
- package/hooks/shadow_manager.py +297 -0
- package/hooks/state_migration.py +225 -0
- package/hooks/stop-gate.py +7 -0
- package/hooks/stop_dispatcher.py +945 -0
- package/hooks/test-validator.py +361 -0
- package/hooks/test_generator_hook.py +123 -0
- package/hooks/todo-state-tracker.py +114 -0
- package/hooks/tool-ledger.py +149 -0
- package/hooks/trust_review.py +585 -0
- package/hud/omg-hud.mjs +31 -1
- package/lab/__init__.py +1 -0
- package/lab/pipeline.py +75 -0
- package/lab/policies.py +52 -0
- package/package.json +7 -18
- package/plugins/README.md +33 -61
- package/plugins/advanced/commands/OMG:deep-plan.md +3 -3
- package/plugins/advanced/commands/OMG:learn.md +1 -1
- package/plugins/advanced/commands/OMG:security-review.md +3 -3
- package/plugins/advanced/commands/OMG:ship.md +1 -1
- package/plugins/advanced/plugin.json +1 -1
- package/plugins/core/plugin.json +8 -3
- package/plugins/dephealth/__init__.py +0 -0
- package/plugins/dephealth/cve_scanner.py +188 -0
- package/plugins/dephealth/license_checker.py +135 -0
- package/plugins/dephealth/manifest_detector.py +423 -0
- package/plugins/dephealth/vuln_analyzer.py +169 -0
- package/plugins/testgen/__init__.py +0 -0
- package/plugins/testgen/codamosa_engine.py +402 -0
- package/plugins/testgen/edge_case_synthesizer.py +184 -0
- package/plugins/testgen/framework_detector.py +271 -0
- package/plugins/testgen/skeleton_generator.py +219 -0
- package/plugins/viz/__init__.py +0 -0
- package/plugins/viz/ast_parser.py +139 -0
- package/plugins/viz/diagram_generator.py +192 -0
- package/plugins/viz/graph_builder.py +444 -0
- package/plugins/viz/native_parsers.py +259 -0
- package/plugins/viz/regex_parser.py +112 -0
- package/pyproject.toml +81 -0
- package/rules/contextual/write-verify.md +2 -2
- package/rules/core/00-truth.md +1 -1
- package/rules/core/01-surgical.md +1 -1
- package/rules/core/02-circuit-breaker.md +2 -2
- package/rules/core/03-ensemble.md +3 -3
- package/rules/core/04-testing.md +3 -3
- package/runtime/__init__.py +32 -0
- package/runtime/adapters/__init__.py +13 -0
- package/runtime/adapters/claude.py +60 -0
- package/runtime/adapters/gpt.py +53 -0
- package/runtime/adapters/local.py +53 -0
- package/runtime/adoption.py +212 -0
- package/runtime/business_workflow.py +220 -0
- package/runtime/cli_provider.py +85 -0
- package/runtime/compat.py +1299 -0
- package/runtime/custom_agent_loader.py +366 -0
- package/runtime/dispatcher.py +47 -0
- package/runtime/ecosystem.py +371 -0
- package/runtime/legacy_compat.py +7 -0
- package/runtime/mcp_config_writers.py +115 -0
- package/runtime/mcp_lifecycle.py +153 -0
- package/runtime/mcp_memory_server.py +135 -0
- package/runtime/memory_parsers/__init__.py +0 -0
- package/runtime/memory_parsers/chatgpt_parser.py +257 -0
- package/runtime/memory_parsers/claude_import.py +107 -0
- package/runtime/memory_parsers/export.py +97 -0
- package/runtime/memory_parsers/gemini_import.py +91 -0
- package/runtime/memory_parsers/kimi_import.py +91 -0
- package/runtime/memory_store.py +215 -0
- package/runtime/omc_compat.py +7 -0
- package/runtime/providers/__init__.py +0 -0
- package/runtime/providers/codex_provider.py +112 -0
- package/runtime/providers/gemini_provider.py +128 -0
- package/runtime/providers/kimi_provider.py +151 -0
- package/runtime/providers/opencode_provider.py +144 -0
- package/runtime/subagent_dispatcher.py +362 -0
- package/runtime/team_router.py +1167 -0
- package/runtime/tmux_session_manager.py +169 -0
- package/scripts/check-omg-compat-contract-snapshot.py +137 -0
- package/scripts/check-omg-contract-snapshot.py +12 -0
- package/scripts/check-omg-public-ready.py +193 -0
- package/scripts/check-omg-standalone-clean.py +103 -0
- package/scripts/legacy_to_omg_migrate.py +29 -0
- package/scripts/migrate-legacy.py +464 -0
- package/scripts/omc_to_omg_migrate.py +12 -0
- package/scripts/omg.py +492 -0
- package/scripts/settings-merge.py +283 -0
- package/scripts/verify-standalone.sh +8 -4
- package/settings.json +126 -29
- package/templates/profile.yaml +1 -1
- package/tools/__init__.py +2 -0
- package/tools/browser_consent.py +289 -0
- package/tools/browser_stealth.py +481 -0
- package/tools/browser_tool.py +448 -0
- package/tools/changelog_generator.py +347 -0
- package/tools/commit_splitter.py +746 -0
- package/tools/config_discovery.py +151 -0
- package/tools/config_merger.py +449 -0
- package/tools/dashboard_generator.py +300 -0
- package/tools/git_inspector.py +298 -0
- package/tools/lsp_client.py +275 -0
- package/tools/lsp_discovery.py +231 -0
- package/tools/lsp_operations.py +392 -0
- package/tools/pr_generator.py +404 -0
- package/tools/python_repl.py +656 -0
- package/tools/python_sandbox.py +609 -0
- package/tools/search_providers/__init__.py +77 -0
- package/tools/search_providers/brave.py +115 -0
- package/tools/search_providers/exa.py +116 -0
- package/tools/search_providers/jina.py +104 -0
- package/tools/search_providers/perplexity.py +139 -0
- package/tools/search_providers/synthetic.py +74 -0
- package/tools/session_snapshot.py +736 -0
- package/tools/ssh_manager.py +912 -0
- package/tools/theme_engine.py +294 -0
- package/tools/theme_selector.py +137 -0
- package/tools/web_search.py +622 -0
- package/yaml.py +321 -0
- package/.claude-plugin/scripts/install.sh +0 -9
- package/bun.lock +0 -23
- package/bunfig.toml +0 -3
- package/hooks/_budget.ts +0 -1
- package/hooks/_common.ts +0 -63
- package/hooks/circuit-breaker.ts +0 -101
- package/hooks/config-guard.ts +0 -4
- package/hooks/firewall.ts +0 -20
- package/hooks/policy_engine.ts +0 -156
- package/hooks/post-tool-failure.ts +0 -22
- package/hooks/post-write.ts +0 -4
- package/hooks/pre-tool-inject.ts +0 -4
- package/hooks/prompt-enhancer.ts +0 -46
- package/hooks/quality-runner.ts +0 -24
- package/hooks/secret-guard.ts +0 -4
- package/hooks/session-end-capture.ts +0 -19
- package/hooks/session-start.ts +0 -19
- package/hooks/shadow_manager.ts +0 -81
- package/hooks/stop-gate.ts +0 -22
- package/hooks/stop_dispatcher.ts +0 -147
- package/hooks/test-generator-hook.ts +0 -4
- package/hooks/tool-ledger.ts +0 -27
- package/hooks/trust_review.ts +0 -175
- package/lab/pipeline.ts +0 -75
- package/lab/policies.ts +0 -68
- package/runtime/common.ts +0 -111
- package/runtime/compat.ts +0 -174
- package/runtime/dispatcher.ts +0 -25
- package/runtime/ecosystem.ts +0 -186
- package/runtime/provider_bootstrap.ts +0 -99
- package/runtime/provider_smoke.ts +0 -34
- package/runtime/release_readiness.ts +0 -186
- package/runtime/team_router.ts +0 -144
- package/scripts/check-omg-compat-contract-snapshot.ts +0 -20
- package/scripts/check-omg-standalone-clean.ts +0 -12
- package/scripts/check-runtime-clean.ts +0 -94
- package/scripts/omg.ts +0 -352
- package/scripts/settings-merge.ts +0 -93
- package/tools/commit_splitter.ts +0 -23
- package/tools/git_inspector.ts +0 -18
- package/tools/session_snapshot.ts +0 -47
- package/trac3er-oh-my-god-2.0.0.tgz +0 -0
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# pyright: reportMissingImports=false
|
|
3
|
+
"""Productivity patterns analyzer for OMG state data."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
|
|
10
|
+
from hooks.query import (
|
|
11
|
+
get_failure_hotspots,
|
|
12
|
+
get_file_heatmap,
|
|
13
|
+
get_session_summary,
|
|
14
|
+
get_tool_stats,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
JsonMap: TypeAlias = dict[str, object]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _safe_int(value: object, default: int = 0) -> int:
|
|
22
|
+
try:
|
|
23
|
+
if isinstance(value, (int, float, str)):
|
|
24
|
+
return int(value)
|
|
25
|
+
return default
|
|
26
|
+
except (TypeError, ValueError):
|
|
27
|
+
return default
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _safe_float(value: object, default: float = 0.0) -> float:
|
|
31
|
+
try:
|
|
32
|
+
if isinstance(value, (int, float, str)):
|
|
33
|
+
return float(value)
|
|
34
|
+
return default
|
|
35
|
+
except (TypeError, ValueError):
|
|
36
|
+
return default
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _last_edited_iso(project_dir: str, rel_file: str) -> str:
|
|
40
|
+
path = os.path.join(project_dir, rel_file)
|
|
41
|
+
if not os.path.exists(path):
|
|
42
|
+
return ""
|
|
43
|
+
try:
|
|
44
|
+
ts = os.path.getmtime(path)
|
|
45
|
+
except OSError:
|
|
46
|
+
return ""
|
|
47
|
+
return datetime.fromtimestamp(ts).isoformat(timespec="seconds")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _analyze_hotspots(project_dir: str, heatmap: JsonMap) -> list[JsonMap]:
|
|
51
|
+
if not isinstance(heatmap, dict):
|
|
52
|
+
return []
|
|
53
|
+
|
|
54
|
+
out: list[JsonMap] = []
|
|
55
|
+
for file_path, stats in heatmap.items():
|
|
56
|
+
if not isinstance(file_path, str) or not isinstance(stats, dict):
|
|
57
|
+
continue
|
|
58
|
+
edit_count = _safe_int(stats.get("edits", 0), 0)
|
|
59
|
+
if edit_count <= 5:
|
|
60
|
+
continue
|
|
61
|
+
out.append(
|
|
62
|
+
{
|
|
63
|
+
"file": file_path,
|
|
64
|
+
"edit_count": edit_count,
|
|
65
|
+
"last_edited": _last_edited_iso(project_dir, file_path),
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
out.sort(key=lambda item: (-_safe_int(item.get("edit_count", 0), 0), str(item.get("file", ""))))
|
|
70
|
+
return out
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _analyze_error_trends(failures: JsonMap, days: int) -> JsonMap:
|
|
74
|
+
if not isinstance(failures, dict) or not failures:
|
|
75
|
+
return {"trend": "stable", "recent_rate": 0.0, "baseline_rate": 0.0}
|
|
76
|
+
|
|
77
|
+
total_failures = 0
|
|
78
|
+
recent_failures = 0
|
|
79
|
+
for rec in failures.values():
|
|
80
|
+
if not isinstance(rec, dict):
|
|
81
|
+
continue
|
|
82
|
+
count = max(_safe_int(rec.get("count", 0), 0), 0)
|
|
83
|
+
total_failures += count
|
|
84
|
+
recent_errors = rec.get("last_3_errors", [])
|
|
85
|
+
if isinstance(recent_errors, list):
|
|
86
|
+
recent_failures += min(len(recent_errors), count)
|
|
87
|
+
else:
|
|
88
|
+
recent_failures += min(3, count)
|
|
89
|
+
|
|
90
|
+
baseline_total = max(total_failures - recent_failures, 0)
|
|
91
|
+
baseline_days = max(days - 1, 1)
|
|
92
|
+
recent_rate = float(recent_failures)
|
|
93
|
+
baseline_rate = baseline_total / baseline_days
|
|
94
|
+
|
|
95
|
+
if baseline_rate == 0.0 and recent_rate == 0.0:
|
|
96
|
+
trend = "stable"
|
|
97
|
+
elif baseline_rate == 0.0:
|
|
98
|
+
trend = "increasing"
|
|
99
|
+
else:
|
|
100
|
+
delta_ratio = (recent_rate - baseline_rate) / baseline_rate
|
|
101
|
+
if delta_ratio > 0.10:
|
|
102
|
+
trend = "increasing"
|
|
103
|
+
elif delta_ratio < -0.10:
|
|
104
|
+
trend = "decreasing"
|
|
105
|
+
else:
|
|
106
|
+
trend = "stable"
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
"trend": trend,
|
|
110
|
+
"recent_rate": round(recent_rate, 3),
|
|
111
|
+
"baseline_rate": round(baseline_rate, 3),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _analyze_tool_shifts(tool_stats: JsonMap) -> list[JsonMap]:
|
|
116
|
+
if not isinstance(tool_stats, dict) or not tool_stats:
|
|
117
|
+
return []
|
|
118
|
+
|
|
119
|
+
counts: list[float] = []
|
|
120
|
+
for stats in tool_stats.values():
|
|
121
|
+
if not isinstance(stats, dict):
|
|
122
|
+
continue
|
|
123
|
+
counts.append(max(_safe_float(stats.get("count", 0), 0.0), 0.0))
|
|
124
|
+
|
|
125
|
+
if not counts:
|
|
126
|
+
return []
|
|
127
|
+
|
|
128
|
+
baseline = sum(counts) / len(counts)
|
|
129
|
+
if baseline <= 0.0:
|
|
130
|
+
return []
|
|
131
|
+
|
|
132
|
+
shifts: list[JsonMap] = []
|
|
133
|
+
for tool, stats in tool_stats.items():
|
|
134
|
+
if not isinstance(tool, str) or not isinstance(stats, dict):
|
|
135
|
+
continue
|
|
136
|
+
count = max(_safe_float(stats.get("count", 0), 0.0), 0.0)
|
|
137
|
+
change_pct = ((count - baseline) / baseline) * 100.0
|
|
138
|
+
if abs(change_pct) <= 20.0:
|
|
139
|
+
continue
|
|
140
|
+
shifts.append(
|
|
141
|
+
{
|
|
142
|
+
"tool": tool,
|
|
143
|
+
"change_pct": round(change_pct, 2),
|
|
144
|
+
"direction": "up" if change_pct > 0 else "down",
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
shifts.sort(key=lambda item: -abs(_safe_float(item.get("change_pct", 0.0), 0.0)))
|
|
149
|
+
return shifts
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _analyze_session_trends(summary: JsonMap) -> JsonMap:
|
|
153
|
+
duration_sec = 0.0
|
|
154
|
+
if isinstance(summary, dict):
|
|
155
|
+
duration_sec = max(_safe_float(summary.get("duration", 0), 0.0), 0.0)
|
|
156
|
+
avg_duration_min = duration_sec / 60.0
|
|
157
|
+
|
|
158
|
+
if avg_duration_min > 90.0:
|
|
159
|
+
trend = "longer"
|
|
160
|
+
elif 0.0 < avg_duration_min < 30.0:
|
|
161
|
+
trend = "shorter"
|
|
162
|
+
else:
|
|
163
|
+
trend = "stable"
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"avg_duration_min": round(avg_duration_min, 2),
|
|
167
|
+
"trend": trend,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _build_summary_md(report: JsonMap) -> str:
|
|
172
|
+
hotspots_obj = report.get("hotspots", [])
|
|
173
|
+
hotspots = hotspots_obj if isinstance(hotspots_obj, list) else []
|
|
174
|
+
|
|
175
|
+
error_trends_obj = report.get("error_trends", {})
|
|
176
|
+
error_trends = error_trends_obj if isinstance(error_trends_obj, dict) else {}
|
|
177
|
+
|
|
178
|
+
tool_shifts_obj = report.get("tool_usage_shifts", [])
|
|
179
|
+
tool_shifts = tool_shifts_obj if isinstance(tool_shifts_obj, list) else []
|
|
180
|
+
|
|
181
|
+
session_trends_obj = report.get("session_trends", {})
|
|
182
|
+
session_trends = session_trends_obj if isinstance(session_trends_obj, dict) else {}
|
|
183
|
+
|
|
184
|
+
lines = [
|
|
185
|
+
"# Productivity Patterns",
|
|
186
|
+
"",
|
|
187
|
+
"## Refactoring Hotspots",
|
|
188
|
+
]
|
|
189
|
+
if hotspots:
|
|
190
|
+
for item in hotspots:
|
|
191
|
+
if not isinstance(item, dict):
|
|
192
|
+
continue
|
|
193
|
+
lines.append(
|
|
194
|
+
f"- `{item.get('file', '')}`: {item.get('edit_count', 0)} edits "
|
|
195
|
+
f"(last edited: {item.get('last_edited', 'n/a') or 'n/a'})"
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
lines.append("- None detected.")
|
|
199
|
+
|
|
200
|
+
lines.extend(
|
|
201
|
+
[
|
|
202
|
+
"",
|
|
203
|
+
"## Error Trends",
|
|
204
|
+
(
|
|
205
|
+
"- Trend: "
|
|
206
|
+
f"{error_trends.get('trend', 'stable')} "
|
|
207
|
+
f"(recent_rate={error_trends.get('recent_rate', 0.0)}, "
|
|
208
|
+
f"baseline_rate={error_trends.get('baseline_rate', 0.0)})"
|
|
209
|
+
),
|
|
210
|
+
"",
|
|
211
|
+
"## Tool Usage Shifts",
|
|
212
|
+
]
|
|
213
|
+
)
|
|
214
|
+
if tool_shifts:
|
|
215
|
+
for item in tool_shifts:
|
|
216
|
+
if not isinstance(item, dict):
|
|
217
|
+
continue
|
|
218
|
+
lines.append(
|
|
219
|
+
f"- {item.get('tool', 'unknown')}: {item.get('change_pct', 0.0)}% "
|
|
220
|
+
f"({item.get('direction', 'up')})"
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
lines.append("- No significant (>20%) usage shifts.")
|
|
224
|
+
|
|
225
|
+
lines.extend(
|
|
226
|
+
[
|
|
227
|
+
"",
|
|
228
|
+
"## Session Length Trends",
|
|
229
|
+
(
|
|
230
|
+
"- Avg duration: "
|
|
231
|
+
f"{session_trends.get('avg_duration_min', 0.0)} min "
|
|
232
|
+
f"({session_trends.get('trend', 'stable')})"
|
|
233
|
+
),
|
|
234
|
+
]
|
|
235
|
+
)
|
|
236
|
+
return "\n".join(lines)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def analyze_patterns(project_dir: str, days: int = 7) -> JsonMap:
|
|
240
|
+
"""Return productivity pattern report from query-layer analytics."""
|
|
241
|
+
try:
|
|
242
|
+
import importlib
|
|
243
|
+
_common = importlib.import_module("hooks._common")
|
|
244
|
+
if not _common.get_feature_flag("SESSION_ANALYTICS", default=False):
|
|
245
|
+
return {
|
|
246
|
+
"hotspots": [],
|
|
247
|
+
"error_trends": {"trend": "stable", "recent_rate": 0.0, "baseline_rate": 0.0},
|
|
248
|
+
"tool_usage_shifts": [],
|
|
249
|
+
"session_trends": {"avg_duration_min": 0.0, "trend": "stable"},
|
|
250
|
+
"summary_md": "",
|
|
251
|
+
}
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
analysis_days = max(_safe_int(days, 7), 1)
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
heatmap_raw = get_file_heatmap(project_dir)
|
|
259
|
+
heatmap = heatmap_raw if isinstance(heatmap_raw, dict) else {}
|
|
260
|
+
except Exception:
|
|
261
|
+
heatmap = {}
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
failures_raw = get_failure_hotspots(project_dir)
|
|
265
|
+
failures = failures_raw if isinstance(failures_raw, dict) else {}
|
|
266
|
+
except Exception:
|
|
267
|
+
failures = {}
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
tool_stats_raw = get_tool_stats(project_dir, hours=analysis_days * 24)
|
|
271
|
+
tool_stats = tool_stats_raw if isinstance(tool_stats_raw, dict) else {}
|
|
272
|
+
except Exception:
|
|
273
|
+
tool_stats = {}
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
session_summary_raw = get_session_summary(project_dir)
|
|
277
|
+
session_summary = session_summary_raw if isinstance(session_summary_raw, dict) else {}
|
|
278
|
+
except Exception:
|
|
279
|
+
session_summary = {}
|
|
280
|
+
|
|
281
|
+
report: JsonMap = {
|
|
282
|
+
"hotspots": _analyze_hotspots(project_dir, heatmap),
|
|
283
|
+
"error_trends": _analyze_error_trends(failures, analysis_days),
|
|
284
|
+
"tool_usage_shifts": _analyze_tool_shifts(tool_stats),
|
|
285
|
+
"session_trends": _analyze_session_trends(session_summary),
|
|
286
|
+
}
|
|
287
|
+
report["summary_md"] = _build_summary_md(report)
|
|
288
|
+
return report
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
__all__ = ["analyze_patterns"]
|
package/hooks/_budget.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Budget Constants — OMG Context & Prompt Budgets
|
|
4
|
+
|
|
5
|
+
Named constants for token/character budgets across OMG hooks.
|
|
6
|
+
Replaces magic numbers with semantic names for maintainability.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ═══════════════════════════════════════════════════════════
|
|
10
|
+
# Session-start budgets (chars)
|
|
11
|
+
# ═══════════════════════════════════════════════════════════
|
|
12
|
+
BUDGET_SESSION_TOTAL = 2000 # Total context injection budget
|
|
13
|
+
BUDGET_SESSION_IDLE = 200 # When no features active
|
|
14
|
+
BUDGET_PROFILE = 200 # Project profile section
|
|
15
|
+
BUDGET_WORKING_MEMORY = 400 # Working memory section
|
|
16
|
+
BUDGET_HANDOFF = 300 # Handoff section
|
|
17
|
+
BUDGET_MEMORY = 300 # Memory/state section
|
|
18
|
+
BUDGET_FAILURES = 200 # Active failures section
|
|
19
|
+
BUDGET_TOOLS = 100 # Tools inventory section
|
|
20
|
+
BUDGET_PLANNING = 100 # Planning/checklist section
|
|
21
|
+
BUDGET_RALPH = 100 # Ralph/persistent mode section
|
|
22
|
+
|
|
23
|
+
# ═══════════════════════════════════════════════════════════
|
|
24
|
+
# Prompt-enhancer budgets (chars)
|
|
25
|
+
# ═══════════════════════════════════════════════════════════
|
|
26
|
+
BUDGET_PROMPT_TOTAL = 1000 # Total prompt enhancement budget
|
|
27
|
+
BUDGET_INTENT_DISCIPLINE = 200 # Intent classification + discipline
|
|
28
|
+
BUDGET_KNOWLEDGE = 300 # Knowledge retrieval section
|
|
29
|
+
BUDGET_LEARNINGS = 200 # Learnings/patterns section
|
|
30
|
+
BUDGET_AGENT_ROUTING = 200 # Agent routing directives
|
|
31
|
+
BUDGET_MODE = 100 # Mode detection (ulw/crazy/etc)
|