@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
package/lab/policies.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Policy checks for OMG lab train/eval pipeline."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
ALLOWED_LICENSES = {"apache-2.0", "mit", "bsd-3-clause", "cc-by-4.0"}
|
|
8
|
+
BLOCKED_SOURCE_TOKENS = {"unknown", "leaked", "stolen", "unauthorized", "pirated"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def validate_dataset_source(dataset: dict[str, Any]) -> tuple[bool, str]:
|
|
12
|
+
license_name = str(dataset.get("license", "")).lower()
|
|
13
|
+
source = str(dataset.get("source", "")).lower()
|
|
14
|
+
|
|
15
|
+
if not license_name:
|
|
16
|
+
return False, "dataset license missing"
|
|
17
|
+
if license_name not in ALLOWED_LICENSES:
|
|
18
|
+
return False, f"dataset license not allowed: {license_name}"
|
|
19
|
+
if any(token in source for token in BLOCKED_SOURCE_TOKENS):
|
|
20
|
+
return False, "dataset source violates policy"
|
|
21
|
+
return True, "ok"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def validate_model_source(model: dict[str, Any]) -> tuple[bool, str]:
|
|
25
|
+
source = str(model.get("source", "")).lower()
|
|
26
|
+
allow_distill = bool(model.get("allow_distill", False))
|
|
27
|
+
|
|
28
|
+
if any(token in source for token in BLOCKED_SOURCE_TOKENS):
|
|
29
|
+
return False, "model source violates policy"
|
|
30
|
+
if not allow_distill:
|
|
31
|
+
return False, "model source disallows distillation"
|
|
32
|
+
return True, "ok"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def validate_job_request(job: dict[str, Any]) -> tuple[bool, str]:
|
|
36
|
+
dataset = job.get("dataset")
|
|
37
|
+
model = job.get("base_model")
|
|
38
|
+
|
|
39
|
+
if not isinstance(dataset, dict):
|
|
40
|
+
return False, "dataset block missing"
|
|
41
|
+
if not isinstance(model, dict):
|
|
42
|
+
return False, "base_model block missing"
|
|
43
|
+
|
|
44
|
+
ok, reason = validate_dataset_source(dataset)
|
|
45
|
+
if not ok:
|
|
46
|
+
return False, reason
|
|
47
|
+
|
|
48
|
+
ok, reason = validate_model_source(model)
|
|
49
|
+
if not ok:
|
|
50
|
+
return False, reason
|
|
51
|
+
|
|
52
|
+
return True, "ok"
|
package/package.json
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trac3er/oh-my-god",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "OMG (Oh My God) — Multi-agent orchestration, intelligent model routing, and durable session state for Claude Code",
|
|
5
|
-
"
|
|
6
|
-
"omg": "scripts/omg.ts"
|
|
7
|
-
},
|
|
8
|
-
"main": "scripts/omg.ts",
|
|
5
|
+
"main": "OMG-setup.sh",
|
|
9
6
|
"scripts": {
|
|
10
|
-
"postinstall": "
|
|
11
|
-
"update": "
|
|
12
|
-
"uninstall": "
|
|
13
|
-
"test": "
|
|
14
|
-
"typecheck": "bunx tsc --noEmit",
|
|
15
|
-
"check:runtime-clean": "bun scripts/check-runtime-clean.ts"
|
|
7
|
+
"postinstall": "./OMG-setup.sh install --non-interactive",
|
|
8
|
+
"update": "./OMG-setup.sh update",
|
|
9
|
+
"uninstall": "./OMG-setup.sh uninstall",
|
|
10
|
+
"test": "python3 -m pytest tests/ -x -q"
|
|
16
11
|
},
|
|
17
12
|
"keywords": [
|
|
18
13
|
"claude",
|
|
@@ -35,12 +30,6 @@
|
|
|
35
30
|
},
|
|
36
31
|
"homepage": "https://github.com/trac3er00/OMG#readme",
|
|
37
32
|
"engines": {
|
|
38
|
-
"
|
|
39
|
-
},
|
|
40
|
-
"packageManager": "bun@1.3.9",
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/node": "^25.3.5",
|
|
43
|
-
"bun-types": "^1.3.10",
|
|
44
|
-
"typescript": "^5.9.2"
|
|
33
|
+
"node": ">=14"
|
|
45
34
|
}
|
|
46
35
|
}
|
package/plugins/README.md
CHANGED
|
@@ -1,82 +1,54 @@
|
|
|
1
1
|
# OMG Commands
|
|
2
2
|
|
|
3
|
-
OMG
|
|
3
|
+
OMG exposes a small native front door and keeps the rest of the surface available as advanced plugins.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Native Entry Points
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
| Command | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `/OMG:setup` | Native setup and adoption flow for supported hosts |
|
|
10
|
+
| `/OMG:crazy` | Default OMG orchestration flow |
|
|
11
|
+
|
|
12
|
+
## Core Commands
|
|
8
13
|
|
|
9
14
|
| Command | Description |
|
|
10
15
|
|---------|-------------|
|
|
11
16
|
| `/OMG:init` | Initialize project or domain |
|
|
12
|
-
| `/OMG:escalate` | Route to Codex
|
|
13
|
-
| `/OMG:teams` | Team routing
|
|
14
|
-
| `/OMG:ccg` | Tri-track synthesis
|
|
15
|
-
| `/OMG:
|
|
16
|
-
| `/OMG:
|
|
17
|
-
| `/OMG:
|
|
18
|
-
| `/OMG:mode` | Set cognitive mode |
|
|
19
|
-
|
|
20
|
-
## Advanced Commands (`plugins/advanced/`)
|
|
17
|
+
| `/OMG:escalate` | Route to Codex, Gemini, or CCG |
|
|
18
|
+
| `/OMG:teams` | Team routing for internal OMG execution |
|
|
19
|
+
| `/OMG:ccg` | Tri-track synthesis |
|
|
20
|
+
| `/OMG:compat` | Legacy compatibility routing |
|
|
21
|
+
| `/OMG:health-check` | Verify setup and tool integration |
|
|
22
|
+
| `/OMG:mode` | Set cognitive mode for the session |
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
## Advanced Commands
|
|
23
25
|
|
|
24
26
|
| Command | Category | Description |
|
|
25
27
|
|---------|----------|-------------|
|
|
26
28
|
| `/OMG:deep-plan` | Planning | Strategic planning with domain awareness |
|
|
27
|
-
| `/OMG:learn` | Knowledge |
|
|
28
|
-
| `/OMG:code-review` | Quality | Deep
|
|
29
|
-
| `/OMG:security-review` | Security | Security
|
|
30
|
-
| `/OMG:ship` | Delivery |
|
|
31
|
-
| `/OMG:handoff` | Collaboration | Session transfer |
|
|
32
|
-
| `/OMG:maintainer` | OSS | Open-source maintainer tools |
|
|
33
|
-
| `/OMG:sequential-thinking` | Thinking | Structured reasoning |
|
|
34
|
-
| `/OMG:ralph-start` | Automation | Start Ralph loop |
|
|
35
|
-
| `/OMG:ralph-stop` | Automation | Stop Ralph loop |
|
|
29
|
+
| `/OMG:learn` | Knowledge | Convert patterns into OMG-native instincts and skills |
|
|
30
|
+
| `/OMG:code-review` | Quality | Deep review flow |
|
|
31
|
+
| `/OMG:security-review` | Security | Security-focused review |
|
|
32
|
+
| `/OMG:ship` | Delivery | Idea to evidence to release |
|
|
33
|
+
| `/OMG:handoff` | Collaboration | Session transfer and continuity |
|
|
36
34
|
|
|
37
|
-
## Plugin
|
|
35
|
+
## Plugin Layout
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```
|
|
37
|
+
```text
|
|
42
38
|
plugins/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Using Advanced Commands
|
|
52
|
-
|
|
53
|
-
Advanced commands work the same as core commands:
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
/OMG:deep-plan implement OAuth2 flow
|
|
57
|
-
/OMG:learn from this session
|
|
58
|
-
/OMG:code-review src/auth.ts
|
|
39
|
+
core/
|
|
40
|
+
commands/
|
|
41
|
+
plugin.json
|
|
42
|
+
advanced/
|
|
43
|
+
commands/
|
|
44
|
+
plugin.json
|
|
59
45
|
```
|
|
60
46
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
1. Create `plugins/my-plugin/plugin.json`
|
|
64
|
-
2. Add commands to `plugins/my-plugin/commands/`
|
|
65
|
-
3. OMG auto-discovers plugins on startup
|
|
66
|
-
|
|
67
|
-
See `plugins/advanced/` for examples.
|
|
68
|
-
|
|
69
|
-
## Migration from Legacy Plugins
|
|
47
|
+
## Adoption Notes
|
|
70
48
|
|
|
71
|
-
|
|
49
|
+
Public migration commands are intentionally avoided. OMG uses `/OMG:setup` and `OMG-setup.sh` to detect and adopt older ecosystems internally, while `compat` remains focused on legacy skill routing.
|
|
72
50
|
|
|
73
|
-
|
|
74
|
-
|-------------|--------------|
|
|
75
|
-
| `writing-plans` | `/OMG:deep-plan` |
|
|
76
|
-
| `learner` | `/OMG:learn` |
|
|
77
|
-
| `requesting-code-review` | `/OMG:code-review` |
|
|
78
|
-
| `security-review` | `/OMG:security-review` |
|
|
79
|
-
| `finishing-a-development-branch` | `/OMG:ship` |
|
|
80
|
-
| `handoff` | `/OMG:handoff` |
|
|
51
|
+
## Public Docs
|
|
81
52
|
|
|
82
|
-
|
|
53
|
+
- Install guides live in [docs/install/claude-code.md](../docs/install/claude-code.md), [docs/install/codex.md](../docs/install/codex.md), and [docs/install/opencode.md](../docs/install/opencode.md).
|
|
54
|
+
- Proof surface lives in [docs/proof.md](../docs/proof.md).
|
|
@@ -37,10 +37,10 @@ Examples of GOOD questions:
|
|
|
37
37
|
Read the codebase to understand the CURRENT state:
|
|
38
38
|
```bash
|
|
39
39
|
# Directory structure
|
|
40
|
-
find . -type f
|
|
40
|
+
find . -type f -name "*.ts" -o -name "*.py" -o -name "*.go" | head -50
|
|
41
41
|
|
|
42
42
|
# Key architectural patterns
|
|
43
|
-
grep -rn "export class\|export function\|
|
|
43
|
+
grep -rn "export class\|export function\|def \|func \|struct " src/ --include="*.{ts,py,go}" | head -30
|
|
44
44
|
|
|
45
45
|
# Existing domain boundaries
|
|
46
46
|
ls -la src/*/ # or app/*/ or packages/*/
|
|
@@ -140,7 +140,7 @@ After validation, launch exactly 5 planning tracks with mixed-model intent using
|
|
|
140
140
|
|
|
141
141
|
Dispatch pattern is mandatory: all 5 tracks launch in parallel as background sub-agents.
|
|
142
142
|
|
|
143
|
-
```
|
|
143
|
+
```python
|
|
144
144
|
task(subagent_type="explore", run_in_background=true, load_skills=[], description="Architect planning track", prompt="...")
|
|
145
145
|
task(subagent_type="explore", run_in_background=true, load_skills=[], description="Backend planning track", prompt="...")
|
|
146
146
|
task(subagent_type="explore", run_in_background=true, load_skills=[], description="Frontend planning track", prompt="...")
|
|
@@ -99,7 +99,7 @@ When detecting patterns, especially look for:
|
|
|
99
99
|
## Aggregated Patterns (Auto)
|
|
100
100
|
When you run `/OMG:learn auto`, OMG reads all learning files from `.omg/state/learnings/` and generates `.omg/knowledge/critical-patterns.md` with your top tool and file patterns.
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Run: `python3 -c "import sys; sys.path.insert(0,'hooks'); from _learnings import save_critical_patterns; save_critical_patterns('.')"`
|
|
103
103
|
## File Write Fallback
|
|
104
104
|
If `Write` fails (file exists), use `Edit` or Bash heredoc:
|
|
105
105
|
```bash
|
|
@@ -16,13 +16,13 @@ Determine what to scan:
|
|
|
16
16
|
Identify security-critical files automatically:
|
|
17
17
|
```bash
|
|
18
18
|
# Auth/session
|
|
19
|
-
find . -type f \( -name "*.ts" -o -name "*.
|
|
19
|
+
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" -o -name "*.go" -o -name "*.rs" \) | xargs grep -li "auth\|login\|session\|token\|password\|jwt\|oauth" 2>/dev/null
|
|
20
20
|
|
|
21
21
|
# Payment
|
|
22
|
-
find . -type f
|
|
22
|
+
find . -type f -name "*.{ts,js,py,go}" | xargs grep -li "payment\|billing\|stripe\|checkout\|card\|price" 2>/dev/null
|
|
23
23
|
|
|
24
24
|
# Database
|
|
25
|
-
find . -type f
|
|
25
|
+
find . -type f -name "*.{ts,js,py,go}" | xargs grep -li "query\|SELECT\|INSERT\|UPDATE\|DELETE\|migration\|schema" 2>/dev/null
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Step 2: Automated Vulnerability Scan
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Ship pipeline — Idea -> Plan -> Execute -> Evidence -> PR-ready summary
|
|
3
|
-
allowed-tools: Read, Write, Edit, MultiEdit, Grep, Glob, Bash(git:*), Bash(rg:*), Bash(find:*), Bash(cat:*), Bash(
|
|
3
|
+
allowed-tools: Read, Write, Edit, MultiEdit, Grep, Glob, Bash(git:*), Bash(rg:*), Bash(find:*), Bash(cat:*), Bash(python3:*), Bash(pytest:*), Bash(npm test:*), Bash(go test:*), Bash(cargo test:*), Bash(jest:*), Bash(vitest:*)
|
|
4
4
|
argument-hint: "[goal or optional path to .omg/idea.yml]"
|
|
5
5
|
---
|
|
6
6
|
|
package/plugins/core/plugin.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omg-core",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Core OMG commands -
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "Core OMG commands - native setup, routing, orchestration, and verification surfaces",
|
|
5
5
|
"type": "omg-plugin",
|
|
6
6
|
"commands": {
|
|
7
|
+
"setup": {
|
|
8
|
+
"path": "commands/OMG:setup.md",
|
|
9
|
+
"description": "Native OMG setup and adoption flow for supported hosts",
|
|
10
|
+
"category": "setup"
|
|
11
|
+
},
|
|
7
12
|
"init": {
|
|
8
13
|
"path": "commands/OMG:init.md",
|
|
9
14
|
"description": "Unified initializer for project setup and domain scaffolding",
|
|
@@ -81,7 +86,7 @@
|
|
|
81
86
|
"categories": {
|
|
82
87
|
"setup": {
|
|
83
88
|
"description": "Project initialization and setup",
|
|
84
|
-
"commands": ["init", "health-check"]
|
|
89
|
+
"commands": ["setup", "init", "health-check"]
|
|
85
90
|
},
|
|
86
91
|
"routing": {
|
|
87
92
|
"description": "Model routing and escalation",
|
|
File without changes
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime, timedelta, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
import urllib.error
|
|
9
|
+
import urllib.request
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch"
|
|
13
|
+
CACHE_REL_PATH = Path(".omg") / "state" / "cve-cache.json"
|
|
14
|
+
CACHE_TTL_HOURS = 24
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _dep_health_enabled() -> bool:
|
|
18
|
+
env_val = os.environ.get("OMG_DEP_HEALTH_ENABLED", "").lower()
|
|
19
|
+
if env_val in ("1", "true", "yes"):
|
|
20
|
+
return True
|
|
21
|
+
if env_val in ("0", "false", "no"):
|
|
22
|
+
return False
|
|
23
|
+
try:
|
|
24
|
+
import sys
|
|
25
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
26
|
+
from hooks._common import get_feature_flag
|
|
27
|
+
return get_feature_flag("DEP_HEALTH", default=False)
|
|
28
|
+
except Exception:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def scan_for_cves(dependency_list: list[dict[str, str]], project_dir: str = ".") -> dict[str, Any]:
|
|
33
|
+
if not _dep_health_enabled():
|
|
34
|
+
return {"results": {}, "cached": False, "scan_ts": datetime.now(timezone.utc).isoformat()}
|
|
35
|
+
|
|
36
|
+
now = datetime.now(timezone.utc)
|
|
37
|
+
cache_path = Path(project_dir) / CACHE_REL_PATH
|
|
38
|
+
cached_payload = _load_cache(cache_path)
|
|
39
|
+
|
|
40
|
+
if not dependency_list:
|
|
41
|
+
return {
|
|
42
|
+
"results": {},
|
|
43
|
+
"cached": False,
|
|
44
|
+
"scan_ts": now.isoformat(),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if cached_payload and _is_cache_fresh(cached_payload.get("scan_ts")):
|
|
48
|
+
return {
|
|
49
|
+
"results": cached_payload.get("results", {}),
|
|
50
|
+
"cached": True,
|
|
51
|
+
"scan_ts": cached_payload.get("scan_ts", now.isoformat()),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
osv_response = _query_osv_batch(dependency_list)
|
|
56
|
+
except urllib.error.URLError:
|
|
57
|
+
if cached_payload:
|
|
58
|
+
return {
|
|
59
|
+
"results": cached_payload.get("results", {}),
|
|
60
|
+
"cached": True,
|
|
61
|
+
"scan_ts": cached_payload.get("scan_ts", now.isoformat()),
|
|
62
|
+
}
|
|
63
|
+
return {"status": "offline", "results": {}, "cached": False}
|
|
64
|
+
|
|
65
|
+
structured_results = _normalize_results(dependency_list, osv_response)
|
|
66
|
+
scan_result = {
|
|
67
|
+
"results": structured_results,
|
|
68
|
+
"cached": False,
|
|
69
|
+
"scan_ts": now.isoformat(),
|
|
70
|
+
}
|
|
71
|
+
_save_cache(cache_path, scan_result)
|
|
72
|
+
return scan_result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _query_osv_batch(dependency_list: list[dict[str, str]]) -> dict[str, Any]:
|
|
76
|
+
payload = {
|
|
77
|
+
"queries": [
|
|
78
|
+
{
|
|
79
|
+
"package": {
|
|
80
|
+
"name": dependency["name"],
|
|
81
|
+
"ecosystem": dependency["ecosystem"],
|
|
82
|
+
},
|
|
83
|
+
"version": dependency["version"],
|
|
84
|
+
}
|
|
85
|
+
for dependency in dependency_list
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
request = urllib.request.Request(
|
|
90
|
+
OSV_BATCH_URL,
|
|
91
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
92
|
+
headers={"Content-Type": "application/json"},
|
|
93
|
+
method="POST",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
with urllib.request.urlopen(request) as response:
|
|
97
|
+
body = response.read().decode("utf-8")
|
|
98
|
+
return json.loads(body)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _normalize_results(
|
|
102
|
+
dependency_list: list[dict[str, str]], osv_response: dict[str, Any]
|
|
103
|
+
) -> dict[str, list[dict[str, Any]]]:
|
|
104
|
+
output: dict[str, list[dict[str, Any]]] = {}
|
|
105
|
+
response_results = osv_response.get("results", [])
|
|
106
|
+
|
|
107
|
+
for index, dependency in enumerate(dependency_list):
|
|
108
|
+
pkg_name = dependency.get("name", "")
|
|
109
|
+
query_result = response_results[index] if index < len(response_results) else {}
|
|
110
|
+
vulns = query_result.get("vulns", [])
|
|
111
|
+
|
|
112
|
+
normalized_vulns: list[dict[str, Any]] = []
|
|
113
|
+
for vuln in vulns:
|
|
114
|
+
affected_versions, fixed_version = _extract_affected(vuln)
|
|
115
|
+
normalized_vulns.append(
|
|
116
|
+
{
|
|
117
|
+
"id": vuln.get("id", ""),
|
|
118
|
+
"severity": _extract_severity(vuln),
|
|
119
|
+
"summary": vuln.get("summary", ""),
|
|
120
|
+
"affected_versions": affected_versions,
|
|
121
|
+
"fixed_version": fixed_version,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
output[pkg_name] = normalized_vulns
|
|
126
|
+
|
|
127
|
+
return output
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _extract_severity(vuln: dict[str, Any]) -> str:
|
|
131
|
+
severities = vuln.get("severity") or []
|
|
132
|
+
if severities and isinstance(severities[0], dict):
|
|
133
|
+
score = severities[0].get("score")
|
|
134
|
+
if score:
|
|
135
|
+
return str(score)
|
|
136
|
+
return "UNKNOWN"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _extract_affected(vuln: dict[str, Any]) -> tuple[list[str], str]:
|
|
140
|
+
affected_versions: list[str] = []
|
|
141
|
+
fixed_version = ""
|
|
142
|
+
|
|
143
|
+
for affected in vuln.get("affected", []) or []:
|
|
144
|
+
for version in affected.get("versions", []) or []:
|
|
145
|
+
affected_versions.append(str(version))
|
|
146
|
+
|
|
147
|
+
for version_range in affected.get("ranges", []) or []:
|
|
148
|
+
for event in version_range.get("events", []) or []:
|
|
149
|
+
introduced = event.get("introduced")
|
|
150
|
+
fixed = event.get("fixed")
|
|
151
|
+
if introduced:
|
|
152
|
+
affected_versions.append(str(introduced))
|
|
153
|
+
if fixed and not fixed_version:
|
|
154
|
+
fixed_version = str(fixed)
|
|
155
|
+
|
|
156
|
+
deduped_versions = list(dict.fromkeys(affected_versions))
|
|
157
|
+
return deduped_versions, fixed_version
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _load_cache(cache_path: Path) -> dict[str, Any] | None:
|
|
161
|
+
if not cache_path.exists():
|
|
162
|
+
return None
|
|
163
|
+
try:
|
|
164
|
+
return json.loads(cache_path.read_text(encoding="utf-8"))
|
|
165
|
+
except (OSError, json.JSONDecodeError):
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _save_cache(cache_path: Path, payload: dict[str, Any]) -> None:
|
|
170
|
+
try:
|
|
171
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
172
|
+
cache_path.write_text(json.dumps(payload, separators=(",", ":")), encoding="utf-8")
|
|
173
|
+
except OSError:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _is_cache_fresh(scan_ts: str | None) -> bool:
|
|
178
|
+
if not scan_ts:
|
|
179
|
+
return False
|
|
180
|
+
try:
|
|
181
|
+
scanned_at = datetime.fromisoformat(scan_ts)
|
|
182
|
+
except ValueError:
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
if scanned_at.tzinfo is None:
|
|
186
|
+
scanned_at = scanned_at.replace(tzinfo=timezone.utc)
|
|
187
|
+
|
|
188
|
+
return datetime.now(timezone.utc) - scanned_at < timedelta(hours=CACHE_TTL_HOURS)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""License compatibility checker for dependency health analysis.
|
|
2
|
+
|
|
3
|
+
Checks whether project dependencies have licenses compatible with
|
|
4
|
+
the project's own license using a static compatibility matrix.
|
|
5
|
+
No network access required.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
# License categories (restrictiveness increases top to bottom)
|
|
13
|
+
_PERMISSIVE = frozenset({
|
|
14
|
+
"MIT", "BSD-2-Clause", "BSD-3-Clause", "ISC", "Unlicense", "CC0-1.0",
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
_WEAK_COPYLEFT = frozenset({
|
|
18
|
+
"Apache-2.0", "LGPL-2.1", "LGPL-3.0", "MPL-2.0",
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
_STRONG_COPYLEFT = frozenset({
|
|
22
|
+
"GPL-2.0", "GPL-3.0",
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
_NETWORK_COPYLEFT = frozenset({
|
|
26
|
+
"AGPL-3.0",
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
_ALL_KNOWN = _PERMISSIVE | _WEAK_COPYLEFT | _STRONG_COPYLEFT | _NETWORK_COPYLEFT
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _dep_health_enabled() -> bool:
|
|
33
|
+
env_val = os.environ.get("OMG_DEP_HEALTH_ENABLED", "").lower()
|
|
34
|
+
if env_val in ("1", "true", "yes"):
|
|
35
|
+
return True
|
|
36
|
+
if env_val in ("0", "false", "no"):
|
|
37
|
+
return False
|
|
38
|
+
try:
|
|
39
|
+
import sys
|
|
40
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
41
|
+
from hooks._common import get_feature_flag
|
|
42
|
+
return get_feature_flag("DEP_HEALTH", default=False)
|
|
43
|
+
except Exception:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _license_tier(license_id: str) -> int:
|
|
48
|
+
"""Return restrictiveness tier: 0=permissive, 1=weak, 2=strong, 3=network."""
|
|
49
|
+
if license_id in _PERMISSIVE:
|
|
50
|
+
return 0
|
|
51
|
+
if license_id in _WEAK_COPYLEFT:
|
|
52
|
+
return 1
|
|
53
|
+
if license_id in _STRONG_COPYLEFT:
|
|
54
|
+
return 2
|
|
55
|
+
if license_id in _NETWORK_COPYLEFT:
|
|
56
|
+
return 3
|
|
57
|
+
return -1 # unknown
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def check_license_compatibility(
|
|
61
|
+
project_license: str,
|
|
62
|
+
dependencies: list[dict[str, Any]],
|
|
63
|
+
) -> dict[str, list[dict[str, str]]]:
|
|
64
|
+
"""Check license compatibility between project and its dependencies.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
project_license: SPDX identifier for the project license (e.g. "MIT").
|
|
68
|
+
dependencies: List of dicts with "name" and "license" keys.
|
|
69
|
+
License may be None or "UNKNOWN".
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Dict with three lists:
|
|
73
|
+
compatible: [{"pkg": str, "license": str}]
|
|
74
|
+
incompatible: [{"pkg": str, "license": str, "reason": str}]
|
|
75
|
+
unknown: [{"pkg": str}]
|
|
76
|
+
"""
|
|
77
|
+
if not _dep_health_enabled():
|
|
78
|
+
return {"compatible": [], "incompatible": [], "unknown": []}
|
|
79
|
+
|
|
80
|
+
compatible: list[dict[str, str]] = []
|
|
81
|
+
incompatible: list[dict[str, str]] = []
|
|
82
|
+
unknown: list[dict[str, str]] = []
|
|
83
|
+
|
|
84
|
+
project_tier = _license_tier(project_license)
|
|
85
|
+
|
|
86
|
+
for dep in dependencies:
|
|
87
|
+
name = dep.get("name", "")
|
|
88
|
+
dep_license = dep.get("license")
|
|
89
|
+
|
|
90
|
+
# Unknown / missing license
|
|
91
|
+
if not dep_license or dep_license == "UNKNOWN":
|
|
92
|
+
unknown.append({"pkg": name})
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
dep_tier = _license_tier(dep_license)
|
|
96
|
+
|
|
97
|
+
# Unrecognized license string
|
|
98
|
+
if dep_tier == -1:
|
|
99
|
+
unknown.append({"pkg": name})
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
# AGPL dep in non-AGPL project is always incompatible
|
|
103
|
+
if dep_tier == 3 and project_tier != 3:
|
|
104
|
+
incompatible.append({
|
|
105
|
+
"pkg": name,
|
|
106
|
+
"license": dep_license,
|
|
107
|
+
"reason": (
|
|
108
|
+
f"AGPL-3.0 dependency in {project_license} project: "
|
|
109
|
+
f"network copyleft requires entire project to be AGPL-3.0"
|
|
110
|
+
),
|
|
111
|
+
})
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
# Strong copyleft dep in permissive/weak-copyleft project
|
|
115
|
+
if dep_tier == 2 and project_tier < 2:
|
|
116
|
+
incompatible.append({
|
|
117
|
+
"pkg": name,
|
|
118
|
+
"license": dep_license,
|
|
119
|
+
"reason": (
|
|
120
|
+
f"{dep_license} dependency in {project_license} project: "
|
|
121
|
+
f"copyleft contamination requires project to adopt {dep_license}"
|
|
122
|
+
),
|
|
123
|
+
})
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
# Everything else: permissive deps are always OK,
|
|
127
|
+
# weak copyleft in permissive is OK (dynamic linking),
|
|
128
|
+
# same-tier or higher-tier project can use lower-tier deps
|
|
129
|
+
compatible.append({"pkg": name, "license": dep_license})
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
"compatible": compatible,
|
|
133
|
+
"incompatible": incompatible,
|
|
134
|
+
"unknown": unknown,
|
|
135
|
+
}
|