@trac3er/oh-my-god 1.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 (229) hide show
  1. package/.claude-plugin/marketplace.json +36 -0
  2. package/.claude-plugin/plugin.json +23 -0
  3. package/.claude-plugin/scripts/install.sh +49 -0
  4. package/.claude-plugin/scripts/uninstall.sh +80 -0
  5. package/.claude-plugin/scripts/update.sh +84 -0
  6. package/.mcp.json +20 -0
  7. package/LICENSE +21 -0
  8. package/OMG-setup.sh +1093 -0
  9. package/README.md +335 -0
  10. package/THIRD_PARTY_NOTICES.md +24 -0
  11. package/UPSTREAM_DIFF.md +20 -0
  12. package/agents/__init__.py +1 -0
  13. package/agents/_model_roles.yaml +26 -0
  14. package/agents/designer.md +67 -0
  15. package/agents/explore.md +60 -0
  16. package/agents/model_roles.py +196 -0
  17. package/agents/omg-api-builder.md +23 -0
  18. package/agents/omg-architect-mode.md +43 -0
  19. package/agents/omg-architect.md +13 -0
  20. package/agents/omg-backend-engineer.md +43 -0
  21. package/agents/omg-critic.md +16 -0
  22. package/agents/omg-database-engineer.md +43 -0
  23. package/agents/omg-escalation-router.md +17 -0
  24. package/agents/omg-executor.md +12 -0
  25. package/agents/omg-frontend-designer.md +42 -0
  26. package/agents/omg-implement-mode.md +50 -0
  27. package/agents/omg-infra-engineer.md +43 -0
  28. package/agents/omg-qa-tester.md +16 -0
  29. package/agents/omg-research-mode.md +43 -0
  30. package/agents/omg-security-auditor.md +43 -0
  31. package/agents/omg-testing-engineer.md +43 -0
  32. package/agents/plan.md +80 -0
  33. package/agents/quick_task.md +64 -0
  34. package/agents/reviewer.md +83 -0
  35. package/agents/task.md +71 -0
  36. package/commands/OMG:ccg.md +22 -0
  37. package/commands/OMG:compat.md +57 -0
  38. package/commands/OMG:crazy.md +125 -0
  39. package/commands/OMG:domain-init.md +11 -0
  40. package/commands/OMG:escalate.md +52 -0
  41. package/commands/OMG:health-check.md +45 -0
  42. package/commands/OMG:init.md +134 -0
  43. package/commands/OMG:mode.md +44 -0
  44. package/commands/OMG:project-init.md +11 -0
  45. package/commands/OMG:ralph-start.md +43 -0
  46. package/commands/OMG:ralph-stop.md +23 -0
  47. package/commands/OMG:teams.md +39 -0
  48. package/commands/ai-commit.md +113 -0
  49. package/commands/ccg.md +9 -0
  50. package/commands/create-agent.md +183 -0
  51. package/commands/omc-teams.md +9 -0
  52. package/commands/session-branch.md +85 -0
  53. package/commands/session-fork.md +53 -0
  54. package/commands/session-merge.md +134 -0
  55. package/commands/theme.md +44 -0
  56. package/config/lsp_languages.yaml +324 -0
  57. package/config/themes/catppuccin-frappe.yaml +14 -0
  58. package/config/themes/catppuccin-latte.yaml +14 -0
  59. package/config/themes/catppuccin-macchiato.yaml +14 -0
  60. package/config/themes/catppuccin-mocha.yaml +14 -0
  61. package/config/themes/dracula.yaml +14 -0
  62. package/config/themes/gruvbox-dark.yaml +14 -0
  63. package/config/themes/nord.yaml +14 -0
  64. package/config/themes/one-dark.yaml +14 -0
  65. package/config/themes/solarized-dark.yaml +14 -0
  66. package/config/themes/tokyo-night.yaml +14 -0
  67. package/control_plane/__init__.py +2 -0
  68. package/control_plane/openapi.yaml +109 -0
  69. package/control_plane/server.py +107 -0
  70. package/control_plane/service.py +148 -0
  71. package/crates/omg-natives/Cargo.toml +17 -0
  72. package/crates/omg-natives/src/clipboard.rs +5 -0
  73. package/crates/omg-natives/src/glob.rs +15 -0
  74. package/crates/omg-natives/src/grep.rs +15 -0
  75. package/crates/omg-natives/src/highlight.rs +15 -0
  76. package/crates/omg-natives/src/html.rs +14 -0
  77. package/crates/omg-natives/src/image.rs +5 -0
  78. package/crates/omg-natives/src/keys.rs +5 -0
  79. package/crates/omg-natives/src/lib.rs +36 -0
  80. package/crates/omg-natives/src/prof.rs +5 -0
  81. package/crates/omg-natives/src/ps.rs +5 -0
  82. package/crates/omg-natives/src/shell.rs +5 -0
  83. package/crates/omg-natives/src/task.rs +5 -0
  84. package/crates/omg-natives/src/text.rs +14 -0
  85. package/hooks/_agent_registry.py +421 -0
  86. package/hooks/_budget.py +31 -0
  87. package/hooks/_common.py +476 -0
  88. package/hooks/_learnings.py +126 -0
  89. package/hooks/_memory.py +103 -0
  90. package/hooks/circuit-breaker.py +270 -0
  91. package/hooks/config-guard.py +163 -0
  92. package/hooks/context_pressure.py +53 -0
  93. package/hooks/credential_store.py +801 -0
  94. package/hooks/fetch-rate-limits.py +212 -0
  95. package/hooks/firewall.py +48 -0
  96. package/hooks/hashline-formatter-bridge.py +224 -0
  97. package/hooks/hashline-injector.py +273 -0
  98. package/hooks/hashline-validator.py +216 -0
  99. package/hooks/idle-detector.py +95 -0
  100. package/hooks/intentgate-keyword-detector.py +188 -0
  101. package/hooks/magic-keyword-router.py +195 -0
  102. package/hooks/policy_engine.py +310 -0
  103. package/hooks/post-tool-failure.py +19 -0
  104. package/hooks/post-write.py +199 -0
  105. package/hooks/pre-compact.py +204 -0
  106. package/hooks/pre-tool-inject.py +98 -0
  107. package/hooks/prompt-enhancer.py +672 -0
  108. package/hooks/quality-runner.py +191 -0
  109. package/hooks/secret-guard.py +47 -0
  110. package/hooks/session-end-capture.py +137 -0
  111. package/hooks/session-start.py +275 -0
  112. package/hooks/shadow_manager.py +297 -0
  113. package/hooks/state_migration.py +209 -0
  114. package/hooks/stop-gate.py +7 -0
  115. package/hooks/stop_dispatcher.py +929 -0
  116. package/hooks/test-validator.py +138 -0
  117. package/hooks/todo-state-tracker.py +114 -0
  118. package/hooks/tool-ledger.py +126 -0
  119. package/hooks/trust_review.py +524 -0
  120. package/install.sh +9 -0
  121. package/omg_natives/__init__.py +186 -0
  122. package/omg_natives/_bindings.py +165 -0
  123. package/omg_natives/clipboard.py +36 -0
  124. package/omg_natives/glob.py +42 -0
  125. package/omg_natives/grep.py +61 -0
  126. package/omg_natives/highlight.py +54 -0
  127. package/omg_natives/html.py +157 -0
  128. package/omg_natives/image.py +51 -0
  129. package/omg_natives/keys.py +46 -0
  130. package/omg_natives/prof.py +39 -0
  131. package/omg_natives/ps.py +93 -0
  132. package/omg_natives/shell.py +58 -0
  133. package/omg_natives/task.py +41 -0
  134. package/omg_natives/text.py +50 -0
  135. package/package.json +26 -0
  136. package/plugins/README.md +82 -0
  137. package/plugins/advanced/commands/OMG:code-review.md +114 -0
  138. package/plugins/advanced/commands/OMG:deep-plan.md +221 -0
  139. package/plugins/advanced/commands/OMG:handoff.md +115 -0
  140. package/plugins/advanced/commands/OMG:learn.md +110 -0
  141. package/plugins/advanced/commands/OMG:maintainer.md +31 -0
  142. package/plugins/advanced/commands/OMG:ralph-start.md +43 -0
  143. package/plugins/advanced/commands/OMG:ralph-stop.md +23 -0
  144. package/plugins/advanced/commands/OMG:security-review.md +119 -0
  145. package/plugins/advanced/commands/OMG:sequential-thinking.md +20 -0
  146. package/plugins/advanced/commands/OMG:ship.md +46 -0
  147. package/plugins/advanced/plugin.json +96 -0
  148. package/plugins/core/plugin.json +82 -0
  149. package/pytest.ini +5 -0
  150. package/registry/__init__.py +1 -0
  151. package/registry/verify_artifact.py +90 -0
  152. package/rules/contextual/architect-mode.md +9 -0
  153. package/rules/contextual/big-picture.md +20 -0
  154. package/rules/contextual/code-hygiene.md +26 -0
  155. package/rules/contextual/context-management.md +19 -0
  156. package/rules/contextual/context-minimization.md +32 -0
  157. package/rules/contextual/ddd-sdd.md +28 -0
  158. package/rules/contextual/dependency-safety.md +16 -0
  159. package/rules/contextual/doc-check.md +13 -0
  160. package/rules/contextual/implement-mode.md +9 -0
  161. package/rules/contextual/infra-safety.md +14 -0
  162. package/rules/contextual/outside-in.md +13 -0
  163. package/rules/contextual/persistent-mode.md +24 -0
  164. package/rules/contextual/research-mode.md +9 -0
  165. package/rules/contextual/security-domains.md +25 -0
  166. package/rules/contextual/vision-detection.md +27 -0
  167. package/rules/contextual/web-search.md +25 -0
  168. package/rules/contextual/write-verify.md +23 -0
  169. package/rules/core/00-truth.md +20 -0
  170. package/rules/core/01-surgical.md +19 -0
  171. package/rules/core/02-circuit-breaker.md +22 -0
  172. package/rules/core/03-ensemble.md +28 -0
  173. package/rules/core/04-testing.md +30 -0
  174. package/runtime/__init__.py +32 -0
  175. package/runtime/adapters/__init__.py +13 -0
  176. package/runtime/adapters/claude.py +60 -0
  177. package/runtime/adapters/gpt.py +53 -0
  178. package/runtime/adapters/local.py +53 -0
  179. package/runtime/business_workflow.py +220 -0
  180. package/runtime/compat.py +1299 -0
  181. package/runtime/custom_agent_loader.py +366 -0
  182. package/runtime/dispatcher.py +47 -0
  183. package/runtime/ecosystem.py +371 -0
  184. package/runtime/legacy_compat.py +7 -0
  185. package/runtime/omc_compat.py +7 -0
  186. package/runtime/omc_contract_snapshot.json +916 -0
  187. package/runtime/omg_compat_contract_snapshot.json +916 -0
  188. package/runtime/subagent_dispatcher.py +362 -0
  189. package/runtime/team_router.py +838 -0
  190. package/scripts/check-omc-contract-snapshot.py +12 -0
  191. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  192. package/scripts/check-omg-standalone-clean.py +102 -0
  193. package/scripts/legacy_to_omg_migrate.py +29 -0
  194. package/scripts/migrate-omc.py +464 -0
  195. package/scripts/omc_to_omg_migrate.py +12 -0
  196. package/scripts/omg.py +493 -0
  197. package/scripts/settings-merge.py +224 -0
  198. package/scripts/verify-no-omc.sh +5 -0
  199. package/scripts/verify-standalone.sh +21 -0
  200. package/templates/idea.yml +30 -0
  201. package/templates/policy.yaml +15 -0
  202. package/templates/profile.yaml +25 -0
  203. package/templates/runtime.yaml +12 -0
  204. package/templates/working-memory.md +17 -0
  205. package/tools/__init__.py +2 -0
  206. package/tools/browser_consent.py +289 -0
  207. package/tools/browser_stealth.py +481 -0
  208. package/tools/browser_tool.py +448 -0
  209. package/tools/changelog_generator.py +268 -0
  210. package/tools/commit_splitter.py +361 -0
  211. package/tools/config_discovery.py +151 -0
  212. package/tools/config_merger.py +449 -0
  213. package/tools/git_inspector.py +298 -0
  214. package/tools/lsp_client.py +275 -0
  215. package/tools/lsp_discovery.py +231 -0
  216. package/tools/lsp_operations.py +392 -0
  217. package/tools/python_repl.py +656 -0
  218. package/tools/python_sandbox.py +609 -0
  219. package/tools/search_providers/__init__.py +77 -0
  220. package/tools/search_providers/brave.py +115 -0
  221. package/tools/search_providers/exa.py +116 -0
  222. package/tools/search_providers/jina.py +104 -0
  223. package/tools/search_providers/perplexity.py +139 -0
  224. package/tools/search_providers/synthetic.py +74 -0
  225. package/tools/session_snapshot.py +736 -0
  226. package/tools/ssh_manager.py +912 -0
  227. package/tools/theme_engine.py +294 -0
  228. package/tools/theme_selector.py +137 -0
  229. package/tools/web_search.py +622 -0
