@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,362 @@
1
+ """Parallel Execution Backend — concurrent subagent job management.
2
+
3
+ Manages concurrent subagent jobs with isolation, artifact streaming,
4
+ and a 100-job limit using stdlib ThreadPoolExecutor.
5
+
6
+ Feature flag: OMG_PARALLEL_SUBAGENTS_ENABLED (default: False)
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import sys
12
+ import time
13
+ import uuid
14
+ import threading
15
+ from concurrent.futures import ThreadPoolExecutor
16
+ from datetime import datetime, timezone
17
+ from typing import Any
18
+
19
+ # --- Path resolution (never relies on CWD) ---
20
+ _DISPATCHER_DIR = os.path.dirname(os.path.abspath(__file__))
21
+ _OMG_ROOT = os.path.dirname(_DISPATCHER_DIR)
22
+
23
+ # --- Constants ---
24
+ MAX_JOBS = 100
25
+
26
+ # --- Module-level singletons ---
27
+ _executor: ThreadPoolExecutor | None = None
28
+ _jobs: dict[str, dict[str, Any]] = {}
29
+ _lock = threading.Lock()
30
+
31
+
32
+ def _get_feature_flag() -> Any:
33
+ """Lazy-import get_feature_flag from hooks/_common.py."""
34
+ hooks_dir = os.path.join(_OMG_ROOT, "hooks")
35
+ if hooks_dir not in sys.path:
36
+ sys.path.insert(0, hooks_dir)
37
+ try:
38
+ from _common import get_feature_flag # pyright: ignore[reportMissingImports]
39
+ return get_feature_flag
40
+ except ImportError:
41
+ return None
42
+
43
+
44
+ def _get_atomic_json_write() -> Any:
45
+ """Lazy-import atomic_json_write from hooks/_common.py."""
46
+ hooks_dir = os.path.join(_OMG_ROOT, "hooks")
47
+ if hooks_dir not in sys.path:
48
+ sys.path.insert(0, hooks_dir)
49
+ try:
50
+ from _common import atomic_json_write # pyright: ignore[reportMissingImports]
51
+ return atomic_json_write
52
+ except ImportError:
53
+ return None
54
+
55
+
56
+ def _is_enabled() -> bool:
57
+ """Check if parallel subagents feature is enabled.
58
+
59
+ Resolution: env var OMG_PARALLEL_SUBAGENTS_ENABLED → settings.json → default False.
60
+ """
61
+ # Fast path: check env var directly
62
+ env_val = os.environ.get("OMG_PARALLEL_SUBAGENTS_ENABLED", "").lower()
63
+ if env_val in ("0", "false", "no"):
64
+ return False
65
+ if env_val in ("1", "true", "yes"):
66
+ return True
67
+
68
+ # Slow path: check via get_feature_flag
69
+ get_flag = _get_feature_flag()
70
+ if get_flag is not None:
71
+ return get_flag("PARALLEL_SUBAGENTS", default=False)
72
+ return False
73
+
74
+
75
+ def _get_project_dir() -> str:
76
+ """Get project directory from env or cwd."""
77
+ return os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
78
+
79
+
80
+ def _jobs_dir() -> str:
81
+ """Return the jobs state directory path."""
82
+ return os.path.join(_get_project_dir(), ".omg", "state", "jobs")
83
+
84
+
85
+ def _job_path(job_id: str) -> str:
86
+ """Return the file path for a specific job."""
87
+ return os.path.join(_jobs_dir(), f"{job_id}.json")
88
+
89
+
90
+ def _persist_job(job_id: str, record: dict[str, Any]) -> None:
91
+ """Persist job record to disk via atomic_json_write."""
92
+ writer = _get_atomic_json_write()
93
+ if writer is not None:
94
+ writer(_job_path(job_id), record)
95
+
96
+
97
+ def _load_job_from_disk(job_id: str) -> dict[str, Any] | None:
98
+ """Load a job record from disk if it exists."""
99
+ path = _job_path(job_id)
100
+ if not os.path.exists(path):
101
+ return None
102
+ try:
103
+ import json
104
+ with open(path, "r", encoding="utf-8") as f:
105
+ return json.load(f)
106
+ except (OSError, ValueError):
107
+ return None
108
+
109
+
110
+ def get_executor() -> ThreadPoolExecutor:
111
+ """Lazy-init and return the module-level ThreadPoolExecutor singleton."""
112
+ global _executor
113
+ if _executor is None:
114
+ _executor = ThreadPoolExecutor(max_workers=MAX_JOBS)
115
+ return _executor
116
+
117
+
118
+ def _running_count() -> int:
119
+ """Count jobs with status 'running'."""
120
+ return len([j for j in _jobs.values() if j["status"] == "running"])
121
+
122
+
123
+ def submit_job(
124
+ agent_name: str,
125
+ task_text: str,
126
+ isolation: str = "none",
127
+ ) -> str:
128
+ """Submit a subagent job for concurrent execution.
129
+
130
+ Args:
131
+ agent_name: Name of the agent to dispatch.
132
+ task_text: Task description / prompt for the agent.
133
+ isolation: Isolation backend — "none" (default) or "worktree".
134
+
135
+ Returns:
136
+ job_id (8-char hex string).
137
+
138
+ Raises:
139
+ RuntimeError: If feature is disabled or job limit reached.
140
+ """
141
+ if not _is_enabled():
142
+ raise RuntimeError("feature disabled")
143
+
144
+ with _lock:
145
+ if _running_count() >= MAX_JOBS:
146
+ raise RuntimeError("job limit reached")
147
+
148
+ job_id = uuid.uuid4().hex[:8]
149
+ now = datetime.now(timezone.utc).isoformat()
150
+ record: dict[str, Any] = {
151
+ "job_id": job_id,
152
+ "agent_name": agent_name,
153
+ "task_text": task_text,
154
+ "isolation": isolation,
155
+ "status": "queued",
156
+ "created_at": now,
157
+ "artifacts": [],
158
+ "error": None,
159
+ }
160
+ _jobs[job_id] = record
161
+
162
+ # Persist initial state
163
+ _persist_job(job_id, record)
164
+
165
+ # Submit to thread pool — returns immediately
166
+ executor = get_executor()
167
+ executor.submit(_run_job, job_id)
168
+
169
+ return job_id
170
+
171
+
172
+ def _check_git_available() -> bool:
173
+ """Return True if git is available on PATH."""
174
+ import shutil
175
+ return shutil.which("git") is not None
176
+
177
+
178
+ def _setup_worktree(job_id: str) -> str | None:
179
+ """Attempt to create a git worktree for job isolation.
180
+
181
+ Returns worktree path on success, None on failure.
182
+ """
183
+ if not _check_git_available():
184
+ return None
185
+
186
+ import subprocess
187
+
188
+ project_dir = _get_project_dir()
189
+ worktree_dir = os.path.join(project_dir, ".omg", "worktrees", job_id)
190
+
191
+ try:
192
+ os.makedirs(os.path.dirname(worktree_dir), exist_ok=True)
193
+ subprocess.run(
194
+ ["git", "worktree", "add", "--detach", worktree_dir],
195
+ capture_output=True,
196
+ text=True,
197
+ check=True,
198
+ timeout=30,
199
+ cwd=project_dir,
200
+ )
201
+ return worktree_dir
202
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired, OSError):
203
+ return None
204
+
205
+
206
+ def _cleanup_worktree(worktree_dir: str) -> None:
207
+ """Remove a git worktree (best-effort)."""
208
+ import subprocess
209
+ import shutil
210
+
211
+ project_dir = _get_project_dir()
212
+ try:
213
+ subprocess.run(
214
+ ["git", "worktree", "remove", "--force", worktree_dir],
215
+ capture_output=True,
216
+ text=True,
217
+ check=False,
218
+ timeout=15,
219
+ cwd=project_dir,
220
+ )
221
+ except (subprocess.TimeoutExpired, OSError):
222
+ pass
223
+
224
+ # Fallback: remove directory if still exists
225
+ try:
226
+ if os.path.isdir(worktree_dir):
227
+ shutil.rmtree(worktree_dir, ignore_errors=True)
228
+ except OSError:
229
+ pass
230
+
231
+
232
+ def _run_job(job_id: str) -> None:
233
+ """Execute a subagent job in the thread pool.
234
+
235
+ Updates job status and persists artifacts as they are produced.
236
+ NOTE: Does NOT actually spawn Claude — simulates execution for now.
237
+ """
238
+ with _lock:
239
+ record = _jobs.get(job_id)
240
+ if record is None:
241
+ return
242
+ if record["status"] == "cancelled":
243
+ return
244
+ record["status"] = "running"
245
+ record["started_at"] = datetime.now(timezone.utc).isoformat()
246
+
247
+ _persist_job(job_id, record)
248
+
249
+ worktree_dir: str | None = None
250
+ try:
251
+ # Setup isolation if requested
252
+ if record.get("isolation") == "worktree":
253
+ worktree_dir = _setup_worktree(job_id)
254
+ if worktree_dir:
255
+ with _lock:
256
+ record["worktree"] = worktree_dir
257
+ _persist_job(job_id, record)
258
+
259
+ # --- Simulated execution ---
260
+ # Real implementation would dispatch to an agent here.
261
+ # For now, simulate work + produce an artifact.
262
+ time.sleep(0.05) # Simulate brief work
263
+
264
+ artifact = {
265
+ "type": "result",
266
+ "agent": record["agent_name"],
267
+ "content": f"Simulated output for: {record['task_text'][:100]}",
268
+ "produced_at": datetime.now(timezone.utc).isoformat(),
269
+ }
270
+
271
+ with _lock:
272
+ # Check for cancellation mid-execution
273
+ if record["status"] == "cancelled":
274
+ return
275
+ record["artifacts"].append(artifact)
276
+ record["status"] = "completed"
277
+ record["completed_at"] = datetime.now(timezone.utc).isoformat()
278
+
279
+ _persist_job(job_id, record)
280
+
281
+ except Exception as exc:
282
+ with _lock:
283
+ record["status"] = "failed"
284
+ record["error"] = str(exc)
285
+ record["completed_at"] = datetime.now(timezone.utc).isoformat()
286
+ _persist_job(job_id, record)
287
+
288
+ finally:
289
+ # Cleanup worktree if created
290
+ if worktree_dir:
291
+ _cleanup_worktree(worktree_dir)
292
+
293
+
294
+ def get_job_status(job_id: str) -> dict[str, Any]:
295
+ """Get the current status and artifacts for a job.
296
+
297
+ Args:
298
+ job_id: Job identifier returned by submit_job().
299
+
300
+ Returns:
301
+ Job record dict, or {"error": "not found"} if job doesn't exist.
302
+ """
303
+ with _lock:
304
+ record = _jobs.get(job_id)
305
+ if record is not None:
306
+ return dict(record)
307
+
308
+ # Not in memory — try loading from disk
309
+ disk_record = _load_job_from_disk(job_id)
310
+ if disk_record is not None:
311
+ return disk_record
312
+
313
+ return {"error": "not found"}
314
+
315
+
316
+ def cancel_job(job_id: str) -> bool:
317
+ """Cancel a queued or running job.
318
+
319
+ Args:
320
+ job_id: Job identifier to cancel.
321
+
322
+ Returns:
323
+ True if the job was cancelled, False if not found or already terminal.
324
+ """
325
+ with _lock:
326
+ record = _jobs.get(job_id)
327
+ if record is None:
328
+ return False
329
+ if record["status"] in ("completed", "failed", "cancelled"):
330
+ return False
331
+ record["status"] = "cancelled"
332
+ record["completed_at"] = datetime.now(timezone.utc).isoformat()
333
+
334
+ _persist_job(job_id, record)
335
+ return True
336
+
337
+
338
+ def list_jobs(status_filter: str | None = None) -> list[dict[str, Any]]:
339
+ """List all jobs, optionally filtered by status.
340
+
341
+ Args:
342
+ status_filter: If provided, only return jobs matching this status.
343
+
344
+ Returns:
345
+ List of job record dicts.
346
+ """
347
+ with _lock:
348
+ if status_filter is None:
349
+ return [dict(j) for j in _jobs.values()]
350
+ return [dict(j) for j in _jobs.values() if j["status"] == status_filter]
351
+
352
+
353
+ def shutdown(wait: bool = True) -> None:
354
+ """Shut down the executor gracefully.
355
+
356
+ Args:
357
+ wait: If True, wait for running jobs to complete before returning.
358
+ """
359
+ global _executor
360
+ if _executor is not None:
361
+ _executor.shutdown(wait=wait)
362
+ _executor = None