@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.
Files changed (243) hide show
  1. package/.claude-plugin/marketplace.json +8 -8
  2. package/.claude-plugin/plugin.json +5 -4
  3. package/.claude-plugin/scripts/uninstall.sh +74 -3
  4. package/.claude-plugin/scripts/update.sh +78 -3
  5. package/.coveragerc +26 -0
  6. package/.mcp.json +4 -4
  7. package/CHANGELOG.md +14 -0
  8. package/CODE_OF_CONDUCT.md +27 -0
  9. package/CONTRIBUTING.md +62 -0
  10. package/OMG-setup.sh +1201 -355
  11. package/README.md +77 -56
  12. package/SECURITY.md +25 -0
  13. package/agents/__init__.py +1 -0
  14. package/agents/model_roles.py +196 -0
  15. package/agents/omg-architect-mode.md +3 -5
  16. package/agents/omg-backend-engineer.md +3 -5
  17. package/agents/omg-database-engineer.md +3 -5
  18. package/agents/omg-frontend-designer.md +4 -5
  19. package/agents/omg-implement-mode.md +4 -5
  20. package/agents/omg-infra-engineer.md +3 -5
  21. package/agents/omg-research-mode.md +4 -6
  22. package/agents/omg-security-auditor.md +3 -5
  23. package/agents/omg-testing-engineer.md +3 -5
  24. package/build/lib/yaml.py +321 -0
  25. package/commands/OMG:ai-commit.md +101 -14
  26. package/commands/OMG:arch.md +302 -19
  27. package/commands/OMG:ccg.md +12 -7
  28. package/commands/OMG:compat.md +25 -17
  29. package/commands/OMG:cost.md +173 -13
  30. package/commands/OMG:crazy.md +1 -1
  31. package/commands/OMG:create-agent.md +170 -20
  32. package/commands/OMG:deps.md +235 -17
  33. package/commands/OMG:domain-init.md +1 -1
  34. package/commands/OMG:escalate.md +41 -12
  35. package/commands/OMG:health-check.md +37 -13
  36. package/commands/OMG:init.md +122 -14
  37. package/commands/OMG:project-init.md +1 -1
  38. package/commands/OMG:session-branch.md +76 -9
  39. package/commands/OMG:session-fork.md +42 -5
  40. package/commands/OMG:session-merge.md +124 -8
  41. package/commands/OMG:setup.md +69 -12
  42. package/commands/OMG:stats.md +215 -14
  43. package/commands/OMG:teams.md +19 -10
  44. package/config/lsp_languages.yaml +8 -0
  45. package/hooks/__init__.py +0 -0
  46. package/hooks/_agent_registry.py +423 -0
  47. package/hooks/_analytics.py +291 -0
  48. package/hooks/_budget.py +31 -0
  49. package/hooks/_common.py +569 -0
  50. package/hooks/_compression_optimizer.py +119 -0
  51. package/hooks/_cost_ledger.py +176 -0
  52. package/hooks/_learnings.py +126 -0
  53. package/hooks/_memory.py +103 -0
  54. package/hooks/_protected_context.py +150 -0
  55. package/hooks/_token_counter.py +221 -0
  56. package/hooks/branch_manager.py +236 -0
  57. package/hooks/budget_governor.py +232 -0
  58. package/hooks/circuit-breaker.py +270 -0
  59. package/hooks/compression_feedback.py +254 -0
  60. package/hooks/config-guard.py +216 -0
  61. package/hooks/context_pressure.py +53 -0
  62. package/hooks/credential_store.py +1020 -0
  63. package/hooks/fetch-rate-limits.py +212 -0
  64. package/hooks/firewall.py +48 -0
  65. package/hooks/hashline-formatter-bridge.py +224 -0
  66. package/hooks/hashline-injector.py +273 -0
  67. package/hooks/hashline-validator.py +216 -0
  68. package/hooks/idle-detector.py +95 -0
  69. package/hooks/intentgate-keyword-detector.py +188 -0
  70. package/hooks/magic-keyword-router.py +195 -0
  71. package/hooks/policy_engine.py +505 -0
  72. package/hooks/post-tool-failure.py +19 -0
  73. package/hooks/post-write.py +219 -0
  74. package/hooks/post_write.py +46 -0
  75. package/hooks/pre-compact.py +398 -0
  76. package/hooks/pre-tool-inject.py +98 -0
  77. package/hooks/prompt-enhancer.py +672 -0
  78. package/hooks/quality-runner.py +191 -0
  79. package/hooks/query.py +512 -0
  80. package/hooks/secret-guard.py +61 -0
  81. package/hooks/secret_audit.py +144 -0
  82. package/hooks/session-end-capture.py +137 -0
  83. package/hooks/session-start.py +277 -0
  84. package/hooks/setup_wizard.py +582 -0
  85. package/hooks/shadow_manager.py +297 -0
  86. package/hooks/state_migration.py +225 -0
  87. package/hooks/stop-gate.py +7 -0
  88. package/hooks/stop_dispatcher.py +945 -0
  89. package/hooks/test-validator.py +361 -0
  90. package/hooks/test_generator_hook.py +123 -0
  91. package/hooks/todo-state-tracker.py +114 -0
  92. package/hooks/tool-ledger.py +149 -0
  93. package/hooks/trust_review.py +585 -0
  94. package/hud/omg-hud.mjs +31 -1
  95. package/lab/__init__.py +1 -0
  96. package/lab/pipeline.py +75 -0
  97. package/lab/policies.py +52 -0
  98. package/package.json +7 -18
  99. package/plugins/README.md +33 -61
  100. package/plugins/advanced/commands/OMG:deep-plan.md +3 -3
  101. package/plugins/advanced/commands/OMG:learn.md +1 -1
  102. package/plugins/advanced/commands/OMG:security-review.md +3 -3
  103. package/plugins/advanced/commands/OMG:ship.md +1 -1
  104. package/plugins/advanced/plugin.json +1 -1
  105. package/plugins/core/plugin.json +8 -3
  106. package/plugins/dephealth/__init__.py +0 -0
  107. package/plugins/dephealth/cve_scanner.py +188 -0
  108. package/plugins/dephealth/license_checker.py +135 -0
  109. package/plugins/dephealth/manifest_detector.py +423 -0
  110. package/plugins/dephealth/vuln_analyzer.py +169 -0
  111. package/plugins/testgen/__init__.py +0 -0
  112. package/plugins/testgen/codamosa_engine.py +402 -0
  113. package/plugins/testgen/edge_case_synthesizer.py +184 -0
  114. package/plugins/testgen/framework_detector.py +271 -0
  115. package/plugins/testgen/skeleton_generator.py +219 -0
  116. package/plugins/viz/__init__.py +0 -0
  117. package/plugins/viz/ast_parser.py +139 -0
  118. package/plugins/viz/diagram_generator.py +192 -0
  119. package/plugins/viz/graph_builder.py +444 -0
  120. package/plugins/viz/native_parsers.py +259 -0
  121. package/plugins/viz/regex_parser.py +112 -0
  122. package/pyproject.toml +81 -0
  123. package/rules/contextual/write-verify.md +2 -2
  124. package/rules/core/00-truth.md +1 -1
  125. package/rules/core/01-surgical.md +1 -1
  126. package/rules/core/02-circuit-breaker.md +2 -2
  127. package/rules/core/03-ensemble.md +3 -3
  128. package/rules/core/04-testing.md +3 -3
  129. package/runtime/__init__.py +32 -0
  130. package/runtime/adapters/__init__.py +13 -0
  131. package/runtime/adapters/claude.py +60 -0
  132. package/runtime/adapters/gpt.py +53 -0
  133. package/runtime/adapters/local.py +53 -0
  134. package/runtime/adoption.py +212 -0
  135. package/runtime/business_workflow.py +220 -0
  136. package/runtime/cli_provider.py +85 -0
  137. package/runtime/compat.py +1299 -0
  138. package/runtime/custom_agent_loader.py +366 -0
  139. package/runtime/dispatcher.py +47 -0
  140. package/runtime/ecosystem.py +371 -0
  141. package/runtime/legacy_compat.py +7 -0
  142. package/runtime/mcp_config_writers.py +115 -0
  143. package/runtime/mcp_lifecycle.py +153 -0
  144. package/runtime/mcp_memory_server.py +135 -0
  145. package/runtime/memory_parsers/__init__.py +0 -0
  146. package/runtime/memory_parsers/chatgpt_parser.py +257 -0
  147. package/runtime/memory_parsers/claude_import.py +107 -0
  148. package/runtime/memory_parsers/export.py +97 -0
  149. package/runtime/memory_parsers/gemini_import.py +91 -0
  150. package/runtime/memory_parsers/kimi_import.py +91 -0
  151. package/runtime/memory_store.py +215 -0
  152. package/runtime/omc_compat.py +7 -0
  153. package/runtime/providers/__init__.py +0 -0
  154. package/runtime/providers/codex_provider.py +112 -0
  155. package/runtime/providers/gemini_provider.py +128 -0
  156. package/runtime/providers/kimi_provider.py +151 -0
  157. package/runtime/providers/opencode_provider.py +144 -0
  158. package/runtime/subagent_dispatcher.py +362 -0
  159. package/runtime/team_router.py +1167 -0
  160. package/runtime/tmux_session_manager.py +169 -0
  161. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  162. package/scripts/check-omg-contract-snapshot.py +12 -0
  163. package/scripts/check-omg-public-ready.py +193 -0
  164. package/scripts/check-omg-standalone-clean.py +103 -0
  165. package/scripts/legacy_to_omg_migrate.py +29 -0
  166. package/scripts/migrate-legacy.py +464 -0
  167. package/scripts/omc_to_omg_migrate.py +12 -0
  168. package/scripts/omg.py +492 -0
  169. package/scripts/settings-merge.py +283 -0
  170. package/scripts/verify-standalone.sh +8 -4
  171. package/settings.json +126 -29
  172. package/templates/profile.yaml +1 -1
  173. package/tools/__init__.py +2 -0
  174. package/tools/browser_consent.py +289 -0
  175. package/tools/browser_stealth.py +481 -0
  176. package/tools/browser_tool.py +448 -0
  177. package/tools/changelog_generator.py +347 -0
  178. package/tools/commit_splitter.py +746 -0
  179. package/tools/config_discovery.py +151 -0
  180. package/tools/config_merger.py +449 -0
  181. package/tools/dashboard_generator.py +300 -0
  182. package/tools/git_inspector.py +298 -0
  183. package/tools/lsp_client.py +275 -0
  184. package/tools/lsp_discovery.py +231 -0
  185. package/tools/lsp_operations.py +392 -0
  186. package/tools/pr_generator.py +404 -0
  187. package/tools/python_repl.py +656 -0
  188. package/tools/python_sandbox.py +609 -0
  189. package/tools/search_providers/__init__.py +77 -0
  190. package/tools/search_providers/brave.py +115 -0
  191. package/tools/search_providers/exa.py +116 -0
  192. package/tools/search_providers/jina.py +104 -0
  193. package/tools/search_providers/perplexity.py +139 -0
  194. package/tools/search_providers/synthetic.py +74 -0
  195. package/tools/session_snapshot.py +736 -0
  196. package/tools/ssh_manager.py +912 -0
  197. package/tools/theme_engine.py +294 -0
  198. package/tools/theme_selector.py +137 -0
  199. package/tools/web_search.py +622 -0
  200. package/yaml.py +321 -0
  201. package/.claude-plugin/scripts/install.sh +0 -9
  202. package/bun.lock +0 -23
  203. package/bunfig.toml +0 -3
  204. package/hooks/_budget.ts +0 -1
  205. package/hooks/_common.ts +0 -63
  206. package/hooks/circuit-breaker.ts +0 -101
  207. package/hooks/config-guard.ts +0 -4
  208. package/hooks/firewall.ts +0 -20
  209. package/hooks/policy_engine.ts +0 -156
  210. package/hooks/post-tool-failure.ts +0 -22
  211. package/hooks/post-write.ts +0 -4
  212. package/hooks/pre-tool-inject.ts +0 -4
  213. package/hooks/prompt-enhancer.ts +0 -46
  214. package/hooks/quality-runner.ts +0 -24
  215. package/hooks/secret-guard.ts +0 -4
  216. package/hooks/session-end-capture.ts +0 -19
  217. package/hooks/session-start.ts +0 -19
  218. package/hooks/shadow_manager.ts +0 -81
  219. package/hooks/stop-gate.ts +0 -22
  220. package/hooks/stop_dispatcher.ts +0 -147
  221. package/hooks/test-generator-hook.ts +0 -4
  222. package/hooks/tool-ledger.ts +0 -27
  223. package/hooks/trust_review.ts +0 -175
  224. package/lab/pipeline.ts +0 -75
  225. package/lab/policies.ts +0 -68
  226. package/runtime/common.ts +0 -111
  227. package/runtime/compat.ts +0 -174
  228. package/runtime/dispatcher.ts +0 -25
  229. package/runtime/ecosystem.ts +0 -186
  230. package/runtime/provider_bootstrap.ts +0 -99
  231. package/runtime/provider_smoke.ts +0 -34
  232. package/runtime/release_readiness.ts +0 -186
  233. package/runtime/team_router.ts +0 -144
  234. package/scripts/check-omg-compat-contract-snapshot.ts +0 -20
  235. package/scripts/check-omg-standalone-clean.ts +0 -12
  236. package/scripts/check-runtime-clean.ts +0 -94
  237. package/scripts/omg.ts +0 -352
  238. package/scripts/settings-merge.ts +0 -93
  239. package/tools/commit_splitter.ts +0 -23
  240. package/tools/git_inspector.ts +0 -18
  241. package/tools/session_snapshot.ts +0 -47
  242. package/trac3er-oh-my-god-2.0.0.tgz +0 -0
  243. package/tsconfig.json +0 -15
