@trac3r/oh-my-god 2.2.11

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 (638) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/INSTALL-VERIFICATION-INDEX.md +51 -0
  3. package/LICENSE +21 -0
  4. package/OMG-setup.sh +2549 -0
  5. package/QUICK-REFERENCE.md +58 -0
  6. package/README.md +207 -0
  7. package/agents/__init__.py +1 -0
  8. package/agents/__pycache__/model_roles.cpython-313.pyc +0 -0
  9. package/agents/_model_roles.yaml +26 -0
  10. package/agents/designer.md +67 -0
  11. package/agents/explore.md +60 -0
  12. package/agents/model_roles.py +196 -0
  13. package/agents/omg-api-builder.md +23 -0
  14. package/agents/omg-architect-mode.md +41 -0
  15. package/agents/omg-architect.md +13 -0
  16. package/agents/omg-backend-engineer.md +41 -0
  17. package/agents/omg-critic.md +16 -0
  18. package/agents/omg-database-engineer.md +41 -0
  19. package/agents/omg-escalation-router.md +17 -0
  20. package/agents/omg-executor.md +12 -0
  21. package/agents/omg-frontend-designer.md +41 -0
  22. package/agents/omg-implement-mode.md +49 -0
  23. package/agents/omg-infra-engineer.md +41 -0
  24. package/agents/omg-qa-tester.md +16 -0
  25. package/agents/omg-research-mode.md +41 -0
  26. package/agents/omg-security-auditor.md +41 -0
  27. package/agents/omg-testing-engineer.md +41 -0
  28. package/agents/plan.md +80 -0
  29. package/agents/quick_task.md +64 -0
  30. package/agents/reviewer.md +83 -0
  31. package/agents/task.md +71 -0
  32. package/bin/omg +41 -0
  33. package/commands/OMG:ai-commit.md +113 -0
  34. package/commands/OMG:api-twin.md +22 -0
  35. package/commands/OMG:arch.md +313 -0
  36. package/commands/OMG:browser.md +29 -0
  37. package/commands/OMG:ccg.md +22 -0
  38. package/commands/OMG:compat.md +57 -0
  39. package/commands/OMG:cost.md +181 -0
  40. package/commands/OMG:crazy.md +125 -0
  41. package/commands/OMG:create-agent.md +183 -0
  42. package/commands/OMG:deep-plan.md +18 -0
  43. package/commands/OMG:deps.md +248 -0
  44. package/commands/OMG:diagnose-plugins.md +33 -0
  45. package/commands/OMG:doctor.md +37 -0
  46. package/commands/OMG:domain-init.md +11 -0
  47. package/commands/OMG:escalate.md +52 -0
  48. package/commands/OMG:forge.md +103 -0
  49. package/commands/OMG:health-check.md +48 -0
  50. package/commands/OMG:init.md +134 -0
  51. package/commands/OMG:issue.md +56 -0
  52. package/commands/OMG:mode.md +44 -0
  53. package/commands/OMG:playwright.md +17 -0
  54. package/commands/OMG:preflight.md +26 -0
  55. package/commands/OMG:preset.md +49 -0
  56. package/commands/OMG:profile-review.md +58 -0
  57. package/commands/OMG:project-init.md +11 -0
  58. package/commands/OMG:ralph-start.md +43 -0
  59. package/commands/OMG:ralph-stop.md +23 -0
  60. package/commands/OMG:security-check.md +28 -0
  61. package/commands/OMG:session-branch.md +101 -0
  62. package/commands/OMG:session-fork.md +57 -0
  63. package/commands/OMG:session-merge.md +138 -0
  64. package/commands/OMG:setup.md +82 -0
  65. package/commands/OMG:ship.md +18 -0
  66. package/commands/OMG:stats.md +225 -0
  67. package/commands/OMG:teams.md +54 -0
  68. package/commands/OMG:theme.md +44 -0
  69. package/commands/OMG:validate.md +59 -0
  70. package/commands/__init__.py +1 -0
  71. package/docs/command-surface.md +55 -0
  72. package/docs/install/claude-code.md +53 -0
  73. package/docs/install/codex.md +45 -0
  74. package/docs/install/gemini.md +43 -0
  75. package/docs/install/github-action.md +81 -0
  76. package/docs/install/github-app-required-checks.md +107 -0
  77. package/docs/install/github-app.md +161 -0
  78. package/docs/install/kimi.md +43 -0
  79. package/docs/install/opencode.md +38 -0
  80. package/docs/proof.md +182 -0
  81. package/hooks/__init__.py +0 -0
  82. package/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  83. package/hooks/__pycache__/_agent_registry.cpython-313.pyc +0 -0
  84. package/hooks/__pycache__/_analytics.cpython-313.pyc +0 -0
  85. package/hooks/__pycache__/_budget.cpython-313.pyc +0 -0
  86. package/hooks/__pycache__/_common.cpython-313.pyc +0 -0
  87. package/hooks/__pycache__/_compression_optimizer.cpython-313.pyc +0 -0
  88. package/hooks/__pycache__/_cost_ledger.cpython-313.pyc +0 -0
  89. package/hooks/__pycache__/_learnings.cpython-313.pyc +0 -0
  90. package/hooks/__pycache__/_memory.cpython-313.pyc +0 -0
  91. package/hooks/__pycache__/_post_write.cpython-313.pyc +0 -0
  92. package/hooks/__pycache__/_protected_context.cpython-313.pyc +0 -0
  93. package/hooks/__pycache__/_token_counter.cpython-313.pyc +0 -0
  94. package/hooks/__pycache__/branch_manager.cpython-313.pyc +0 -0
  95. package/hooks/__pycache__/budget_governor.cpython-313.pyc +0 -0
  96. package/hooks/__pycache__/circuit-breaker.cpython-313.pyc +0 -0
  97. package/hooks/__pycache__/compression_feedback.cpython-313.pyc +0 -0
  98. package/hooks/__pycache__/config-guard.cpython-313.pyc +0 -0
  99. package/hooks/__pycache__/context_pressure.cpython-313.pyc +0 -0
  100. package/hooks/__pycache__/credential_store.cpython-313.pyc +0 -0
  101. package/hooks/__pycache__/fetch-rate-limits.cpython-313.pyc +0 -0
  102. package/hooks/__pycache__/firewall.cpython-313.pyc +0 -0
  103. package/hooks/__pycache__/hashline-formatter-bridge.cpython-313.pyc +0 -0
  104. package/hooks/__pycache__/hashline-injector.cpython-313.pyc +0 -0
  105. package/hooks/__pycache__/hashline-validator.cpython-313.pyc +0 -0
  106. package/hooks/__pycache__/idle-detector.cpython-313.pyc +0 -0
  107. package/hooks/__pycache__/instructions-loaded.cpython-313.pyc +0 -0
  108. package/hooks/__pycache__/intentgate-keyword-detector.cpython-313.pyc +0 -0
  109. package/hooks/__pycache__/magic-keyword-router.cpython-313.pyc +0 -0
  110. package/hooks/__pycache__/policy_engine.cpython-313.pyc +0 -0
  111. package/hooks/__pycache__/post-tool-failure.cpython-313.pyc +0 -0
  112. package/hooks/__pycache__/post-write.cpython-313.pyc +0 -0
  113. package/hooks/__pycache__/post_write.cpython-313.pyc +0 -0
  114. package/hooks/__pycache__/pre-compact.cpython-313.pyc +0 -0
  115. package/hooks/__pycache__/pre-tool-inject.cpython-313.pyc +0 -0
  116. package/hooks/__pycache__/prompt-enhancer.cpython-313.pyc +0 -0
  117. package/hooks/__pycache__/quality-runner.cpython-313.pyc +0 -0
  118. package/hooks/__pycache__/query.cpython-313.pyc +0 -0
  119. package/hooks/__pycache__/secret-guard.cpython-313.pyc +0 -0
  120. package/hooks/__pycache__/secret_audit.cpython-313.pyc +0 -0
  121. package/hooks/__pycache__/security_validators.cpython-313.pyc +0 -0
  122. package/hooks/__pycache__/session-end-capture.cpython-313.pyc +0 -0
  123. package/hooks/__pycache__/session-start.cpython-313.pyc +0 -0
  124. package/hooks/__pycache__/setup_wizard.cpython-313.pyc +0 -0
  125. package/hooks/__pycache__/shadow_manager.cpython-313.pyc +0 -0
  126. package/hooks/__pycache__/state_migration.cpython-313.pyc +0 -0
  127. package/hooks/__pycache__/stop-gate.cpython-313.pyc +0 -0
  128. package/hooks/__pycache__/stop_dispatcher.cpython-313.pyc +0 -0
  129. package/hooks/__pycache__/tdd-gate.cpython-313.pyc +0 -0
  130. package/hooks/__pycache__/terms-guard.cpython-313.pyc +0 -0
  131. package/hooks/__pycache__/test-validator.cpython-313.pyc +0 -0
  132. package/hooks/__pycache__/test_generator_hook.cpython-313.pyc +0 -0
  133. package/hooks/__pycache__/todo-state-tracker.cpython-313.pyc +0 -0
  134. package/hooks/__pycache__/tool-ledger.cpython-313.pyc +0 -0
  135. package/hooks/__pycache__/trust_review.cpython-313.pyc +0 -0
  136. package/hooks/__pycache__/user-prompt-submit.cpython-313.pyc +0 -0
  137. package/hooks/_agent_registry.py +481 -0
  138. package/hooks/_analytics.py +291 -0
  139. package/hooks/_budget.py +31 -0
  140. package/hooks/_common.py +761 -0
  141. package/hooks/_compression_optimizer.py +119 -0
  142. package/hooks/_cost_ledger.py +176 -0
  143. package/hooks/_learnings.py +126 -0
  144. package/hooks/_memory.py +103 -0
  145. package/hooks/_post_write.py +46 -0
  146. package/hooks/_protected_context.py +150 -0
  147. package/hooks/_token_counter.py +221 -0
  148. package/hooks/branch_manager.py +255 -0
  149. package/hooks/budget_governor.py +326 -0
  150. package/hooks/circuit-breaker.py +270 -0
  151. package/hooks/compression_feedback.py +254 -0
  152. package/hooks/config-guard.py +193 -0
  153. package/hooks/context_pressure.py +119 -0
  154. package/hooks/credential_store.py +970 -0
  155. package/hooks/fetch-rate-limits.py +212 -0
  156. package/hooks/firewall.py +323 -0
  157. package/hooks/hashline-formatter-bridge.py +224 -0
  158. package/hooks/hashline-injector.py +273 -0
  159. package/hooks/hashline-validator.py +216 -0
  160. package/hooks/idle-detector.py +97 -0
  161. package/hooks/instructions-loaded.py +26 -0
  162. package/hooks/intentgate-keyword-detector.py +200 -0
  163. package/hooks/magic-keyword-router.py +195 -0
  164. package/hooks/policy_engine.py +767 -0
  165. package/hooks/post-tool-failure.py +19 -0
  166. package/hooks/post-write.py +233 -0
  167. package/hooks/pre-compact.py +470 -0
  168. package/hooks/pre-tool-inject.py +98 -0
  169. package/hooks/prompt-enhancer.py +879 -0
  170. package/hooks/quality-runner.py +191 -0
  171. package/hooks/query.py +512 -0
  172. package/hooks/secret-guard.py +120 -0
  173. package/hooks/secret_audit.py +144 -0
  174. package/hooks/security_validators.py +93 -0
  175. package/hooks/session-end-capture.py +505 -0
  176. package/hooks/session-start.py +261 -0
  177. package/hooks/setup_wizard.py +1101 -0
  178. package/hooks/shadow_manager.py +476 -0
  179. package/hooks/state_migration.py +228 -0
  180. package/hooks/stop-gate.py +7 -0
  181. package/hooks/stop_dispatcher.py +1259 -0
  182. package/hooks/tdd-gate.py +10 -0
  183. package/hooks/terms-guard.py +98 -0
  184. package/hooks/test-validator.py +462 -0
  185. package/hooks/test_generator_hook.py +123 -0
  186. package/hooks/todo-state-tracker.py +114 -0
  187. package/hooks/tool-ledger.py +165 -0
  188. package/hooks/trust_review.py +662 -0
  189. package/hooks/user-prompt-submit.py +12 -0
  190. package/hud/omg-hud.mjs +1571 -0
  191. package/lab/__init__.py +1 -0
  192. package/lab/__pycache__/__init__.cpython-313.pyc +0 -0
  193. package/lab/__pycache__/axolotl_adapter.cpython-313.pyc +0 -0
  194. package/lab/__pycache__/forge_runner.cpython-313.pyc +0 -0
  195. package/lab/__pycache__/gazebo_adapter.cpython-313.pyc +0 -0
  196. package/lab/__pycache__/isaac_gym_adapter.cpython-313.pyc +0 -0
  197. package/lab/__pycache__/mock_isaac_env.cpython-313.pyc +0 -0
  198. package/lab/__pycache__/pipeline.cpython-313.pyc +0 -0
  199. package/lab/__pycache__/policies.cpython-313.pyc +0 -0
  200. package/lab/__pycache__/pybullet_adapter.cpython-313.pyc +0 -0
  201. package/lab/axolotl_adapter.py +531 -0
  202. package/lab/forge_runner.py +103 -0
  203. package/lab/gazebo_adapter.py +168 -0
  204. package/lab/isaac_gym_adapter.py +190 -0
  205. package/lab/mock_isaac_env.py +47 -0
  206. package/lab/pipeline.py +712 -0
  207. package/lab/policies.py +52 -0
  208. package/lab/pybullet_adapter.py +192 -0
  209. package/package.json +61 -0
  210. package/plugins/README.md +78 -0
  211. package/plugins/__init__.py +1 -0
  212. package/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/plugins/advanced/commands/OMG-code-review.md +114 -0
  214. package/plugins/advanced/commands/OMG-deep-plan.md +266 -0
  215. package/plugins/advanced/commands/OMG-handoff.md +115 -0
  216. package/plugins/advanced/commands/OMG-learn.md +110 -0
  217. package/plugins/advanced/commands/OMG-maintainer.md +31 -0
  218. package/plugins/advanced/commands/OMG-ralph-start.md +43 -0
  219. package/plugins/advanced/commands/OMG-ralph-stop.md +23 -0
  220. package/plugins/advanced/commands/OMG-security-review.md +16 -0
  221. package/plugins/advanced/commands/OMG-sequential-thinking.md +20 -0
  222. package/plugins/advanced/commands/OMG-ship.md +46 -0
  223. package/plugins/advanced/commands/OMG:code-review.md +114 -0
  224. package/plugins/advanced/commands/OMG:deep-plan.md +266 -0
  225. package/plugins/advanced/commands/OMG:handoff.md +115 -0
  226. package/plugins/advanced/commands/OMG:learn.md +110 -0
  227. package/plugins/advanced/commands/OMG:maintainer.md +31 -0
  228. package/plugins/advanced/commands/OMG:ralph-start.md +43 -0
  229. package/plugins/advanced/commands/OMG:ralph-stop.md +23 -0
  230. package/plugins/advanced/commands/OMG:security-review.md +16 -0
  231. package/plugins/advanced/commands/OMG:sequential-thinking.md +20 -0
  232. package/plugins/advanced/commands/OMG:ship.md +46 -0
  233. package/plugins/advanced/plugin.json +104 -0
  234. package/plugins/core/plugin.json +204 -0
  235. package/plugins/dephealth/__init__.py +0 -0
  236. package/plugins/dephealth/__pycache__/__init__.cpython-313.pyc +0 -0
  237. package/plugins/dephealth/__pycache__/cve_scanner.cpython-313.pyc +0 -0
  238. package/plugins/dephealth/__pycache__/license_checker.cpython-313.pyc +0 -0
  239. package/plugins/dephealth/__pycache__/manifest_detector.cpython-313.pyc +0 -0
  240. package/plugins/dephealth/__pycache__/vuln_analyzer.cpython-313.pyc +0 -0
  241. package/plugins/dephealth/cve_scanner.py +279 -0
  242. package/plugins/dephealth/license_checker.py +135 -0
  243. package/plugins/dephealth/manifest_detector.py +423 -0
  244. package/plugins/dephealth/vuln_analyzer.py +176 -0
  245. package/plugins/testgen/__init__.py +0 -0
  246. package/plugins/testgen/__pycache__/__init__.cpython-313.pyc +0 -0
  247. package/plugins/testgen/__pycache__/codamosa_engine.cpython-313.pyc +0 -0
  248. package/plugins/testgen/__pycache__/edge_case_synthesizer.cpython-313.pyc +0 -0
  249. package/plugins/testgen/__pycache__/framework_detector.cpython-313.pyc +0 -0
  250. package/plugins/testgen/__pycache__/skeleton_generator.cpython-313.pyc +0 -0
  251. package/plugins/testgen/codamosa_engine.py +402 -0
  252. package/plugins/testgen/edge_case_synthesizer.py +184 -0
  253. package/plugins/testgen/framework_detector.py +271 -0
  254. package/plugins/testgen/skeleton_generator.py +219 -0
  255. package/plugins/viz/__init__.py +0 -0
  256. package/plugins/viz/__pycache__/__init__.cpython-313.pyc +0 -0
  257. package/plugins/viz/__pycache__/ast_parser.cpython-313.pyc +0 -0
  258. package/plugins/viz/__pycache__/diagram_generator.cpython-313.pyc +0 -0
  259. package/plugins/viz/__pycache__/graph_builder.cpython-313.pyc +0 -0
  260. package/plugins/viz/__pycache__/native_parsers.cpython-313.pyc +0 -0
  261. package/plugins/viz/__pycache__/regex_parser.cpython-313.pyc +0 -0
  262. package/plugins/viz/ast_parser.py +139 -0
  263. package/plugins/viz/diagram_generator.py +192 -0
  264. package/plugins/viz/graph_builder.py +444 -0
  265. package/plugins/viz/native_parsers.py +259 -0
  266. package/plugins/viz/regex_parser.py +112 -0
  267. package/pyproject.toml +143 -0
  268. package/registry/__init__.py +1 -0
  269. package/registry/__pycache__/__init__.cpython-313.pyc +0 -0
  270. package/registry/__pycache__/approval_artifact.cpython-313.pyc +0 -0
  271. package/registry/__pycache__/verify_artifact.cpython-313.pyc +0 -0
  272. package/registry/approval_artifact.py +236 -0
  273. package/registry/bundles/algorithms.yaml +45 -0
  274. package/registry/bundles/api-twin.yaml +48 -0
  275. package/registry/bundles/ast-pack.yaml +80 -0
  276. package/registry/bundles/claim-judge.yaml +49 -0
  277. package/registry/bundles/control-plane.yaml +192 -0
  278. package/registry/bundles/data-lineage.yaml +47 -0
  279. package/registry/bundles/delta-classifier.yaml +47 -0
  280. package/registry/bundles/eval-gate.yaml +47 -0
  281. package/registry/bundles/hash-edit.yaml +73 -0
  282. package/registry/bundles/health.yaml +45 -0
  283. package/registry/bundles/hook-governor.yaml +101 -0
  284. package/registry/bundles/incident-replay.yaml +47 -0
  285. package/registry/bundles/lsp-pack.yaml +80 -0
  286. package/registry/bundles/mcp-fabric.yaml +53 -0
  287. package/registry/bundles/plan-council.yaml +56 -0
  288. package/registry/bundles/preflight.yaml +48 -0
  289. package/registry/bundles/proof-gate.yaml +49 -0
  290. package/registry/bundles/remote-supervisor.yaml +49 -0
  291. package/registry/bundles/robotics.yaml +45 -0
  292. package/registry/bundles/secure-worktree-pipeline.yaml +69 -0
  293. package/registry/bundles/security-check.yaml +50 -0
  294. package/registry/bundles/terminal-lane.yaml +61 -0
  295. package/registry/bundles/test-intent-lock.yaml +49 -0
  296. package/registry/bundles/tracebank.yaml +47 -0
  297. package/registry/bundles/vision.yaml +45 -0
  298. package/registry/omg-capability.schema.json +378 -0
  299. package/registry/policy-packs/airgapped.lock.json +11 -0
  300. package/registry/policy-packs/airgapped.signature.json +10 -0
  301. package/registry/policy-packs/airgapped.yaml +16 -0
  302. package/registry/policy-packs/fintech.lock.json +11 -0
  303. package/registry/policy-packs/fintech.signature.json +10 -0
  304. package/registry/policy-packs/fintech.yaml +15 -0
  305. package/registry/policy-packs/locked-prod.lock.json +11 -0
  306. package/registry/policy-packs/locked-prod.signature.json +10 -0
  307. package/registry/policy-packs/locked-prod.yaml +18 -0
  308. package/registry/trusted_signers.json +44 -0
  309. package/registry/verify_artifact.py +493 -0
  310. package/runtime/__init__.py +36 -0
  311. package/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  312. package/runtime/__pycache__/adoption.cpython-313.pyc +0 -0
  313. package/runtime/__pycache__/agent_selector.cpython-313.pyc +0 -0
  314. package/runtime/__pycache__/api_twin.cpython-313.pyc +0 -0
  315. package/runtime/__pycache__/architecture_signal.cpython-313.pyc +0 -0
  316. package/runtime/__pycache__/artifact_parsers.cpython-313.pyc +0 -0
  317. package/runtime/__pycache__/asset_loader.cpython-313.pyc +0 -0
  318. package/runtime/__pycache__/background_verification.cpython-313.pyc +0 -0
  319. package/runtime/__pycache__/budget_envelopes.cpython-313.pyc +0 -0
  320. package/runtime/__pycache__/business_workflow.cpython-313.pyc +0 -0
  321. package/runtime/__pycache__/canonical_surface.cpython-313.pyc +0 -0
  322. package/runtime/__pycache__/canonical_taxonomy.cpython-313.pyc +0 -0
  323. package/runtime/__pycache__/claim_judge.cpython-313.pyc +0 -0
  324. package/runtime/__pycache__/cli_provider.cpython-313.pyc +0 -0
  325. package/runtime/__pycache__/compat.cpython-313.pyc +0 -0
  326. package/runtime/__pycache__/complexity_scorer.cpython-313.pyc +0 -0
  327. package/runtime/__pycache__/compliance_governor.cpython-313.pyc +0 -0
  328. package/runtime/__pycache__/config_transaction.cpython-313.pyc +0 -0
  329. package/runtime/__pycache__/context_compiler.cpython-313.pyc +0 -0
  330. package/runtime/__pycache__/context_engine.cpython-313.pyc +0 -0
  331. package/runtime/__pycache__/context_limits.cpython-313.pyc +0 -0
  332. package/runtime/__pycache__/contract_compiler.cpython-313.pyc +0 -0
  333. package/runtime/__pycache__/custom_agent_loader.cpython-313.pyc +0 -0
  334. package/runtime/__pycache__/data_lineage.cpython-313.pyc +0 -0
  335. package/runtime/__pycache__/defense_state.cpython-313.pyc +0 -0
  336. package/runtime/__pycache__/delta_classifier.cpython-313.pyc +0 -0
  337. package/runtime/__pycache__/dispatcher.cpython-313.pyc +0 -0
  338. package/runtime/__pycache__/doc_generator.cpython-313.pyc +0 -0
  339. package/runtime/__pycache__/domain_packs.cpython-313.pyc +0 -0
  340. package/runtime/__pycache__/ecosystem.cpython-313.pyc +0 -0
  341. package/runtime/__pycache__/equalizer.cpython-313.pyc +0 -0
  342. package/runtime/__pycache__/eval_gate.cpython-313.pyc +0 -0
  343. package/runtime/__pycache__/evidence_narrator.cpython-313.pyc +0 -0
  344. package/runtime/__pycache__/evidence_query.cpython-313.pyc +0 -0
  345. package/runtime/__pycache__/evidence_registry.cpython-313.pyc +0 -0
  346. package/runtime/__pycache__/evidence_requirements.cpython-313.pyc +0 -0
  347. package/runtime/__pycache__/exec_kernel.cpython-313.pyc +0 -0
  348. package/runtime/__pycache__/explainer_formatter.cpython-313.pyc +0 -0
  349. package/runtime/__pycache__/feature_registry.cpython-313.pyc +0 -0
  350. package/runtime/__pycache__/forge_agents.cpython-313.pyc +0 -0
  351. package/runtime/__pycache__/forge_contracts.cpython-313.pyc +0 -0
  352. package/runtime/__pycache__/forge_domains.cpython-313.pyc +0 -0
  353. package/runtime/__pycache__/forge_run_id.cpython-313.pyc +0 -0
  354. package/runtime/__pycache__/github_integration.cpython-313.pyc +0 -0
  355. package/runtime/__pycache__/github_review_bot.cpython-313.pyc +0 -0
  356. package/runtime/__pycache__/github_review_contract.cpython-313.pyc +0 -0
  357. package/runtime/__pycache__/github_review_formatter.cpython-313.pyc +0 -0
  358. package/runtime/__pycache__/guide_assert.cpython-313.pyc +0 -0
  359. package/runtime/__pycache__/hook_governor.cpython-313.pyc +0 -0
  360. package/runtime/__pycache__/host_parity.cpython-313.pyc +0 -0
  361. package/runtime/__pycache__/incident_replay.cpython-313.pyc +0 -0
  362. package/runtime/__pycache__/install_planner.cpython-313.pyc +0 -0
  363. package/runtime/__pycache__/interaction_journal.cpython-313.pyc +0 -0
  364. package/runtime/__pycache__/issue_surface.cpython-313.pyc +0 -0
  365. package/runtime/__pycache__/legacy_compat.cpython-313.pyc +0 -0
  366. package/runtime/__pycache__/mcp_config_writers.cpython-313.pyc +0 -0
  367. package/runtime/__pycache__/mcp_lifecycle.cpython-313.pyc +0 -0
  368. package/runtime/__pycache__/mcp_memory_server.cpython-313.pyc +0 -0
  369. package/runtime/__pycache__/memory_store.cpython-313.pyc +0 -0
  370. package/runtime/__pycache__/merge_writer.cpython-313.pyc +0 -0
  371. package/runtime/__pycache__/music_omr_testbed.cpython-313.pyc +0 -0
  372. package/runtime/__pycache__/mutation_gate.cpython-313.pyc +0 -0
  373. package/runtime/__pycache__/omc_compat.cpython-313.pyc +0 -0
  374. package/runtime/__pycache__/omg_browser_cli.cpython-313.pyc +0 -0
  375. package/runtime/__pycache__/omg_mcp_server.cpython-313.pyc +0 -0
  376. package/runtime/__pycache__/opus_plan.cpython-313.pyc +0 -0
  377. package/runtime/__pycache__/playwright_adapter.cpython-313.pyc +0 -0
  378. package/runtime/__pycache__/playwright_pack.cpython-313.pyc +0 -0
  379. package/runtime/__pycache__/plugin_diagnostics.cpython-313.pyc +0 -0
  380. package/runtime/__pycache__/plugin_interop.cpython-313.pyc +0 -0
  381. package/runtime/__pycache__/policy_pack_loader.cpython-313.pyc +0 -0
  382. package/runtime/__pycache__/preflight.cpython-313.pyc +0 -0
  383. package/runtime/__pycache__/profile_io.cpython-313.pyc +0 -0
  384. package/runtime/__pycache__/prompt_compiler.cpython-313.pyc +0 -0
  385. package/runtime/__pycache__/proof_chain.cpython-313.pyc +0 -0
  386. package/runtime/__pycache__/proof_gate.cpython-313.pyc +0 -0
  387. package/runtime/__pycache__/provider_parity_eval.cpython-313.pyc +0 -0
  388. package/runtime/__pycache__/release_artifact_audit.cpython-313.pyc +0 -0
  389. package/runtime/__pycache__/release_run_coordinator.cpython-313.pyc +0 -0
  390. package/runtime/__pycache__/release_surface_compiler.cpython-313.pyc +0 -0
  391. package/runtime/__pycache__/release_surface_registry.cpython-313.pyc +0 -0
  392. package/runtime/__pycache__/release_surfaces.cpython-313.pyc +0 -0
  393. package/runtime/__pycache__/remote_supervisor.cpython-313.pyc +0 -0
  394. package/runtime/__pycache__/repro_pack.cpython-313.pyc +0 -0
  395. package/runtime/__pycache__/rollback_manifest.cpython-313.pyc +0 -0
  396. package/runtime/__pycache__/router_critics.cpython-313.pyc +0 -0
  397. package/runtime/__pycache__/router_executor.cpython-313.pyc +0 -0
  398. package/runtime/__pycache__/router_selector.cpython-313.pyc +0 -0
  399. package/runtime/__pycache__/runtime_contracts.cpython-313.pyc +0 -0
  400. package/runtime/__pycache__/runtime_profile.cpython-313.pyc +0 -0
  401. package/runtime/__pycache__/security_check.cpython-313.pyc +0 -0
  402. package/runtime/__pycache__/session_health.cpython-313.pyc +0 -0
  403. package/runtime/__pycache__/skill_evolution.cpython-313.pyc +0 -0
  404. package/runtime/__pycache__/skill_registry.cpython-313.pyc +0 -0
  405. package/runtime/__pycache__/subagent_dispatcher.cpython-313.pyc +0 -0
  406. package/runtime/__pycache__/subscription_tiers.cpython-313.pyc +0 -0
  407. package/runtime/__pycache__/team_router.cpython-313.pyc +0 -0
  408. package/runtime/__pycache__/test_intent_lock.cpython-313-pytest-9.0.2.pyc +0 -0
  409. package/runtime/__pycache__/test_intent_lock.cpython-313.pyc +0 -0
  410. package/runtime/__pycache__/tmux_session_manager.cpython-313.pyc +0 -0
  411. package/runtime/__pycache__/tool_fabric.cpython-313.pyc +0 -0
  412. package/runtime/__pycache__/tool_plan_gate.cpython-313.pyc +0 -0
  413. package/runtime/__pycache__/tool_relevance.cpython-313.pyc +0 -0
  414. package/runtime/__pycache__/tracebank.cpython-313.pyc +0 -0
  415. package/runtime/__pycache__/untrusted_content.cpython-313.pyc +0 -0
  416. package/runtime/__pycache__/validate.cpython-313.pyc +0 -0
  417. package/runtime/__pycache__/verdict_schema.cpython-313.pyc +0 -0
  418. package/runtime/__pycache__/verification_controller.cpython-313.pyc +0 -0
  419. package/runtime/__pycache__/verification_loop.cpython-313.pyc +0 -0
  420. package/runtime/__pycache__/vision_artifacts.cpython-313.pyc +0 -0
  421. package/runtime/__pycache__/vision_cache.cpython-313.pyc +0 -0
  422. package/runtime/__pycache__/vision_jobs.cpython-313.pyc +0 -0
  423. package/runtime/__pycache__/worker_watchdog.cpython-313.pyc +0 -0
  424. package/runtime/adapters/__init__.py +13 -0
  425. package/runtime/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
  426. package/runtime/adapters/__pycache__/claude.cpython-313.pyc +0 -0
  427. package/runtime/adapters/__pycache__/gpt.cpython-313.pyc +0 -0
  428. package/runtime/adapters/__pycache__/local.cpython-313.pyc +0 -0
  429. package/runtime/adapters/claude.py +63 -0
  430. package/runtime/adapters/gpt.py +56 -0
  431. package/runtime/adapters/local.py +56 -0
  432. package/runtime/adoption.py +280 -0
  433. package/runtime/api_twin.py +450 -0
  434. package/runtime/architecture_signal.py +226 -0
  435. package/runtime/artifact_parsers.py +161 -0
  436. package/runtime/asset_loader.py +62 -0
  437. package/runtime/background_verification.py +178 -0
  438. package/runtime/budget_envelopes.py +398 -0
  439. package/runtime/business_workflow.py +234 -0
  440. package/runtime/canonical_surface.py +53 -0
  441. package/runtime/canonical_taxonomy.py +27 -0
  442. package/runtime/claim_judge.py +648 -0
  443. package/runtime/cli_provider.py +105 -0
  444. package/runtime/compat.py +2222 -0
  445. package/runtime/complexity_scorer.py +148 -0
  446. package/runtime/compliance_governor.py +505 -0
  447. package/runtime/config_transaction.py +304 -0
  448. package/runtime/context_compiler.py +131 -0
  449. package/runtime/context_engine.py +708 -0
  450. package/runtime/context_limits.py +363 -0
  451. package/runtime/contract_compiler.py +3664 -0
  452. package/runtime/custom_agent_loader.py +366 -0
  453. package/runtime/data_lineage.py +244 -0
  454. package/runtime/defense_state.py +261 -0
  455. package/runtime/delta_classifier.py +231 -0
  456. package/runtime/dispatcher.py +47 -0
  457. package/runtime/doc_generator.py +319 -0
  458. package/runtime/domain_packs.py +75 -0
  459. package/runtime/ecosystem.py +371 -0
  460. package/runtime/equalizer.py +268 -0
  461. package/runtime/eval_gate.py +96 -0
  462. package/runtime/evidence_narrator.py +147 -0
  463. package/runtime/evidence_query.py +303 -0
  464. package/runtime/evidence_registry.py +16 -0
  465. package/runtime/evidence_requirements.py +157 -0
  466. package/runtime/exec_kernel.py +267 -0
  467. package/runtime/explainer_formatter.py +82 -0
  468. package/runtime/feature_registry.py +109 -0
  469. package/runtime/forge_agents.py +915 -0
  470. package/runtime/forge_contracts.py +519 -0
  471. package/runtime/forge_domains.py +68 -0
  472. package/runtime/forge_run_id.py +86 -0
  473. package/runtime/guide_assert.py +135 -0
  474. package/runtime/hook_governor.py +156 -0
  475. package/runtime/host_parity.py +373 -0
  476. package/runtime/incident_replay.py +310 -0
  477. package/runtime/install_planner.py +617 -0
  478. package/runtime/interaction_journal.py +566 -0
  479. package/runtime/issue_surface.py +472 -0
  480. package/runtime/legacy_compat.py +7 -0
  481. package/runtime/mcp_config_writers.py +360 -0
  482. package/runtime/mcp_lifecycle.py +175 -0
  483. package/runtime/mcp_memory_server.py +220 -0
  484. package/runtime/memory_parsers/__init__.py +0 -0
  485. package/runtime/memory_parsers/__pycache__/__init__.cpython-313.pyc +0 -0
  486. package/runtime/memory_parsers/__pycache__/chatgpt_parser.cpython-313.pyc +0 -0
  487. package/runtime/memory_parsers/__pycache__/claude_import.cpython-313.pyc +0 -0
  488. package/runtime/memory_parsers/__pycache__/export.cpython-313.pyc +0 -0
  489. package/runtime/memory_parsers/__pycache__/gemini_import.cpython-313.pyc +0 -0
  490. package/runtime/memory_parsers/__pycache__/kimi_import.cpython-313.pyc +0 -0
  491. package/runtime/memory_parsers/chatgpt_parser.py +257 -0
  492. package/runtime/memory_parsers/claude_import.py +107 -0
  493. package/runtime/memory_parsers/export.py +97 -0
  494. package/runtime/memory_parsers/gemini_import.py +91 -0
  495. package/runtime/memory_parsers/kimi_import.py +91 -0
  496. package/runtime/memory_store.py +1182 -0
  497. package/runtime/merge_writer.py +445 -0
  498. package/runtime/music_omr_testbed.py +336 -0
  499. package/runtime/mutation_gate.py +320 -0
  500. package/runtime/omc_compat.py +7 -0
  501. package/runtime/omg_browser_cli.py +95 -0
  502. package/runtime/omg_compat_contract_snapshot.json +936 -0
  503. package/runtime/omg_contract_snapshot.json +936 -0
  504. package/runtime/omg_mcp_server.py +306 -0
  505. package/runtime/playwright_adapter.py +39 -0
  506. package/runtime/playwright_pack.py +253 -0
  507. package/runtime/plugin_diagnostics.py +308 -0
  508. package/runtime/plugin_interop.py +1060 -0
  509. package/runtime/policy_pack_loader.py +147 -0
  510. package/runtime/preflight.py +135 -0
  511. package/runtime/profile_io.py +328 -0
  512. package/runtime/proof_chain.py +472 -0
  513. package/runtime/proof_gate.py +442 -0
  514. package/runtime/provider_parity_eval.py +109 -0
  515. package/runtime/providers/__init__.py +0 -0
  516. package/runtime/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  517. package/runtime/providers/__pycache__/codex_provider.cpython-313.pyc +0 -0
  518. package/runtime/providers/__pycache__/gemini_provider.cpython-313.pyc +0 -0
  519. package/runtime/providers/__pycache__/kimi_provider.cpython-313.pyc +0 -0
  520. package/runtime/providers/__pycache__/opencode_provider.cpython-313.pyc +0 -0
  521. package/runtime/providers/codex_provider.py +129 -0
  522. package/runtime/providers/gemini_provider.py +143 -0
  523. package/runtime/providers/kimi_provider.py +167 -0
  524. package/runtime/providers/opencode_provider.py +99 -0
  525. package/runtime/release_artifact_audit.py +556 -0
  526. package/runtime/release_run_coordinator.py +574 -0
  527. package/runtime/release_surface_compiler.py +643 -0
  528. package/runtime/release_surface_registry.py +283 -0
  529. package/runtime/release_surfaces.py +320 -0
  530. package/runtime/remote_supervisor.py +79 -0
  531. package/runtime/repro_pack.py +398 -0
  532. package/runtime/rollback_manifest.py +143 -0
  533. package/runtime/router_critics.py +229 -0
  534. package/runtime/router_executor.py +142 -0
  535. package/runtime/router_selector.py +99 -0
  536. package/runtime/runtime_contracts.py +292 -0
  537. package/runtime/runtime_profile.py +133 -0
  538. package/runtime/security_check.py +1094 -0
  539. package/runtime/session_health.py +546 -0
  540. package/runtime/skill_evolution.py +221 -0
  541. package/runtime/skill_registry.py +53 -0
  542. package/runtime/subagent_dispatcher.py +604 -0
  543. package/runtime/subscription_tiers.py +258 -0
  544. package/runtime/team_router.py +1399 -0
  545. package/runtime/test_intent_lock.py +543 -0
  546. package/runtime/tmux_session_manager.py +172 -0
  547. package/runtime/tool_fabric.py +570 -0
  548. package/runtime/tool_plan_gate.py +460 -0
  549. package/runtime/tracebank.py +125 -0
  550. package/runtime/untrusted_content.py +360 -0
  551. package/runtime/validate.py +293 -0
  552. package/runtime/verdict_schema.py +198 -0
  553. package/runtime/verification_controller.py +235 -0
  554. package/runtime/verification_loop.py +73 -0
  555. package/runtime/vision_artifacts.py +31 -0
  556. package/runtime/vision_cache.py +38 -0
  557. package/runtime/vision_jobs.py +92 -0
  558. package/runtime/worker_watchdog.py +526 -0
  559. package/scripts/__pycache__/audit-published-artifact.cpython-313.pyc +0 -0
  560. package/scripts/__pycache__/check-doc-parity.cpython-313.pyc +0 -0
  561. package/scripts/__pycache__/check-omg-standalone-clean.cpython-313.pyc +0 -0
  562. package/scripts/__pycache__/github_review_helpers.cpython-313.pyc +0 -0
  563. package/scripts/__pycache__/omg.cpython-313.pyc +0 -0
  564. package/scripts/__pycache__/prepare-release-proof-fixtures.cpython-313.pyc +0 -0
  565. package/scripts/__pycache__/sync-release-identity.cpython-313.pyc +0 -0
  566. package/scripts/__pycache__/validate-release-identity.cpython-313.pyc +0 -0
  567. package/scripts/audit-published-artifact.py +59 -0
  568. package/scripts/check-omg-compat-contract-snapshot.py +137 -0
  569. package/scripts/check-omg-contract-snapshot.py +12 -0
  570. package/scripts/check-omg-public-ready.py +273 -0
  571. package/scripts/check-omg-standalone-clean.py +133 -0
  572. package/scripts/emit_host_parity.py +72 -0
  573. package/scripts/legacy_to_omg_migrate.py +29 -0
  574. package/scripts/migrate-legacy.py +464 -0
  575. package/scripts/omc_to_omg_migrate.py +12 -0
  576. package/scripts/omg.py +2962 -0
  577. package/scripts/pre-release-check.sh +38 -0
  578. package/scripts/prepare-release-proof-fixtures.py +602 -0
  579. package/scripts/print-canonical-version.py +80 -0
  580. package/scripts/settings-merge.py +289 -0
  581. package/scripts/sync-release-identity.py +481 -0
  582. package/scripts/validate-release-identity.py +632 -0
  583. package/scripts/verify-no-omc.sh +5 -0
  584. package/scripts/verify-standalone.sh +35 -0
  585. package/settings.json +751 -0
  586. package/tools/__init__.py +2 -0
  587. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  588. package/tools/__pycache__/browser_consent.cpython-313.pyc +0 -0
  589. package/tools/__pycache__/browser_stealth.cpython-313.pyc +0 -0
  590. package/tools/__pycache__/browser_tool.cpython-313.pyc +0 -0
  591. package/tools/__pycache__/changelog_generator.cpython-313.pyc +0 -0
  592. package/tools/__pycache__/commit_splitter.cpython-313.pyc +0 -0
  593. package/tools/__pycache__/config_discovery.cpython-313.pyc +0 -0
  594. package/tools/__pycache__/config_merger.cpython-313.pyc +0 -0
  595. package/tools/__pycache__/dashboard_generator.cpython-313.pyc +0 -0
  596. package/tools/__pycache__/git_inspector.cpython-313.pyc +0 -0
  597. package/tools/__pycache__/lsp_client.cpython-313.pyc +0 -0
  598. package/tools/__pycache__/lsp_operations.cpython-313.pyc +0 -0
  599. package/tools/__pycache__/pr_generator.cpython-313.pyc +0 -0
  600. package/tools/__pycache__/python_repl.cpython-313.pyc +0 -0
  601. package/tools/__pycache__/python_sandbox.cpython-313.pyc +0 -0
  602. package/tools/__pycache__/session_snapshot.cpython-313.pyc +0 -0
  603. package/tools/__pycache__/ssh_manager.cpython-313.pyc +0 -0
  604. package/tools/__pycache__/theme_engine.cpython-313.pyc +0 -0
  605. package/tools/__pycache__/theme_selector.cpython-313.pyc +0 -0
  606. package/tools/__pycache__/web_search.cpython-313.pyc +0 -0
  607. package/tools/browser_consent.py +289 -0
  608. package/tools/browser_stealth.py +481 -0
  609. package/tools/browser_tool.py +448 -0
  610. package/tools/changelog_generator.py +347 -0
  611. package/tools/commit_splitter.py +749 -0
  612. package/tools/config_discovery.py +151 -0
  613. package/tools/config_merger.py +449 -0
  614. package/tools/dashboard_generator.py +300 -0
  615. package/tools/git_inspector.py +298 -0
  616. package/tools/lsp_client.py +275 -0
  617. package/tools/lsp_discovery.py +231 -0
  618. package/tools/lsp_operations.py +392 -0
  619. package/tools/pr_generator.py +404 -0
  620. package/tools/python_repl.py +712 -0
  621. package/tools/python_sandbox.py +768 -0
  622. package/tools/search_providers/__init__.py +77 -0
  623. package/tools/search_providers/__pycache__/__init__.cpython-313.pyc +0 -0
  624. package/tools/search_providers/__pycache__/brave.cpython-313.pyc +0 -0
  625. package/tools/search_providers/__pycache__/exa.cpython-313.pyc +0 -0
  626. package/tools/search_providers/__pycache__/jina.cpython-313.pyc +0 -0
  627. package/tools/search_providers/__pycache__/perplexity.cpython-313.pyc +0 -0
  628. package/tools/search_providers/__pycache__/synthetic.cpython-313.pyc +0 -0
  629. package/tools/search_providers/brave.py +115 -0
  630. package/tools/search_providers/exa.py +116 -0
  631. package/tools/search_providers/jina.py +104 -0
  632. package/tools/search_providers/perplexity.py +139 -0
  633. package/tools/search_providers/synthetic.py +74 -0
  634. package/tools/session_snapshot.py +851 -0
  635. package/tools/ssh_manager.py +912 -0
  636. package/tools/theme_engine.py +296 -0
  637. package/tools/theme_selector.py +137 -0
  638. package/tools/web_search.py +675 -0