@@ -0,0 +1,371 @@
1
+ """Optional upstream ecosystem sync and integration helpers for OMG."""
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime, timezone
5
+ import json
6
+ from pathlib import Path
7
+ import subprocess
8
+ from typing import Any
9
+
10
+ ECOSYSTEM_SCHEMA = "OmgEcosystemCatalog"
11
+ ECOSYSTEM_CATALOG_VERSION = "1.0.0"
12
+ ECOSYSTEM_LOCK_SCHEMA = "OmgEcosystemLock"
13
+ DEFAULT_ECOSYSTEM_REPO_DIR = ".omg/ecosystem/repos"
14
+ DEFAULT_ECOSYSTEM_LOCK_PATH = ".omg/state/ecosystem-lock.json"
15
+ DEFAULT_ECOSYSTEM_PLAYBOOK_DIR = ".omg/knowledge/ecosystem"
16
+ MAX_SELECTION = 32
17
+
18
+
19
+ ECOSYSTEM_REPOS: tuple[dict[str, Any], ...] = (
20
+ {
21
+ "name": "superpowers",
22
+ "aliases": ("superpowers",),
23
+ "repo": "https://github.com/obra/superpowers.git",
24
+ "ref": "main",
25
+ "route": "plan",
26
+ "category": "tdd",
27
+ "capabilities": ("tdd", "planning", "execution"),
28
+ "notes": "Primary source for strict red-green-refactor and plan execution discipline.",
29
+ },
30
+ {
31
+ "name": "ralph-wiggum",
32
+ "aliases": ("ralph-wiggum", "ralph wiggum", "ralph"),
33
+ "repo": "https://github.com/anthropics/claude-code.git",
34
+ "ref": "main",
35
+ "sparse_path": "plugins/ralph-wiggum",
36
+ "route": "runtime_ship",
37
+ "category": "persistent-loop",
38
+ "capabilities": ("persistent-mode", "completion-promises", "iteration"),
39
+ "notes": "Provides loop-style persistent execution patterns via stop-hook based iteration.",
40
+ },
41
+ {
42
+ "name": "claude-flow",
43
+ "aliases": ("claude-flow",),
44
+ "repo": "https://github.com/ruvnet/claude-flow.git",
45
+ "ref": "main",
46
+ "route": "ccg",
47
+ "category": "orchestration",
48
+ "capabilities": ("multi-agent", "coordination", "task-routing"),
49
+ "notes": "Informs CCG-style orchestrated task dispatch and multi-agent coordination.",
50
+ },
51
+ {
52
+ "name": "claude-mem",
53
+ "aliases": ("claude-mem",),
54
+ "repo": "https://github.com/thedotmack/claude-mem.git",
55
+ "ref": "main",
56
+ "route": "memory",
57
+ "category": "memory",
58
+ "capabilities": ("session-memory", "knowledge-capture", "recall"),
59
+ "notes": "Complements OMG knowledge/state artifacts with memory-centric workflows.",
60
+ },
61
+ {
62
+ "name": "memsearch",
63
+ "aliases": ("memsearch", "memory-search"),
64
+ "repo": "https://github.com/rjyo/memory-search.git",
65
+ "ref": "main",
66
+ "route": "memory",
67
+ "category": "memory-search",
68
+ "capabilities": ("semantic-search", "retrieval", "indexing"),
69
+ "notes": "Adds focused memory retrieval and search patterns for long-running sessions.",
70
+ },
71
+ {
72
+ "name": "beads",
73
+ "aliases": ("beads",),
74
+ "repo": "https://github.com/steveyegge/beads.git",
75
+ "ref": "main",
76
+ "route": "maintainer",
77
+ "category": "context-engineering",
78
+ "capabilities": ("context", "workflow", "agent-patterns"),
79
+ "notes": "Source of context-engineering and disciplined workflow patterns.",
80
+ },
81
+ {
82
+ "name": "planning-with-files",
83
+ "aliases": ("planning-with-files", "planning with files"),
84
+ "repo": "https://github.com/OthmanAdi/planning-with-files.git",
85
+ "ref": "master",
86
+ "route": "plan",
87
+ "category": "planning",
88
+ "capabilities": ("file-based-plans", "checklists", "handoff"),
89
+ "notes": "Reinforces file-native planning artifacts and execution checklists.",
90
+ },
91
+ {
92
+ "name": "hooks-mastery",
93
+ "aliases": ("hooks-mastery", "hooks mastery"),
94
+ "repo": "https://github.com/disler/claude-code-hooks-mastery.git",
95
+ "ref": "main",
96
+ "route": "health",
97
+ "category": "hooks",
98
+ "capabilities": ("hook-design", "hook-hardening", "hook-automation"),
99
+ "notes": "Hardening references for robust, low-noise hook behavior.",
100
+ },
101
+ {
102
+ "name": "compound-engineering",
103
+ "aliases": ("compound-engineering", "compounding-engineering"),
104
+ "repo": "https://github.com/EveryInc/compounding-engineering-plugin.git",
105
+ "ref": "main",
106
+ "route": "ccg",
107
+ "category": "compound-workflows",
108
+ "capabilities": ("iterative-improvement", "compound-results", "workflow-composition"),
109
+ "notes": "Compound engineering workflow patterns for iterative gains over multiple passes.",
110
+ },
111
+ )
112
+
113
+
114
+ def _now() -> str:
115
+ return datetime.now(timezone.utc).isoformat()
116
+
117
+
118
+ def _canonical(value: str) -> str:
119
+ return value.strip().lower().replace("_", "-").replace(" ", "-")
120
+
121
+
122
+ def _run_git(args: list[str], *, cwd: Path | None = None) -> str:
123
+ proc = subprocess.run(
124
+ ["git", *args],
125
+ cwd=str(cwd) if cwd else None,
126
+ capture_output=True,
127
+ text=True,
128
+ check=False,
129
+ )
130
+ if proc.returncode != 0:
131
+ stderr = proc.stderr.strip()
132
+ raise RuntimeError(f"git {' '.join(args)} failed: {stderr or 'unknown error'}")
133
+ return proc.stdout.strip()
134
+
135
+
136
+ def list_ecosystem_repos() -> list[dict[str, Any]]:
137
+ repos: list[dict[str, Any]] = []
138
+ for repo in ECOSYSTEM_REPOS:
139
+ cloned = dict(repo)
140
+ cloned["aliases"] = list(repo.get("aliases", ()))
141
+ cloned["capabilities"] = list(repo.get("capabilities", ()))
142
+ repos.append(cloned)
143
+ return repos
144
+
145
+
146
+ def resolve_ecosystem_repo(name: str) -> dict[str, Any] | None:
147
+ if not name.strip():
148
+ return None
149
+ wanted = _canonical(name)
150
+ for repo in ECOSYSTEM_REPOS:
151
+ if wanted == _canonical(str(repo["name"])):
152
+ return dict(repo)
153
+ for alias in repo.get("aliases", ()):
154
+ if wanted == _canonical(str(alias)):
155
+ return dict(repo)
156
+ return None
157
+
158
+
159
+ def resolve_ecosystem_selection(names: list[str] | None) -> tuple[list[dict[str, Any]], list[str]]:
160
+ if not names:
161
+ return list_ecosystem_repos(), []
162
+ selected: list[dict[str, Any]] = []
163
+ unknown: list[str] = []
164
+ seen: set[str] = set()
165
+ for raw in names[:MAX_SELECTION]:
166
+ repo = resolve_ecosystem_repo(raw)
167
+ if repo is None:
168
+ unknown.append(raw)
169
+ continue
170
+ key = str(repo["name"])
171
+ if key in seen:
172
+ continue
173
+ seen.add(key)
174
+ selected.append(repo)
175
+ return selected, unknown
176
+
177
+
178
+ def _read_lock(lock_path: Path) -> dict[str, Any]:
179
+ if not lock_path.exists():
180
+ return {}
181
+ try:
182
+ payload = json.loads(lock_path.read_text(encoding="utf-8"))
183
+ except Exception:
184
+ return {}
185
+ if not isinstance(payload, dict):
186
+ return {}
187
+ return payload
188
+
189
+
190
+ def _clone_or_update_repo(
191
+ *,
192
+ repo: dict[str, Any],
193
+ target: Path,
194
+ update: bool,
195
+ depth: int,
196
+ ) -> dict[str, Any]:
197
+ ref = str(repo.get("ref", "main"))
198
+ repo_url = str(repo["repo"])
199
+ sparse_path = str(repo.get("sparse_path", "")).strip()
200
+ action = "cached"
201
+
202
+ if not target.exists():
203
+ target.parent.mkdir(parents=True, exist_ok=True)
204
+ if sparse_path:
205
+ _run_git(["clone", "--depth", str(depth), "--filter=blob:none", "--sparse", repo_url, str(target)])
206
+ _run_git(["-C", str(target), "sparse-checkout", "set", sparse_path])
207
+ if ref and ref != "main":
208
+ _run_git(["-C", str(target), "checkout", ref])
209
+ else:
210
+ _run_git(
211
+ [
212
+ "clone",
213
+ "--depth",
214
+ str(depth),
215
+ "--filter=blob:none",
216
+ "--branch",
217
+ ref,
218
+ "--single-branch",
219
+ repo_url,
220
+ str(target),
221
+ ]
222
+ )
223
+ action = "cloned"
224
+ elif update:
225
+ _run_git(["-C", str(target), "fetch", "--depth", str(depth), "origin", ref])
226
+ _run_git(["-C", str(target), "checkout", "-B", ref, "FETCH_HEAD"])
227
+ action = "updated"
228
+
229
+ commit = _run_git(["-C", str(target), "rev-parse", "HEAD"])
230
+ branch = _run_git(["-C", str(target), "rev-parse", "--abbrev-ref", "HEAD"])
231
+ return {
232
+ "name": repo["name"],
233
+ "repo": repo_url,
234
+ "ref": ref,
235
+ "repo_segments": [".omg", "ecosystem", "repos", str(repo["name"])],
236
+ "action": action,
237
+ "commit": commit,
238
+ "branch": branch,
239
+ "sparse_path": sparse_path,
240
+ }
241
+
242
+
243
+ def _write_playbook(project_dir: Path, selected: list[dict[str, Any]]) -> list[str]:
244
+ base = project_dir / DEFAULT_ECOSYSTEM_PLAYBOOK_DIR
245
+ base.mkdir(parents=True, exist_ok=True)
246
+ written: list[str] = []
247
+ for repo in selected:
248
+ path = base / f"{repo['name']}.md"
249
+ capabilities = ", ".join(repo.get("capabilities", []))
250
+ content = (
251
+ f"# {repo['name']} Integration Notes\n\n"
252
+ f"- Route: `{repo.get('route', '')}`\n"
253
+ f"- Category: `{repo.get('category', '')}`\n"
254
+ f"- Capabilities: {capabilities}\n"
255
+ f"- Source: {repo.get('repo', '')}\n\n"
256
+ f"{repo.get('notes', '').strip()}\n"
257
+ )
258
+ path.write_text(content, encoding="utf-8")
259
+ written.append(str(path))
260
+ return written
261
+
262
+
263
+ def sync_ecosystem_repos(
264
+ *,
265
+ project_dir: str,
266
+ names: list[str] | None = None,
267
+ update: bool = False,
268
+ depth: int = 1,
269
+ ) -> dict[str, Any]:
270
+ root = Path(project_dir)
271
+ selected, unknown = resolve_ecosystem_selection(names)
272
+ repo_root = root / DEFAULT_ECOSYSTEM_REPO_DIR
273
+ lock_path = root / DEFAULT_ECOSYSTEM_LOCK_PATH
274
+ lock_path.parent.mkdir(parents=True, exist_ok=True)
275
+
276
+ entries: list[dict[str, Any]] = []
277
+ for repo in selected:
278
+ target = repo_root / str(repo["name"])
279
+ try:
280
+ synced = _clone_or_update_repo(repo=repo, target=target, update=update, depth=depth)
281
+ synced["status"] = "ok"
282
+ entries.append(synced)
283
+ except Exception as exc:
284
+ entries.append(
285
+ {
286
+ "name": repo["name"],
287
+ "repo": repo["repo"],
288
+ "ref": repo.get("ref", "main"),
289
+ "repo_segments": [".omg", "ecosystem", "repos", str(repo["name"])],
290
+ "status": "error",
291
+ "error": str(exc),
292
+ }
293
+ )
294
+
295
+ playbook_files = _write_playbook(root, selected)
296
+ previous = _read_lock(lock_path)
297
+ payload = {
298
+ "schema": ECOSYSTEM_LOCK_SCHEMA,
299
+ "catalog_schema": ECOSYSTEM_SCHEMA,
300
+ "catalog_version": ECOSYSTEM_CATALOG_VERSION,
301
+ "generated_at": _now(),
302
+ "selected_count": len(selected),
303
+ "unknown_count": len(unknown),
304
+ "selected": [repo["name"] for repo in selected],
305
+ "unknown": unknown,
306
+ "entries": entries,
307
+ "playbook_files": playbook_files,
308
+ "previous_generated_at": previous.get("generated_at", ""),
309
+ }
310
+ lock_path.write_text(json.dumps(payload, indent=2, ensure_ascii=True), encoding="utf-8")
311
+ return {
312
+ "status": "ok",
313
+ "schema": ECOSYSTEM_LOCK_SCHEMA,
314
+ "catalog_version": ECOSYSTEM_CATALOG_VERSION,
315
+ "lock_path": str(lock_path),
316
+ "repo_dir": str(repo_root),
317
+ "selected": payload["selected"],
318
+ "unknown": unknown,
319
+ "entries": entries,
320
+ "playbook_files": playbook_files,
321
+ }
322
+
323
+
324
+ def ecosystem_status(*, project_dir: str) -> dict[str, Any]:
325
+ root = Path(project_dir)
326
+ repo_root = root / DEFAULT_ECOSYSTEM_REPO_DIR
327
+ lock_path = root / DEFAULT_ECOSYSTEM_LOCK_PATH
328
+ lock = _read_lock(lock_path)
329
+
330
+ repos = list_ecosystem_repos()
331
+ statuses: list[dict[str, Any]] = []
332
+ for repo in repos:
333
+ target = repo_root / str(repo["name"])
334
+ if not target.exists():
335
+ statuses.append(
336
+ {
337
+ "name": repo["name"],
338
+ "installed": False,
339
+ "repo_segments": [".omg", "ecosystem", "repos", str(repo["name"])],
340
+ }
341
+ )
342
+ continue
343
+ commit = ""
344
+ branch = ""
345
+ error = ""
346
+ try:
347
+ commit = _run_git(["-C", str(target), "rev-parse", "HEAD"])
348
+ branch = _run_git(["-C", str(target), "rev-parse", "--abbrev-ref", "HEAD"])
349
+ except Exception as exc:
350
+ error = str(exc)
351
+ statuses.append(
352
+ {
353
+ "name": repo["name"],
354
+ "installed": True,
355
+ "repo_segments": [".omg", "ecosystem", "repos", str(repo["name"])],
356
+ "commit": commit,
357
+ "branch": branch,
358
+ "error": error,
359
+ }
360
+ )
361
+
362
+ return {
363
+ "status": "ok",
364
+ "schema": ECOSYSTEM_LOCK_SCHEMA,
365
+ "catalog_schema": ECOSYSTEM_SCHEMA,
366
+ "catalog_version": ECOSYSTEM_CATALOG_VERSION,
367
+ "lock_exists": lock_path.exists(),
368
+ "lock_generated_at": lock.get("generated_at", ""),
369
+ "repo_dir": str(repo_root),
370
+ "repos": statuses,
371
+ }
@@ -0,0 +1,7 @@
1
+ """Backward-compatible alias module.
2
+
3
+ Primary implementation lives in `runtime.compat`.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from .compat import * # noqa: F401,F403
@@ -0,0 +1,7 @@
1
+ """Backward-compatible aliases for legacy compatibility routing.
2
+
3
+ Primary implementation lives in `runtime.compat`.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from .compat import * # noqa: F401,F403