@@ -1,156 +0,0 @@
1
- import { realpathSync } from "node:fs";
2
- import { basename, normalize } from "node:path";
3
-
4
- export type PolicyDecision = {
5
- action: "allow" | "ask" | "deny";
6
- risk_level: "low" | "med" | "high" | "critical";
7
- reason: string;
8
- controls: string[];
9
- };
10
-
11
- const DESTRUCTIVE_PATTERNS: Array<[RegExp, string]> = [
12
- [/rm\s+-[A-Za-z]*r[A-Za-z]*f[A-Za-z]*\s+\/(\s|$|\*)/, "rm -rf /"],
13
- [/rm\s+-[A-Za-z]*r[A-Za-z]*f[A-Za-z]*\s+~\/?(\s|$|\*)/, "rm -rf ~"],
14
- [/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/, "fork bomb"],
15
- [/sudo\s+(dd|mkfs|fdisk|parted|wipefs)\b/, "destructive disk op"],
16
- [/sudo\s+rm\b/, "sudo rm"]
17
- ];
18
-
19
- const PIPE_TO_SHELL = [
20
- /(curl|wget)\s+.*\|\s*(sudo\s+)?(ba)?sh/,
21
- /(curl|wget)\s+.*\|\s*(bun|node)\b/,
22
- /base64\s+.*\|\s*(ba)?sh/
23
- ];
24
-
25
- const SECRET_PATTERNS = [
26
- /\.(env|pem|key|p12|pfx|jks|keystore|netrc|npmrc|pypirc)\b/i,
27
- /\/\.aws\/(credentials|config)\b/i,
28
- /\/\.kube\/config\b/i,
29
- /\/id_(rsa|ed25519|ecdsa)\b/i,
30
- /\/\.ssh\//i,
31
- /\bsecrets?\//i
32
- ];
33
-
34
- const SECRET_FILE_NAMES = new Set([
35
- ".env",
36
- ".env.local",
37
- ".env.development",
38
- ".env.production",
39
- ".env.staging",
40
- ".env.test",
41
- ".npmrc",
42
- ".netrc",
43
- "id_rsa",
44
- "id_ed25519",
45
- "id_ecdsa",
46
- "id_rsa.pub",
47
- "id_ed25519.pub",
48
- "id_ecdsa.pub"
49
- ]);
50
-
51
- const EXAMPLE_ENV_FILES = new Set([".env.example", ".env.sample", ".env.template"]);
52
-
53
- const SENSITIVE_PATH_PATTERNS = [
54
- /\/\.aws\/(credentials|config)$/i,
55
- /\/\.kube\/config$/i,
56
- /\/\.ssh\//i,
57
- /\/\.gnupg\//i,
58
- /\/secrets?\//i,
59
- /\.(pem|key|p12|pfx|jks|keystore)$/i,
60
- /(^|\/)secret[s]?\./i,
61
- /(^|\/)credential[s]?\./i,
62
- /(^|\/)password[s]?\./i,
63
- /(^|\/)token[s]?\./i,
64
- /(^|\/)\.docker\/config\.json$/i,
65
- /(^|\/)\.git-credentials$/i
66
- ];
67
-
68
- function decision(
69
- action: PolicyDecision["action"],
70
- risk_level: PolicyDecision["risk_level"],
71
- reason: string,
72
- controls: string[] = []
73
- ): PolicyDecision {
74
- return { action, risk_level, reason, controls };
75
- }
76
-
77
- export function evaluateBashCommand(command: string): PolicyDecision {
78
- if (!command.trim()) {
79
- return decision("allow", "low", "empty command");
80
- }
81
- for (const [pattern, label] of DESTRUCTIVE_PATTERNS) {
82
- if (pattern.test(command)) {
83
- return decision("deny", "critical", `Blocked: ${label}`, ["destructive-op"]);
84
- }
85
- }
86
- for (const pattern of PIPE_TO_SHELL) {
87
- if (pattern.test(command)) {
88
- return decision("deny", "critical", "Blocked: pipe-to-shell", ["remote-code-exec"]);
89
- }
90
- }
91
- if (/\beval\s+["'$`]/.test(command)) {
92
- return decision("deny", "high", "Blocked: dynamic eval", ["dynamic-eval"]);
93
- }
94
- for (const pattern of SECRET_PATTERNS) {
95
- if (pattern.test(command) && /\b(cat|less|more|head|tail|grep|awk|bun|node)\b/i.test(command)) {
96
- return decision("deny", "critical", "Blocked: reading secret file", ["secret-access"]);
97
- }
98
- }
99
- if (/\b(curl|wget|ssh|scp|rsync)\b/.test(command)) {
100
- return decision("ask", "med", `Network or remote operation: ${command.slice(0, 120)}`, ["human-approval"]);
101
- }
102
- if (/\bgit\s+push\b.*(--force|-f)/.test(command)) {
103
- return decision("ask", "med", "Force push", ["human-approval"]);
104
- }
105
- return decision("allow", "low", "command allowed");
106
- }
107
-
108
- function safeRealPath(path: string): string {
109
- try {
110
- return realpathSync(path);
111
- } catch {
112
- return normalize(path);
113
- }
114
- }
115
-
116
- export function evaluateFileAccess(tool: string, filePath: string): PolicyDecision {
117
- if (!filePath.trim()) {
118
- return decision("allow", "low", "no file");
119
- }
120
-
121
- const normalizedPath = safeRealPath(filePath);
122
- const fileName = basename(normalizedPath).toLowerCase();
123
-
124
- if (EXAMPLE_ENV_FILES.has(fileName) && ["Write", "Edit", "MultiEdit"].includes(tool)) {
125
- return decision("deny", "high", `Modifying example env file blocked: ${filePath}`, ["immutable-env-template"]);
126
- }
127
-
128
- if (SECRET_FILE_NAMES.has(fileName)) {
129
- return decision("deny", "critical", `Secret file blocked: ${filePath}`, ["secret-access"]);
130
- }
131
-
132
- if (/^\.env(\..+)?$/i.test(fileName) && !EXAMPLE_ENV_FILES.has(fileName)) {
133
- return decision("deny", "critical", `Environment file blocked: ${filePath}`, ["secret-access"]);
134
- }
135
-
136
- for (const pattern of SENSITIVE_PATH_PATTERNS) {
137
- if (pattern.test(normalizedPath)) {
138
- return decision("deny", "critical", `Sensitive path blocked: ${filePath}`, ["secret-access"]);
139
- }
140
- }
141
-
142
- return decision("allow", "low", "file allowed");
143
- }
144
-
145
- export function toPretoolHookOutput(decisionValue: PolicyDecision): Record<string, unknown> | null {
146
- if (decisionValue.action === "allow") {
147
- return null;
148
- }
149
- return {
150
- hookSpecificOutput: {
151
- hookEventName: "PreToolUse",
152
- permissionDecision: decisionValue.action,
153
- permissionDecisionReason: decisionValue.reason
154
- }
155
- };
156
- }
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { join } from "node:path";
3
- import { readJsonFromStdin, resolveProjectDir, writeJsonFile } from "./_common.ts";
4
-
5
- async function main() {
6
- const payload = await readJsonFromStdin<any>({});
7
- const output = join(resolveProjectDir(), ".omg", "state", "last-tool-error.json");
8
- writeJsonFile(output, {
9
- ts: new Date().toISOString(),
10
- tool: payload.tool_name || "",
11
- tool_input: payload.tool_input || {},
12
- tool_response: payload.tool_response || {}
13
- });
14
- }
15
-
16
- if (import.meta.main) {
17
- try {
18
- await main();
19
- } catch {
20
- process.exit(0);
21
- }
22
- }
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { noopHookMain } from "./_common.ts";
3
-
4
- noopHookMain();
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { noopHookMain } from "./_common.ts";
3
-
4
- noopHookMain();
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { BUDGET_PROMPT_TOTAL } from "./_budget.ts";
3
- import { readJsonFromStdin } from "./_common.ts";
4
-
5
- const ZERO_SIGNALS = /^(hello|hi|ok|thanks|yes|no|goodbye|hey there)$/i;
6
- const KEYWORDS =
7
- /\b(fix|bug|implement|review|refactor|auth|login|jwt|oauth|database|deploy|rewrite|redesign|frontend|backend|ui|ux|css|layout)\b|전체|수정|구현|버그|에러|리팩토링/i;
8
-
9
- function buildInjection(prompt: string): string {
10
- const base = [
11
- "OMG Bun runtime context:",
12
- "- Preserve proof-oriented workflow.",
13
- "- Prefer focused edits and explicit verification.",
14
- `- Active prompt: ${prompt}`
15
- ].join("\n");
16
- return base.length > BUDGET_PROMPT_TOTAL ? base.slice(0, BUDGET_PROMPT_TOTAL) : base;
17
- }
18
-
19
- async function main() {
20
- const payload = await readJsonFromStdin<any>({});
21
- const prompt = String(payload.user_message || "").trim();
22
- if (!prompt || ZERO_SIGNALS.test(prompt)) {
23
- return;
24
- }
25
- if (!KEYWORDS.test(prompt)) {
26
- return;
27
- }
28
- process.stdout.write(
29
- `${JSON.stringify(
30
- {
31
- contextInjection: buildInjection(prompt),
32
- sources: ["omg-bun-runtime"]
33
- },
34
- null,
35
- 2
36
- )}\n`
37
- );
38
- }
39
-
40
- if (import.meta.main) {
41
- try {
42
- await main();
43
- } catch {
44
- process.exit(0);
45
- }
46
- }
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { resolveProjectDir } from "./_common.ts";
5
-
6
- const ALLOWLIST = [/^bun test\b/, /^npm test\b/, /^vitest\b/, /^jest\b/];
7
-
8
- if (import.meta.main) {
9
- try {
10
- const configPath = join(resolveProjectDir(), ".omg", "quality-gate.json");
11
- if (!existsSync(configPath)) {
12
- process.exit(0);
13
- }
14
- const payload = JSON.parse(readFileSync(configPath, "utf8"));
15
- const command = String(payload.test || payload.lint || "");
16
- if (command && !ALLOWLIST.some((pattern) => pattern.test(command))) {
17
- process.stdout.write(
18
- `${JSON.stringify({ status: "blocked", reason: `BLOCKED quality command: ${command}` }, null, 2)}\n`
19
- );
20
- }
21
- } catch {
22
- process.exit(0);
23
- }
24
- }
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { noopHookMain } from "./_common.ts";
3
-
4
- noopHookMain();
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { join } from "node:path";
3
- import { readJsonFromStdin, resolveProjectDir, writeJsonFile } from "./_common.ts";
4
-
5
- async function main() {
6
- const payload = await readJsonFromStdin<any>({});
7
- writeJsonFile(join(resolveProjectDir(), ".omg", "state", "session-end.json"), {
8
- ts: new Date().toISOString(),
9
- payload
10
- });
11
- }
12
-
13
- if (import.meta.main) {
14
- try {
15
- await main();
16
- } catch {
17
- process.exit(0);
18
- }
19
- }
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { join } from "node:path";
3
- import { readJsonFromStdin, resolveProjectDir, writeJsonFile } from "./_common.ts";
4
-
5
- async function main() {
6
- const payload = await readJsonFromStdin<any>({});
7
- writeJsonFile(join(resolveProjectDir(), ".omg", "state", "session-start.json"), {
8
- ts: new Date().toISOString(),
9
- payload
10
- });
11
- }
12
-
13
- if (import.meta.main) {
14
- try {
15
- await main();
16
- } catch {
17
- process.exit(0);
18
- }
19
- }
@@ -1,81 +0,0 @@
1
- import { copyFileSync, existsSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
2
- import { dirname, join, relative } from "node:path";
3
- import { ensureDir, writeJsonFile } from "./_common.ts";
4
-
5
- function shadowRoot(projectDir: string): string {
6
- return join(projectDir, ".omg", "shadow");
7
- }
8
-
9
- function evidenceRoot(projectDir: string): string {
10
- return join(projectDir, ".omg", "evidence");
11
- }
12
-
13
- export function createEvidencePack(
14
- projectDir: string,
15
- runId: string,
16
- options: {
17
- tests?: unknown[];
18
- security_scans?: unknown[];
19
- diff_summary?: Record<string, unknown>;
20
- reproducibility?: Record<string, unknown>;
21
- unresolved_risks?: string[];
22
- } = {}
23
- ): string {
24
- ensureDir(evidenceRoot(projectDir));
25
- const output = join(evidenceRoot(projectDir), `${runId}.json`);
26
- writeJsonFile(output, {
27
- schema: "EvidencePack",
28
- run_id: runId,
29
- created_at: new Date().toISOString(),
30
- tests: options.tests || [],
31
- security_scans: options.security_scans || [],
32
- diff_summary: options.diff_summary || {},
33
- reproducibility: options.reproducibility || {},
34
- unresolved_risks: options.unresolved_risks || []
35
- });
36
- return output;
37
- }
38
-
39
- export function hasRecentEvidence(projectDir: string, hours = 24): boolean {
40
- const base = evidenceRoot(projectDir);
41
- if (!existsSync(base)) {
42
- return false;
43
- }
44
- const maxAge = hours * 60 * 60 * 1000;
45
- return readdirSync(base).some((name) => {
46
- if (!name.endsWith(".json")) {
47
- return false;
48
- }
49
- const age = Date.now() - statSync(join(base, name)).mtimeMs;
50
- return age <= maxAge;
51
- });
52
- }
53
-
54
- export function recordShadowWrite(projectDir: string, runId: string, filePath: string, source = "tool") {
55
- const runDir = join(shadowRoot(projectDir), runId);
56
- const overlayPath = join(runDir, "overlay", relative(projectDir, filePath).replace(/\.\./g, "_up_"));
57
- ensureDir(join(runDir, "overlay"));
58
- if (existsSync(filePath)) {
59
- ensureDir(dirname(overlayPath));
60
- copyFileSync(filePath, overlayPath);
61
- }
62
- const manifestPath = join(runDir, "manifest.json");
63
- const manifest = existsSync(manifestPath)
64
- ? JSON.parse(readFileSync(manifestPath, "utf8"))
65
- : { run_id: runId, created_at: new Date().toISOString(), status: "open", files: [] };
66
- manifest.files = Array.isArray(manifest.files) ? manifest.files.filter((entry: any) => entry.file !== filePath) : [];
67
- manifest.files.push({
68
- file: filePath,
69
- shadow_file: relative(runDir, overlayPath),
70
- recorded_at: new Date().toISOString(),
71
- source
72
- });
73
- writeJsonFile(manifestPath, manifest);
74
- return manifest;
75
- }
76
-
77
- export function dropShadow(projectDir: string, runId: string) {
78
- const runDir = join(shadowRoot(projectDir), runId);
79
- rmSync(runDir, { recursive: true, force: true });
80
- return { run_id: runId, dropped: true };
81
- }
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { readJsonFromStdin } from "./_common.ts";
3
- import { runStopDispatcher } from "./stop_dispatcher.ts";
4
-
5
- async function main() {
6
- const payload = await readJsonFromStdin<any>({});
7
- if (payload.stop_hook_active) {
8
- return;
9
- }
10
- const blocks = runStopDispatcher(payload);
11
- if (blocks.length > 0) {
12
- process.stdout.write(`${JSON.stringify({ status: "blocked", blocks }, null, 2)}\n`);
13
- }
14
- }
15
-
16
- if (import.meta.main) {
17
- try {
18
- await main();
19
- } catch {
20
- process.exit(0);
21
- }
22
- }
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { readFileSync, existsSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { getFeatureFlag, readJsonFromStdin, resolveProjectDir } from "./_common.ts";
5
-
6
- type StopData = Record<string, any>;
7
-
8
- function recentCommands(data: StopData): string[] {
9
- return Array.isArray(data?._stop_ctx?.recent_commands) ? data._stop_ctx.recent_commands.map(String) : [];
10
- }
11
-
12
- export function checkVerification(data: StopData): string[] {
13
- if (!getFeatureFlag("STOP_GATE", true)) {
14
- return [];
15
- }
16
- if (!data?._stop_ctx?.has_source_writes) {
17
- return [];
18
- }
19
- const hasVerification = recentCommands(data).some((command) => /\b(bun test|npm test|vitest|jest|cargo test|go test)\b/.test(command));
20
- return hasVerification ? [] : ["NO verification commands were executed after source writes."];
21
- }
22
-
23
- export function checkDiffBudget(_data: StopData, projectDir: string): string[] {
24
- if (!getFeatureFlag("STOP_GATE", true)) {
25
- return [];
26
- }
27
- const names = Bun.spawnSync({ cmd: ["git", "diff", "--name-only"], cwd: projectDir, stdout: "pipe", stderr: "ignore" });
28
- const files = names.stdout.toString().split(/\r?\n/).filter(Boolean);
29
- const stats = Bun.spawnSync({ cmd: ["git", "diff", "--numstat"], cwd: projectDir, stdout: "pipe", stderr: "ignore" });
30
- const lineCount = stats.stdout
31
- .toString()
32
- .split(/\r?\n/)
33
- .filter(Boolean)
34
- .reduce((total: number, line: string) => {
35
- const [added, removed] = line.split("\t");
36
- return total + Number(added || 0) + Number(removed || 0);
37
- }, 0);
38
- return files.length > 3 || lineCount > 150 ? [`Diff exceeds budget (${files.length} files, ${lineCount} lines).`] : [];
39
- }
40
-
41
- export function checkRecentFailures(data: StopData): string[] {
42
- if (!getFeatureFlag("STOP_GATE", true)) {
43
- return [];
44
- }
45
- const entries = Array.isArray(data?._stop_ctx?.recent_entries) ? data._stop_ctx.recent_entries.slice(-3) : [];
46
- if (entries.length === 3 && entries.every((entry: any) => Number(entry.exit_code ?? 0) !== 0)) {
47
- return ["Last 3 commands ALL FAILED."];
48
- }
49
- return [];
50
- }
51
-
52
- export function checkTestExecution(data: StopData): string[] {
53
- if (!getFeatureFlag("STOP_GATE", true)) {
54
- return [];
55
- }
56
- const changed = Array.isArray(data?._changed_files) ? data._changed_files.map(String) : [];
57
- const changedTests = changed.some((path) => /(^|\/)(tests?|__tests__)\//.test(path));
58
- return data?._stop_ctx?.has_material_writes && changedTests && !data?._has_test
59
- ? ["Tests changed but the test suite was never executed."]
60
- : [];
61
- }
62
-
63
- export function checkTestValidatorCoverage(data: StopData): string[] {
64
- if (!getFeatureFlag("STOP_GATE", true)) {
65
- return [];
66
- }
67
- const changed = Array.isArray(data?._changed_files) ? data._changed_files.map(String) : [];
68
- const touchedSource = changed.some((path) => /(^|\/)src\//.test(path) || /\.(ts|tsx|js|jsx)$/.test(path));
69
- const touchedTests = changed.some((path) => /(^|\/)(tests?|__tests__)\//.test(path) || /\.test\.(ts|tsx|js|jsx)$/.test(path));
70
- return data?._stop_ctx?.has_source_writes && touchedSource && !touchedTests
71
- ? ["TEST-VALIDATOR: source changed without matching test updates."]
72
- : [];
73
- }
74
-
75
- export function checkFalseFix(data: StopData): string[] {
76
- if (!getFeatureFlag("STOP_GATE", true)) {
77
- return [];
78
- }
79
- const changed = Array.isArray(data?._changed_files) ? data._changed_files.map(String) : [];
80
- if (!data?._stop_ctx?.has_material_writes || changed.length === 0) {
81
- return [];
82
- }
83
- const onlyTestsAndScripts = changed.every((path) => /(^|\/)(tests?|scripts)\//.test(path) || path.endsWith(".md"));
84
- return onlyTestsAndScripts ? ["FALSE FIX DETECTED: no source files changed."] : [];
85
- }
86
-
87
- export function checkWriteFailures(data: StopData): string[] {
88
- if (!getFeatureFlag("STOP_GATE", true)) {
89
- return [];
90
- }
91
- const entries = Array.isArray(data?._stop_ctx?.recent_entries) ? data._stop_ctx.recent_entries : [];
92
- const failed = entries.find((entry: any) => (entry.tool === "Write" || entry.tool === "Edit") && entry.success === false);
93
- return failed ? [`WRITE/EDIT FAILURE DETECTED: ${failed.file}`] : [];
94
- }
95
-
96
- export function checkSimplifier(data: StopData): string[] {
97
- const entries = Array.isArray(data?._stop_ctx?.source_write_entries) ? data._stop_ctx.source_write_entries : [];
98
- for (const entry of entries) {
99
- const path = String(entry.file || "");
100
- if (!path || !existsSync(path)) {
101
- continue;
102
- }
103
- const content = readFileSync(path, "utf8");
104
- const lines = content.split(/\r?\n/).filter(Boolean);
105
- if (lines.length === 0) {
106
- continue;
107
- }
108
- const commentLines = lines.filter((line) => line.trim().startsWith("//") || line.trim().startsWith("#")).length;
109
- if (commentLines / lines.length > 0.4) {
110
- process.stderr.write(`@simplifier advisory: ${commentLines} comment lines in ${path}\n`);
111
- }
112
- }
113
- return [];
114
- }
115
-
116
- export function runStopDispatcher(data: StopData, projectDir = resolveProjectDir()): string[] {
117
- return [
118
- ...checkVerification(data),
119
- ...checkDiffBudget(data, projectDir),
120
- ...checkRecentFailures(data),
121
- ...checkTestExecution(data),
122
- ...checkTestValidatorCoverage(data),
123
- ...checkFalseFix(data),
124
- ...checkWriteFailures(data),
125
- ...checkSimplifier(data)
126
- ];
127
- }
128
-
129
- async function main() {
130
- const data = await readJsonFromStdin<StopData>({});
131
- if (data.stop_hook_active) {
132
- return;
133
- }
134
- const projectDir = resolveProjectDir();
135
- const blocks = runStopDispatcher(data, projectDir);
136
- if (blocks.length > 0) {
137
- process.stdout.write(`${JSON.stringify({ status: "blocked", blocks }, null, 2)}\n`);
138
- }
139
- }
140
-
141
- if (import.meta.main) {
142
- try {
143
- await main();
144
- } catch {
145
- process.exit(0);
146
- }
147
- }
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { noopHookMain } from "./_common.ts";
3
-
4
- noopHookMain();
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { appendFileSync } from "node:fs";
3
- import { ledgerPath, readJsonFromStdin, resolveProjectDir, ensureParent } from "./_common.ts";
4
-
5
- async function main() {
6
- const payload = await readJsonFromStdin<any>({});
7
- const projectDir = resolveProjectDir();
8
- const path = ledgerPath(projectDir, "tool-ledger.jsonl");
9
- const record = {
10
- ts: new Date().toISOString(),
11
- tool: payload.tool_name || "",
12
- file: payload.tool_input?.file_path || payload.tool_input?.filePath || "",
13
- command: payload.tool_input?.command || "",
14
- success: payload.tool_response?.success,
15
- exit_code: payload.tool_response?.exitCode ?? payload.tool_response?.exit_code
16
- };
17
- ensureParent(path);
18
- appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
19
- }
20
-
21
- if (import.meta.main) {
22
- try {
23
- await main();
24
- } catch {
25
- process.exit(0);
26
- }
27
- }