@@ -0,0 +1,851 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Session State Snapshot System for OMG
4
+
5
+ Captures `.omg/state/` directory, compresses it, versions snapshots,
6
+ and stores them in `.omg/state/snapshots/`.
7
+
8
+ Feature flag: OMG_SNAPSHOT_ENABLED (default: False)
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import sys
14
+ import tarfile
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional
18
+
19
+ # Lazy import from hooks
20
+ def _get_feature_flag_enabled() -> bool:
21
+ """Check if snapshot feature is enabled."""
22
+ env_val = os.environ.get("OMG_SNAPSHOT_ENABLED", "").lower()
23
+ if env_val in ("0", "false", "no"):
24
+ return False
25
+ if env_val in ("1", "true", "yes"):
26
+ return True
27
+
28
+ # Lazy import from hooks
29
+ hooks_dir = os.path.normpath(
30
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "hooks")
31
+ )
32
+ if hooks_dir not in sys.path:
33
+ sys.path.insert(0, hooks_dir)
34
+ try:
35
+ from _common import get_feature_flag # type: ignore[import-untyped] # pyright: ignore[reportMissingImports]
36
+ return get_feature_flag("SNAPSHOT", default=False)
37
+ except ImportError:
38
+ return False
39
+
40
+
41
+ def _get_atomic_json_write():
42
+ """Lazy-import atomic_json_write from hooks/_common.py."""
43
+ hooks_dir = os.path.normpath(
44
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "hooks")
45
+ )
46
+ if hooks_dir not in sys.path:
47
+ sys.path.insert(0, hooks_dir)
48
+ try:
49
+ from _common import atomic_json_write # type: ignore[import-untyped] # pyright: ignore[reportMissingImports]
50
+ return atomic_json_write
51
+ except ImportError:
52
+ return None
53
+
54
+
55
+ def create_snapshot(name: Optional[str] = None, state_dir: str = ".omg/state") -> Dict[str, Any]:
56
+ """
57
+ Capture `.omg/state/` directory and create a compressed snapshot.
58
+
59
+ Args:
60
+ name: Optional name suffix for the snapshot
61
+ state_dir: Path to the state directory (default: ".omg/state")
62
+
63
+ Returns:
64
+ Snapshot metadata dict with keys: id, name, created_at, files_count, compressed_size, state_dir
65
+ or {"skipped": True} if feature flag is disabled
66
+ """
67
+ if not _get_feature_flag_enabled():
68
+ return {"skipped": True}
69
+
70
+ # Generate snapshot ID
71
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
72
+ snapshot_id = f"{timestamp}_{name}" if name else timestamp
73
+
74
+ # Ensure snapshots directory exists
75
+ snapshots_dir = os.path.join(state_dir, "snapshots")
76
+ os.makedirs(snapshots_dir, exist_ok=True)
77
+
78
+ # Paths for snapshot files
79
+ snapshot_tar_path = os.path.join(snapshots_dir, f"{snapshot_id}.tar.gz")
80
+ snapshot_meta_path = os.path.join(snapshots_dir, f"{snapshot_id}.json")
81
+
82
+ # Files to exclude
83
+ exclude_patterns = {
84
+ "snapshots", # Don't snapshot the snapshots directory itself
85
+ "credentials.enc",
86
+ "credentials.meta",
87
+ }
88
+
89
+ # Create tar.gz archive
90
+ files_count = 0
91
+ try:
92
+ with tarfile.open(snapshot_tar_path, "w:gz") as tar:
93
+ for root, dirs, files in os.walk(state_dir):
94
+ # Filter out excluded directories
95
+ dirs[:] = [d for d in dirs if d not in exclude_patterns]
96
+
97
+ for file in files:
98
+ file_path = os.path.join(root, file)
99
+ # Skip excluded files
100
+ if file in exclude_patterns:
101
+ continue
102
+
103
+ # Calculate arcname (relative path in archive)
104
+ arcname = os.path.relpath(file_path, state_dir)
105
+ tar.add(file_path, arcname=arcname)
106
+ files_count += 1
107
+
108
+ except Exception as e:
109
+ print(f"[OMG] Error creating snapshot: {e}", file=sys.stderr)
110
+ return {"error": str(e)}
111
+
112
+ # Get compressed size
113
+ compressed_size = os.path.getsize(snapshot_tar_path) if os.path.exists(snapshot_tar_path) else 0
114
+
115
+ # Create metadata
116
+ metadata = {
117
+ "id": snapshot_id,
118
+ "name": name or "",
119
+ "created_at": datetime.now().isoformat(),
120
+ "files_count": files_count,
121
+ "compressed_size": compressed_size,
122
+ "state_dir": state_dir,
123
+ }
124
+
125
+ # Write metadata atomically
126
+ atomic_json_write = _get_atomic_json_write()
127
+ if atomic_json_write:
128
+ atomic_json_write(snapshot_meta_path, metadata)
129
+ else:
130
+ # Fallback: write without atomic guarantee
131
+ try:
132
+ with open(snapshot_meta_path, "w", encoding="utf-8") as f:
133
+ json.dump(metadata, f, separators=(",", ":"))
134
+ except Exception as e:
135
+ print(f"[OMG] Error writing metadata: {e}", file=sys.stderr)
136
+
137
+ return metadata
138
+
139
+
140
+ def list_snapshots(state_dir: str = ".omg/state") -> List[Dict[str, Any]]:
141
+ """
142
+ List all available snapshots.
143
+
144
+ Args:
145
+ state_dir: Path to the state directory (default: ".omg/state")
146
+
147
+ Returns:
148
+ List of snapshot metadata dicts, sorted by created_at descending (newest first)
149
+ """
150
+ snapshots_dir = os.path.join(state_dir, "snapshots")
151
+ if not os.path.isdir(snapshots_dir):
152
+ return []
153
+
154
+ snapshots = []
155
+ try:
156
+ for file in os.listdir(snapshots_dir):
157
+ if file.endswith(".json"):
158
+ meta_path = os.path.join(snapshots_dir, file)
159
+ try:
160
+ with open(meta_path, "r", encoding="utf-8") as f:
161
+ metadata = json.load(f)
162
+ snapshots.append(metadata)
163
+ except (json.JSONDecodeError, OSError):
164
+ pass # Skip invalid metadata files
165
+ except OSError:
166
+ pass
167
+
168
+ # Sort by created_at descending (newest first)
169
+ snapshots.sort(key=lambda x: x.get("created_at", ""), reverse=True)
170
+ return snapshots
171
+
172
+
173
+ def restore_snapshot(snapshot_id: str, state_dir: str = ".omg/state") -> bool:
174
+ """
175
+ Restore a snapshot to the state directory.
176
+
177
+ Args:
178
+ snapshot_id: ID of the snapshot to restore
179
+ state_dir: Path to the state directory (default: ".omg/state")
180
+
181
+ Returns:
182
+ True if restored successfully, False if snapshot not found
183
+ """
184
+ snapshots_dir = os.path.join(state_dir, "snapshots")
185
+ snapshot_tar_path = os.path.join(snapshots_dir, f"{snapshot_id}.tar.gz")
186
+
187
+ if not os.path.exists(snapshot_tar_path):
188
+ return False
189
+
190
+ try:
191
+ with tarfile.open(snapshot_tar_path, "r:gz") as tar:
192
+ # Use filter='data' for Python 3.14+ compatibility
193
+ try:
194
+ tar.extractall(path=state_dir, filter='data')
195
+ except TypeError:
196
+ # Fallback for Python < 3.12
197
+ tar.extractall(path=state_dir)
198
+ return True
199
+ except Exception as e:
200
+ print(f"[OMG] Error restoring snapshot: {e}", file=sys.stderr)
201
+ return False
202
+
203
+
204
+ def delete_snapshot(snapshot_id: str, state_dir: str = ".omg/state") -> bool:
205
+ """
206
+ Delete a snapshot.
207
+
208
+ Args:
209
+ snapshot_id: ID of the snapshot to delete
210
+ state_dir: Path to the state directory (default: ".omg/state")
211
+
212
+ Returns:
213
+ True if deleted successfully, False if snapshot not found
214
+ """
215
+ snapshots_dir = os.path.join(state_dir, "snapshots")
216
+ snapshot_tar_path = os.path.join(snapshots_dir, f"{snapshot_id}.tar.gz")
217
+ snapshot_meta_path = os.path.join(snapshots_dir, f"{snapshot_id}.json")
218
+
219
+ deleted = False
220
+ try:
221
+ if os.path.exists(snapshot_tar_path):
222
+ os.remove(snapshot_tar_path)
223
+ deleted = True
224
+ except OSError as e:
225
+ print(f"[OMG] Error deleting snapshot tar: {e}", file=sys.stderr)
226
+
227
+ try:
228
+ if os.path.exists(snapshot_meta_path):
229
+ os.remove(snapshot_meta_path)
230
+ deleted = True
231
+ except OSError as e:
232
+ print(f"[OMG] Error deleting snapshot metadata: {e}", file=sys.stderr)
233
+
234
+ return deleted
235
+
236
+
237
+ def collect_snapshot_signals(state_dir: str = ".omg/state") -> Dict[str, Any]:
238
+ snapshots = list_snapshots(state_dir=state_dir)
239
+ latest = snapshots[0] if snapshots else {}
240
+ return {
241
+ "snapshot_count": len(snapshots),
242
+ "latest_snapshot_id": str(latest.get("id", "")) if isinstance(latest, dict) else "",
243
+ }
244
+
245
+
246
+ # --- Branch / Fork API ---
247
+
248
+
249
+ def _get_branching_flag_enabled() -> bool:
250
+ """Check if branching feature is enabled."""
251
+ env_val = os.environ.get("OMG_BRANCHING_ENABLED", "").lower()
252
+ if env_val in ("0", "false", "no"):
253
+ return False
254
+ if env_val in ("1", "true", "yes"):
255
+ return True
256
+
257
+ # Lazy import from hooks
258
+ hooks_dir = os.path.normpath(
259
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "hooks")
260
+ )
261
+ if hooks_dir not in sys.path:
262
+ sys.path.insert(0, hooks_dir)
263
+ try:
264
+ from _common import get_feature_flag # type: ignore[import-untyped] # pyright: ignore[reportMissingImports]
265
+ return get_feature_flag("BRANCHING", default=False)
266
+ except ImportError:
267
+ return False
268
+
269
+
270
+ def create_branch(
271
+ name: str,
272
+ from_snapshot_id: Optional[str] = None,
273
+ state_dir: str = ".omg/state",
274
+ ) -> Dict[str, Any]:
275
+ """
276
+ Create a named branch from a snapshot or the current state.
277
+
278
+ Args:
279
+ name: Branch name (must be non-empty, no slashes)
280
+ from_snapshot_id: Optional snapshot ID to branch from.
281
+ If provided, restores that snapshot first.
282
+ Otherwise, creates a new snapshot automatically.
283
+ state_dir: Path to the state directory (default: ".omg/state")
284
+
285
+ Returns:
286
+ Branch metadata dict with keys: name, snapshot_id, created_at,
287
+ parent_branch, status.
288
+ Or {"skipped": True} if feature flag is disabled.
289
+ """
290
+ if not _get_branching_flag_enabled():
291
+ return {"skipped": True}
292
+
293
+ if not name or "/" in name:
294
+ return {"error": "Invalid branch name: must be non-empty with no slashes"}
295
+
296
+ # Resolve source snapshot
297
+ if from_snapshot_id:
298
+ # Verify snapshot exists before restoring
299
+ snapshots_dir = os.path.join(state_dir, "snapshots")
300
+ tar_path = os.path.join(snapshots_dir, f"{from_snapshot_id}.tar.gz")
301
+ if not os.path.exists(tar_path):
302
+ return {"error": f"Snapshot not found: {from_snapshot_id}"}
303
+ restore_snapshot(from_snapshot_id, state_dir=state_dir)
304
+ snapshot_id = from_snapshot_id
305
+ else:
306
+ # Create a fresh snapshot for this branch
307
+ snap = create_snapshot(name=name, state_dir=state_dir)
308
+ if snap.get("error") or snap.get("skipped"):
309
+ return snap
310
+ snapshot_id = snap["id"]
311
+
312
+ # Read current branch (parent)
313
+ current_branch_path = os.path.join(state_dir, "current_branch.json")
314
+ parent_branch: Optional[str] = None
315
+ if os.path.exists(current_branch_path):
316
+ try:
317
+ with open(current_branch_path, "r", encoding="utf-8") as f:
318
+ cb = json.load(f)
319
+ parent_branch = cb.get("name")
320
+ except (json.JSONDecodeError, OSError):
321
+ pass
322
+
323
+ # Build branch metadata
324
+ metadata: Dict[str, Any] = {
325
+ "name": name,
326
+ "snapshot_id": snapshot_id,
327
+ "created_at": datetime.now().isoformat(),
328
+ "parent_branch": parent_branch,
329
+ "status": "active",
330
+ }
331
+
332
+ # Write branch metadata
333
+ branches_dir = os.path.join(state_dir, "branches")
334
+ os.makedirs(branches_dir, exist_ok=True)
335
+ branch_path = os.path.join(branches_dir, f"{name}.json")
336
+
337
+ atomic_json_write = _get_atomic_json_write()
338
+ if atomic_json_write:
339
+ atomic_json_write(branch_path, metadata)
340
+ else:
341
+ try:
342
+ with open(branch_path, "w", encoding="utf-8") as f:
343
+ json.dump(metadata, f, separators=(",", ":"))
344
+ except Exception as e:
345
+ print(f"[OMG] Error writing branch metadata: {e}", file=sys.stderr)
346
+ return {"error": str(e)}
347
+
348
+ # Update current branch tracker
349
+ _update_current_branch(name, state_dir=state_dir)
350
+
351
+ return metadata
352
+
353
+
354
+ def list_branches(state_dir: str = ".omg/state") -> List[Dict[str, Any]]:
355
+ """
356
+ List all branches with metadata.
357
+
358
+ Args:
359
+ state_dir: Path to the state directory (default: ".omg/state")
360
+
361
+ Returns:
362
+ List of branch metadata dicts, sorted by created_at descending (newest first)
363
+ """
364
+ branches_dir = os.path.join(state_dir, "branches")
365
+ if not os.path.isdir(branches_dir):
366
+ return []
367
+
368
+ branches: List[Dict[str, Any]] = []
369
+ try:
370
+ for file in os.listdir(branches_dir):
371
+ if file.endswith(".json"):
372
+ branch_path = os.path.join(branches_dir, file)
373
+ try:
374
+ with open(branch_path, "r", encoding="utf-8") as f:
375
+ metadata = json.load(f)
376
+ branches.append(metadata)
377
+ except (json.JSONDecodeError, OSError):
378
+ pass # Skip invalid metadata files
379
+ except OSError:
380
+ pass
381
+
382
+ # Sort by created_at descending (newest first)
383
+ branches.sort(key=lambda x: x.get("created_at", ""), reverse=True)
384
+ return branches
385
+
386
+
387
+ def switch_branch(name: str, state_dir: str = ".omg/state") -> bool:
388
+ """
389
+ Switch to a named branch by restoring its snapshot.
390
+
391
+ Args:
392
+ name: Branch name to switch to
393
+ state_dir: Path to the state directory (default: ".omg/state")
394
+
395
+ Returns:
396
+ True if switched successfully, False otherwise
397
+ """
398
+ branches_dir = os.path.join(state_dir, "branches")
399
+ branch_path = os.path.join(branches_dir, f"{name}.json")
400
+
401
+ if not os.path.exists(branch_path):
402
+ return False
403
+
404
+ try:
405
+ with open(branch_path, "r", encoding="utf-8") as f:
406
+ branch_meta = json.load(f)
407
+ except (json.JSONDecodeError, OSError):
408
+ return False
409
+
410
+ snapshot_id = branch_meta.get("snapshot_id")
411
+ if not snapshot_id:
412
+ return False
413
+
414
+ if not restore_snapshot(snapshot_id, state_dir=state_dir):
415
+ return False
416
+
417
+ _update_current_branch(name, state_dir=state_dir)
418
+ return True
419
+
420
+
421
+ def _update_current_branch(name: str, state_dir: str = ".omg/state") -> None:
422
+ """Update the current branch tracker file."""
423
+ current_branch_path = os.path.join(state_dir, "current_branch.json")
424
+ data = {"name": name, "switched_at": datetime.now().isoformat()}
425
+ atomic_json_write = _get_atomic_json_write()
426
+ if atomic_json_write:
427
+ atomic_json_write(current_branch_path, data)
428
+ else:
429
+ try:
430
+ with open(current_branch_path, "w", encoding="utf-8") as f:
431
+ json.dump(data, f, separators=(",", ":"))
432
+ except Exception as e:
433
+ print(f"[OMG] Error updating current branch: {e}", file=sys.stderr)
434
+
435
+
436
+ # --- Merge API ---
437
+
438
+
439
+ def _get_merge_flag_enabled() -> bool:
440
+ """Check if merge feature is enabled."""
441
+ env_val = os.environ.get("OMG_MERGE_ENABLED", "").lower()
442
+ if env_val in ("0", "false", "no"):
443
+ return False
444
+ if env_val in ("1", "true", "yes"):
445
+ return True
446
+
447
+ # Lazy import from hooks
448
+ hooks_dir = os.path.normpath(
449
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "hooks")
450
+ )
451
+ if hooks_dir not in sys.path:
452
+ sys.path.insert(0, hooks_dir)
453
+ try:
454
+ from _common import get_feature_flag # type: ignore[import-untyped] # pyright: ignore[reportMissingImports]
455
+ return get_feature_flag("MERGE", default=False)
456
+ except ImportError:
457
+ return False
458
+
459
+
460
+ def _load_branch_state(branch_name: str, state_dir: str = ".omg/state") -> Optional[Dict[str, Any]]:
461
+ """Load a branch's metadata as a flat state dict.
462
+
463
+ Args:
464
+ branch_name: Name of the branch to load
465
+ state_dir: Path to the state directory
466
+
467
+ Returns:
468
+ Branch metadata dict, or None if branch does not exist or is invalid.
469
+ """
470
+ branch_path = os.path.join(state_dir, "branches", f"{branch_name}.json")
471
+ if not os.path.exists(branch_path):
472
+ return None
473
+ try:
474
+ with open(branch_path, "r", encoding="utf-8") as f:
475
+ return json.load(f)
476
+ except (json.JSONDecodeError, OSError):
477
+ return None
478
+
479
+
480
+ def detect_merge_conflicts(
481
+ source_state: Dict[str, Any], target_state: Dict[str, Any]
482
+ ) -> List[Dict[str, Any]]:
483
+ """Compare two state dicts and find keys where both sides have different values.
484
+
485
+ Args:
486
+ source_state: State dict from the source branch
487
+ target_state: State dict from the target branch
488
+
489
+ Returns:
490
+ List of conflict dicts with keys: key, source_value, target_value, conflict_type.
491
+ conflict_type is "value_conflict" when both sides changed the same key
492
+ to different values.
493
+ """
494
+ conflicts: List[Dict[str, Any]] = []
495
+ # Find keys present in both dicts with different values
496
+ common_keys = set(source_state.keys()) & set(target_state.keys())
497
+ for key in sorted(common_keys):
498
+ source_val = source_state[key]
499
+ target_val = target_state[key]
500
+ if source_val != target_val:
501
+ conflicts.append({
502
+ "key": key,
503
+ "source_value": source_val,
504
+ "target_value": target_val,
505
+ "conflict_type": "value_conflict",
506
+ })
507
+ return conflicts
508
+
509
+
510
+ _BRANCH_META_KEYS = frozenset({
511
+ "name", "created_at", "snapshot_id", "parent_branch",
512
+ "status", "switched_at", "merged_at", "merged_into",
513
+ })
514
+
515
+
516
+ def _strip_branch_meta(state: Dict[str, Any]) -> Dict[str, Any]:
517
+ """Return *state* without branch-level metadata keys."""
518
+ return {k: v for k, v in state.items() if k not in _BRANCH_META_KEYS}
519
+
520
+
521
+ def preview_merge(
522
+ source_branch: str,
523
+ target_branch: str = "main",
524
+ state_dir: str = ".omg/state",
525
+ ) -> Dict[str, Any]:
526
+ """Preview a merge without applying changes.
527
+
528
+ Loads both branch snapshot states (as flat JSON dicts from snapshot
529
+ metadata), strips branch-level metadata keys that always differ,
530
+ and detects conflicts on the remaining user-state keys.
531
+
532
+ Args:
533
+ source_branch: Branch to merge from
534
+ target_branch: Branch to merge into (default: "main")
535
+ state_dir: Path to the state directory
536
+
537
+ Returns:
538
+ Preview dict with keys: source, target, conflicts, changes, preview.
539
+ Or {"skipped": True} if feature flag is disabled.
540
+ Or {"error": ...} if a branch cannot be found.
541
+ """
542
+ if not _get_merge_flag_enabled():
543
+ return {"skipped": True}
544
+
545
+ source_state = _load_branch_state(source_branch, state_dir=state_dir)
546
+ if source_state is None:
547
+ return {"error": f"Source branch not found: {source_branch}"}
548
+
549
+ target_state = _load_branch_state(target_branch, state_dir=state_dir)
550
+ if target_state is None:
551
+ return {"error": f"Target branch not found: {target_branch}"}
552
+
553
+ source_user = _strip_branch_meta(source_state)
554
+ target_user = _strip_branch_meta(target_state)
555
+ conflicts = detect_merge_conflicts(source_user, target_user)
556
+
557
+ source_only_keys = set(source_user.keys()) - set(target_user.keys())
558
+ changes = len(source_only_keys) + len(conflicts)
559
+
560
+ return {
561
+ "source": source_branch,
562
+ "target": target_branch,
563
+ "conflicts": conflicts,
564
+ "changes": changes,
565
+ "preview": True,
566
+ }
567
+
568
+
569
+ def merge_branch(
570
+ source_branch: str,
571
+ target_branch: str = "main",
572
+ state_dir: str = ".omg/state",
573
+ ) -> Dict[str, Any]:
574
+ """Merge source branch state into target branch.
575
+
576
+ Uses last-write-wins strategy when there are no conflicts.
577
+ If conflicts exist, the merge is aborted and conflicts are returned.
578
+
579
+ Args:
580
+ source_branch: Branch to merge from
581
+ target_branch: Branch to merge into (default: "main")
582
+ state_dir: Path to the state directory
583
+
584
+ Returns:
585
+ Result dict with keys: merged, conflicts, changes_applied.
586
+ Or {"skipped": True} if feature flag is disabled.
587
+ Or {"error": ...} on failure.
588
+ """
589
+ if not _get_merge_flag_enabled():
590
+ return {"skipped": True}
591
+
592
+ source_state = _load_branch_state(source_branch, state_dir=state_dir)
593
+ if source_state is None:
594
+ return {"error": f"Source branch not found: {source_branch}"}
595
+
596
+ target_state = _load_branch_state(target_branch, state_dir=state_dir)
597
+ if target_state is None:
598
+ return {"error": f"Target branch not found: {target_branch}"}
599
+
600
+ source_user = _strip_branch_meta(source_state)
601
+ target_user = _strip_branch_meta(target_state)
602
+ conflicts = detect_merge_conflicts(source_user, target_user)
603
+
604
+ if conflicts:
605
+ return {
606
+ "merged": False,
607
+ "conflicts": conflicts,
608
+ "changes_applied": 0,
609
+ }
610
+
611
+ merged_state = {**target_state, **source_state}
612
+ merged_state["name"] = target_branch
613
+ merged_state["status"] = "active"
614
+
615
+ source_only_keys = set(source_user.keys()) - set(target_user.keys())
616
+ changes_applied = len(source_only_keys)
617
+
618
+ # Write merged state to target branch file
619
+ target_branch_path = os.path.join(state_dir, "branches", f"{target_branch}.json")
620
+ atomic_json_write = _get_atomic_json_write()
621
+ if atomic_json_write:
622
+ atomic_json_write(target_branch_path, merged_state)
623
+ else:
624
+ try:
625
+ os.makedirs(os.path.dirname(target_branch_path), exist_ok=True)
626
+ with open(target_branch_path, "w", encoding="utf-8") as f:
627
+ json.dump(merged_state, f, separators=(",", ":"))
628
+ except Exception as e:
629
+ return {"error": f"Failed to write merged state: {e}"}
630
+
631
+ # Mark source branch as merged
632
+ source_branch_path = os.path.join(state_dir, "branches", f"{source_branch}.json")
633
+ if source_state:
634
+ source_state["status"] = "merged"
635
+ source_state["merged_into"] = target_branch
636
+ source_state["merged_at"] = datetime.now().isoformat()
637
+ if atomic_json_write:
638
+ atomic_json_write(source_branch_path, source_state)
639
+ else:
640
+ try:
641
+ with open(source_branch_path, "w", encoding="utf-8") as f:
642
+ json.dump(source_state, f, separators=(",", ":"))
643
+ except Exception as e:
644
+ print(f"[OMG] Error updating source branch status: {e}", file=sys.stderr)
645
+
646
+ # Update current_branch.json to reflect merged state
647
+ _update_current_branch(target_branch, state_dir=state_dir)
648
+
649
+ return {
650
+ "merged": True,
651
+ "conflicts": [],
652
+ "changes_applied": changes_applied,
653
+ }
654
+
655
+ def fork_branch(
656
+ from_snapshot_id: str,
657
+ name: str,
658
+ state_dir: str = ".omg/state",
659
+ ) -> Dict[str, Any]:
660
+ """Fork a new branch from a specific snapshot checkpoint.
661
+
662
+ This is a convenience wrapper around ``create_branch`` that always
663
+ requires a source snapshot ID.
664
+
665
+ Args:
666
+ from_snapshot_id: Snapshot ID to fork from (required, non-empty).
667
+ name: Name for the new branch (required, non-empty, no slashes).
668
+ state_dir: Path to the state directory (default: ".omg/state").
669
+
670
+ Returns:
671
+ Branch metadata dict on success, ``{"skipped": True}`` if feature
672
+ flag is disabled, or ``{"error": ...}`` on failure.
673
+ """
674
+ if not _get_branching_flag_enabled():
675
+ return {"skipped": True}
676
+
677
+ if not from_snapshot_id:
678
+ return {"error": "fork_branch requires a non-empty snapshot ID"}
679
+ if not name or "/" in name:
680
+ return {"error": "Invalid branch name: must be non-empty with no slashes"}
681
+
682
+ return create_branch(name, from_snapshot_id=from_snapshot_id, state_dir=state_dir)
683
+
684
+
685
+ # Public alias expected by callers (canonical name is preview_merge)
686
+ merge_preview = preview_merge
687
+
688
+
689
+ def get_status(state_dir: str = ".omg/state") -> Dict[str, Any]:
690
+ """
691
+ Get the current branch name and total snapshot count.
692
+
693
+ Args:
694
+ state_dir: Path to the state directory (default: ".omg/state")
695
+
696
+ Returns:
697
+ Dict with keys: current_branch, snapshot_count
698
+ """
699
+ # Get current branch
700
+ current_branch = None
701
+ current_branch_path = os.path.join(state_dir, "current_branch.json")
702
+ if os.path.exists(current_branch_path):
703
+ try:
704
+ with open(current_branch_path, "r", encoding="utf-8") as f:
705
+ cb = json.load(f)
706
+ current_branch = cb.get("name")
707
+ except (json.JSONDecodeError, OSError):
708
+ pass
709
+
710
+ # Get snapshot count
711
+ snapshots = list_snapshots(state_dir=state_dir)
712
+ snapshot_count = len(snapshots)
713
+
714
+ return {
715
+ "current_branch": current_branch,
716
+ "snapshot_count": snapshot_count,
717
+ }
718
+
719
+
720
+ def main():
721
+ """CLI entry point."""
722
+ state_dir = os.environ.get("OMG_STATE_DIR", ".omg/state")
723
+
724
+ if len(sys.argv) < 2 or sys.argv[1] in ("--help", "-h"):
725
+ _dest = sys.stdout if (len(sys.argv) > 1 and sys.argv[1] in ("--help", "-h")) else sys.stderr
726
+ _code = 0 if _dest is sys.stdout else 1
727
+ print(
728
+ "Usage: python3 session_snapshot.py <command> [options]",
729
+ file=_dest,
730
+ )
731
+ print("Commands:", file=_dest)
732
+ print(" status Show current branch and snapshot count", file=_dest)
733
+ print(" create [--name NAME] Create a snapshot", file=_dest)
734
+ print(" list List all snapshots", file=_dest)
735
+ print(" restore <snapshot_id> Restore a snapshot", file=_dest)
736
+ print(" delete <snapshot_id> Delete a snapshot", file=_dest)
737
+ print(" branch <name> Create a branch", file=_dest)
738
+ print(" branches List all branches", file=_dest)
739
+ print(" switch <name> Switch to a branch", file=_dest)
740
+ print(" fork --from <snapshot_id> --name <name> Fork from snapshot", file=_dest)
741
+ print(" merge <source> [--into <target>] Merge branches", file=_dest)
742
+ print(" merge-preview <source> [--into <target>] Preview merge", file=_dest)
743
+ sys.exit(_code)
744
+
745
+ command = sys.argv[1]
746
+
747
+ if command == "status":
748
+ result = get_status(state_dir=state_dir)
749
+ print(json.dumps(result, indent=2))
750
+
751
+ elif command == "create":
752
+ name = None
753
+ if len(sys.argv) > 3 and sys.argv[2] == "--name":
754
+ name = sys.argv[3]
755
+ result = create_snapshot(name=name, state_dir=state_dir)
756
+ print(json.dumps(result, indent=2))
757
+
758
+ elif command == "list":
759
+ snapshots = list_snapshots(state_dir=state_dir)
760
+ print(json.dumps(snapshots, indent=2))
761
+
762
+ elif command == "restore":
763
+ if len(sys.argv) < 3:
764
+ print("Usage: python3 session_snapshot.py restore <snapshot_id>", file=sys.stderr)
765
+ sys.exit(1)
766
+ snapshot_id = sys.argv[2]
767
+ success = restore_snapshot(snapshot_id, state_dir=state_dir)
768
+ result = {"success": success, "snapshot_id": snapshot_id}
769
+ print(json.dumps(result, indent=2))
770
+
771
+ elif command == "delete":
772
+ if len(sys.argv) < 3:
773
+ print("Usage: python3 session_snapshot.py delete <snapshot_id>", file=sys.stderr)
774
+ sys.exit(1)
775
+ snapshot_id = sys.argv[2]
776
+ success = delete_snapshot(snapshot_id, state_dir=state_dir)
777
+ result = {"success": success, "snapshot_id": snapshot_id}
778
+ print(json.dumps(result, indent=2))
779
+
780
+ elif command == "branch":
781
+ if len(sys.argv) < 3:
782
+ print("Usage: python3 session_snapshot.py branch <name> [--from <snapshot_id>]", file=sys.stderr)
783
+ sys.exit(1)
784
+ branch_name = sys.argv[2]
785
+ from_id = None
786
+ if len(sys.argv) > 4 and sys.argv[3] == "--from":
787
+ from_id = sys.argv[4]
788
+ result = create_branch(branch_name, from_snapshot_id=from_id, state_dir=state_dir)
789
+ print(json.dumps(result, indent=2))
790
+
791
+ elif command == "branches":
792
+ branches = list_branches(state_dir=state_dir)
793
+ print(json.dumps(branches, indent=2))
794
+
795
+ elif command == "switch":
796
+ if len(sys.argv) < 3:
797
+ print("Usage: python3 session_snapshot.py switch <name>", file=sys.stderr)
798
+ sys.exit(1)
799
+ branch_name = sys.argv[2]
800
+ success = switch_branch(branch_name, state_dir=state_dir)
801
+ result = {"success": success, "branch": branch_name}
802
+ print(json.dumps(result, indent=2))
803
+
804
+ elif command == "merge":
805
+ if len(sys.argv) < 3:
806
+ print("Usage: python3 session_snapshot.py merge <source> [--into <target>]", file=sys.stderr)
807
+ sys.exit(1)
808
+ source = sys.argv[2]
809
+ target = "main"
810
+ if len(sys.argv) > 4 and sys.argv[3] == "--into":
811
+ target = sys.argv[4]
812
+ result = merge_branch(source, target_branch=target, state_dir=state_dir)
813
+ print(json.dumps(result, indent=2))
814
+
815
+ elif command == "fork":
816
+ from_id = None
817
+ fork_name = None
818
+ i = 2
819
+ while i < len(sys.argv):
820
+ if sys.argv[i] == "--from" and i + 1 < len(sys.argv):
821
+ from_id = sys.argv[i + 1]
822
+ i += 2
823
+ elif sys.argv[i] == "--name" and i + 1 < len(sys.argv):
824
+ fork_name = sys.argv[i + 1]
825
+ i += 2
826
+ else:
827
+ i += 1
828
+ if not from_id or not fork_name:
829
+ print("Usage: python3 session_snapshot.py fork --from <snapshot_id> --name <name>", file=sys.stderr)
830
+ sys.exit(1)
831
+ result = fork_branch(from_snapshot_id=from_id, name=fork_name, state_dir=state_dir)
832
+ print(json.dumps(result, indent=2))
833
+
834
+ elif command == "merge-preview":
835
+ if len(sys.argv) < 3:
836
+ print("Usage: python3 session_snapshot.py merge-preview <source> [--into <target>]", file=sys.stderr)
837
+ sys.exit(1)
838
+ source = sys.argv[2]
839
+ target = "main"
840
+ if len(sys.argv) > 4 and sys.argv[3] == "--into":
841
+ target = sys.argv[4]
842
+ result = preview_merge(source, target_branch=target, state_dir=state_dir)
843
+ print(json.dumps(result, indent=2))
844
+
845
+ else:
846
+ print(f"Unknown command: {command}", file=sys.stderr)
847
+ sys.exit(1)
848
+
849
+
850
+ if __name__ == "__main__":
851
+ main()