prompt_objects 0.5.0 → 0.7.0

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +142 -0
  3. data/README.md +211 -19
  4. data/exe/poop +5 -0
  5. data/exe/prompt_objects +1 -1108
  6. data/exe/prompt_objects_mcp +5 -3
  7. data/lib/prompt_objects/capability.rb +33 -0
  8. data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
  9. data/lib/prompt_objects/cli/env_command.rb +435 -0
  10. data/lib/prompt_objects/cli/events_command.rb +145 -0
  11. data/lib/prompt_objects/cli/explore_command.rb +155 -0
  12. data/lib/prompt_objects/cli/helpers.rb +91 -0
  13. data/lib/prompt_objects/cli/message_command.rb +216 -0
  14. data/lib/prompt_objects/cli/repl_command.rb +366 -0
  15. data/lib/prompt_objects/cli/serve_command.rb +162 -0
  16. data/lib/prompt_objects/cli.rb +64 -422
  17. data/lib/prompt_objects/connectors/base.rb +16 -29
  18. data/lib/prompt_objects/connectors/mcp.rb +82 -98
  19. data/lib/prompt_objects/domain/records.rb +45 -0
  20. data/lib/prompt_objects/environment/exporter.rb +5 -4
  21. data/lib/prompt_objects/environment/git.rb +21 -1
  22. data/lib/prompt_objects/environment/importer.rb +5 -4
  23. data/lib/prompt_objects/environment/manager.rb +44 -6
  24. data/lib/prompt_objects/environment/manifest.rb +26 -4
  25. data/lib/prompt_objects/environment.rb +1350 -18
  26. data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
  27. data/lib/prompt_objects/execution/context.rb +137 -0
  28. data/lib/prompt_objects/execution/contracts.rb +59 -0
  29. data/lib/prompt_objects/execution/permit_pool.rb +49 -0
  30. data/lib/prompt_objects/execution/policy.rb +47 -0
  31. data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
  32. data/lib/prompt_objects/execution/runner.rb +498 -0
  33. data/lib/prompt_objects/execution/scheduler.rb +325 -0
  34. data/lib/prompt_objects/llm/factory.rb +4 -4
  35. data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
  36. data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
  37. data/lib/prompt_objects/llm/pricing.rb +9 -2
  38. data/lib/prompt_objects/llm/response.rb +9 -4
  39. data/lib/prompt_objects/loader.rb +28 -1
  40. data/lib/prompt_objects/mcp/server.rb +6 -3
  41. data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
  42. data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
  43. data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
  44. data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
  45. data/lib/prompt_objects/operations/diagnostics.rb +115 -0
  46. data/lib/prompt_objects/operations/retention.rb +75 -0
  47. data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
  48. data/lib/prompt_objects/primitives/http_get.rb +2 -0
  49. data/lib/prompt_objects/primitives/list_files.rb +2 -0
  50. data/lib/prompt_objects/primitives/read_file.rb +2 -0
  51. data/lib/prompt_objects/primitives/think.rb +36 -0
  52. data/lib/prompt_objects/primitives/write_file.rb +9 -0
  53. data/lib/prompt_objects/prompt_object.rb +190 -432
  54. data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
  55. data/lib/prompt_objects/repositories/base_repository.rb +37 -0
  56. data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
  57. data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
  58. data/lib/prompt_objects/repositories/event_repository.rb +105 -0
  59. data/lib/prompt_objects/repositories/message_repository.rb +73 -0
  60. data/lib/prompt_objects/repositories/run_repository.rb +254 -0
  61. data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
  62. data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
  63. data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
  64. data/lib/prompt_objects/server/api/routes.rb +561 -52
  65. data/lib/prompt_objects/server/app.rb +134 -1
  66. data/lib/prompt_objects/server/file_watcher.rb +9 -0
  67. data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
  68. data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
  69. data/lib/prompt_objects/server/public/index.html +2 -2
  70. data/lib/prompt_objects/server/websocket_handler.rb +202 -207
  71. data/lib/prompt_objects/server.rb +170 -1
  72. data/lib/prompt_objects/service.rb +129 -0
  73. data/lib/prompt_objects/session/store.rb +774 -15
  74. data/lib/prompt_objects/universal/add_capability.rb +1 -1
  75. data/lib/prompt_objects/universal/create_capability.rb +4 -2
  76. data/lib/prompt_objects/universal/delete_env_data.rb +78 -0
  77. data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
  78. data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
  79. data/lib/prompt_objects/universal/get_env_data.rb +58 -0
  80. data/lib/prompt_objects/universal/get_run_status.rb +30 -0
  81. data/lib/prompt_objects/universal/join_runs.rb +35 -0
  82. data/lib/prompt_objects/universal/list_env_data.rb +57 -0
  83. data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
  84. data/lib/prompt_objects/universal/render_artifact.rb +91 -0
  85. data/lib/prompt_objects/universal/request_primitive.rb +1 -1
  86. data/lib/prompt_objects/universal/set_env_data.rb +101 -0
  87. data/lib/prompt_objects/version.rb +5 -0
  88. data/lib/prompt_objects.rb +36 -6
  89. data/objects/coordinator.md +8 -4
  90. data/templates/arc-agi-1/objects/data_manager.md +2 -2
  91. data/templates/arc-agi-1/objects/observer.md +6 -2
  92. data/templates/arc-agi-1/objects/solver.md +14 -4
  93. data/templates/arc-agi-1/objects/verifier.md +6 -2
  94. data/templates/arc-agi-3/manifest.yml +19 -0
  95. data/templates/arc-agi-3/objects/analyst.md +79 -0
  96. data/templates/arc-agi-3/objects/player.md +76 -0
  97. data/templates/arc-agi-3/objects/strategist.md +97 -0
  98. data/templates/arc-agi-3/primitives/action_log.rb +133 -0
  99. data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
  100. data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
  101. data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
  102. data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
  103. data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
  104. data/templates/basic/objects/basic.md +11 -3
  105. data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
  106. data/templates/chiptune/manifest.yml +20 -0
  107. data/templates/chiptune/objects/conductor.md +184 -0
  108. data/templates/chiptune/objects/sonic_pi.md +65 -0
  109. data/templates/chiptune/objects/voice_bass.md +44 -0
  110. data/templates/chiptune/objects/voice_lead.md +44 -0
  111. data/templates/chiptune/objects/voice_pad.md +43 -0
  112. data/templates/chiptune/objects/voice_perc.md +45 -0
  113. data/templates/chiptune/primitives/play_note.rb +59 -0
  114. data/templates/chiptune/primitives/play_sequence.rb +55 -0
  115. data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
  116. data/templates/chiptune/primitives/set_synth.rb +57 -0
  117. data/templates/chiptune/primitives/set_tempo.rb +45 -0
  118. data/templates/chiptune/primitives/stop_voice.rb +46 -0
  119. data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
  120. data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  121. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  122. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  123. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  124. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  125. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  126. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  127. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  128. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  129. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  130. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  131. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  132. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  133. data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  134. data/templates/developer/objects/code_reviewer.md +1 -1
  135. data/templates/developer/objects/coordinator.md +16 -3
  136. data/templates/developer/objects/debugger.md +3 -3
  137. data/templates/empty/objects/assistant.md +10 -5
  138. data/templates/minimal/objects/assistant.md +10 -5
  139. data/templates/music/extras/sonic_pi_boot.rb +220 -0
  140. data/templates/music/manifest.yml +16 -0
  141. data/templates/music/objects/conductor.md +154 -0
  142. data/templates/music/objects/sonic_pi.md +65 -0
  143. data/templates/music/primitives/play_note.rb +55 -0
  144. data/templates/music/primitives/play_sequence.rb +51 -0
  145. data/templates/music/primitives/query_sonic_pi.rb +139 -0
  146. data/templates/music/primitives/set_synth.rb +53 -0
  147. data/templates/music/primitives/set_tempo.rb +43 -0
  148. data/templates/music/primitives/stop_voice.rb +44 -0
  149. data/templates/music/primitives/support/osc_bridge.rb +33 -0
  150. data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  151. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  152. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  153. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  154. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  155. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  156. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  157. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  158. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  159. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  160. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  161. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  162. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  163. data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  164. data/templates/music-beginner/README.md +100 -0
  165. data/templates/music-beginner/extras/README.md +27 -0
  166. data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
  167. data/templates/music-beginner/manifest.yml +25 -0
  168. data/templates/music-beginner/objects/conductor.md +222 -0
  169. data/templates/music-beginner/objects/engine_setup.md +54 -0
  170. data/templates/music-beginner/objects/voice_bass.md +43 -0
  171. data/templates/music-beginner/objects/voice_keys.md +48 -0
  172. data/templates/music-beginner/objects/voice_pad.md +46 -0
  173. data/templates/music-beginner/objects/voice_percussion.md +45 -0
  174. data/templates/music-beginner/objects/voice_texture.md +37 -0
  175. data/templates/music-beginner/primitives/play_pattern.rb +118 -0
  176. data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
  177. data/templates/music-beginner/primitives/set_instrument.rb +47 -0
  178. data/templates/music-beginner/primitives/set_tempo.rb +43 -0
  179. data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
  180. data/templates/music-beginner/primitives/stop_voice.rb +37 -0
  181. data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
  182. data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  183. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  184. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  185. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  186. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  187. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  188. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  189. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  190. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  191. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  192. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  193. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  194. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  195. data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  196. data/templates/music-pro/extras/AUDITION.md +100 -0
  197. data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
  198. data/templates/music-pro/manifest.yml +19 -0
  199. data/templates/music-pro/objects/conductor.md +126 -0
  200. data/templates/music-pro/objects/sonic_pi.md +35 -0
  201. data/templates/music-pro/objects/voice_harmony.md +48 -0
  202. data/templates/music-pro/objects/voice_rhythm.md +43 -0
  203. data/templates/music-pro/objects/voice_texture.md +42 -0
  204. data/templates/music-pro/primitives/define_patch.rb +46 -0
  205. data/templates/music-pro/primitives/query_music_engine.rb +69 -0
  206. data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
  207. data/templates/music-pro/primitives/set_tempo.rb +31 -0
  208. data/templates/music-pro/primitives/stop_voice.rb +31 -0
  209. data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
  210. data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
  211. data/templates/music-pro/primitives/trigger_event.rb +44 -0
  212. data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
  213. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
  214. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
  215. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
  216. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
  217. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
  218. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
  219. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
  220. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
  221. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
  222. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
  223. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
  224. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
  225. data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
  226. data/templates/writer/objects/coordinator.md +14 -3
  227. data/templates/writer/objects/editor.md +1 -1
  228. data/templates/writer/objects/researcher.md +2 -2
  229. data/tools/thread-explorer.html +28 -1
  230. metadata +175 -65
  231. data/CLAUDE.md +0 -176
  232. data/Gemfile +0 -10
  233. data/Gemfile.lock +0 -233
  234. data/IMPLEMENTATION_PLAN.md +0 -1073
  235. data/Rakefile +0 -27
  236. data/design-doc-v2.md +0 -1232
  237. data/frontend/.gitignore +0 -3
  238. data/frontend/index.html +0 -17
  239. data/frontend/package-lock.json +0 -4540
  240. data/frontend/package.json +0 -36
  241. data/frontend/postcss.config.js +0 -6
  242. data/frontend/src/App.tsx +0 -103
  243. data/frontend/src/canvas/CanvasView.tsx +0 -113
  244. data/frontend/src/canvas/ForceLayout.ts +0 -115
  245. data/frontend/src/canvas/SceneManager.ts +0 -587
  246. data/frontend/src/canvas/canvasStore.ts +0 -47
  247. data/frontend/src/canvas/constants.ts +0 -95
  248. data/frontend/src/canvas/controls/CameraControls.ts +0 -98
  249. data/frontend/src/canvas/edges/MessageArc.ts +0 -149
  250. data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
  251. data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
  252. data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
  253. data/frontend/src/canvas/nodes/PONode.ts +0 -249
  254. data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
  255. data/frontend/src/canvas/types.ts +0 -24
  256. data/frontend/src/components/ContextMenu.tsx +0 -68
  257. data/frontend/src/components/Inspector.tsx +0 -232
  258. data/frontend/src/components/MarkdownMessage.tsx +0 -155
  259. data/frontend/src/components/MethodList.tsx +0 -90
  260. data/frontend/src/components/ModelSelector.tsx +0 -115
  261. data/frontend/src/components/NotificationPanel.tsx +0 -130
  262. data/frontend/src/components/ObjectList.tsx +0 -78
  263. data/frontend/src/components/PaneSlot.tsx +0 -30
  264. data/frontend/src/components/SourcePane.tsx +0 -202
  265. data/frontend/src/components/SystemBar.tsx +0 -74
  266. data/frontend/src/components/Transcript.tsx +0 -76
  267. data/frontend/src/components/UsagePanel.tsx +0 -105
  268. data/frontend/src/components/Workspace.tsx +0 -260
  269. data/frontend/src/components/index.ts +0 -12
  270. data/frontend/src/hooks/useResize.ts +0 -55
  271. data/frontend/src/hooks/useWebSocket.ts +0 -517
  272. data/frontend/src/index.css +0 -103
  273. data/frontend/src/main.tsx +0 -10
  274. data/frontend/src/store/index.ts +0 -279
  275. data/frontend/src/types/index.ts +0 -165
  276. data/frontend/tailwind.config.js +0 -44
  277. data/frontend/tsconfig.json +0 -30
  278. data/frontend/vite.config.ts +0 -29
  279. data/lib/prompt_objects/server/public/assets/index-D1myxE0l.js +0 -4345
  280. data/lib/prompt_objects/server/public/assets/index-DdCcwC-Z.css +0 -1
  281. data/lib/prompt_objects/universal/add_primitive.rb +0 -118
  282. data/lib/prompt_objects/universal/ask_human.rb +0 -109
  283. data/lib/prompt_objects/universal/create_primitive.rb +0 -174
  284. data/lib/prompt_objects/universal/list_primitives.rb +0 -145
  285. data/lib/prompt_objects/universal/think.rb +0 -41
  286. data/prompt_objects.gemspec +0 -50
  287. data/templates/basic/.gitignore +0 -2
  288. data/templates/developer/.gitignore +0 -5
  289. data/templates/empty/.gitignore +0 -5
  290. data/templates/empty/objects/.gitkeep +0 -0
  291. data/templates/minimal/.gitignore +0 -5
  292. data/templates/writer/.gitignore +0 -5
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require_relative "../lib/prompt_objects"
6
- require_relative "../lib/prompt_objects/mcp/server"
4
+ # Add lib to the load path for development; installed gems already expose it.
5
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
6
+
7
+ require "prompt_objects"
8
+ require "prompt_objects/mcp/server"
7
9
 
8
10
  # Parse command line options
9
11
  objects_dir = ENV["PROMPT_OBJECTS_DIR"] || "objects"
@@ -11,6 +11,39 @@ module PromptObjects
11
11
  @state = :idle
12
12
  end
13
13
 
14
+ class << self
15
+ def execution_policy(parallel_safe: false, side_effect: :exclusive, resource_keys: nil, workload: :blocking)
16
+ @execution_policy = Execution::Policy.new(
17
+ parallel_safe: parallel_safe,
18
+ side_effect: side_effect,
19
+ resource_keys: resource_keys,
20
+ workload: workload
21
+ )
22
+ end
23
+
24
+ def execution_policy_definition
25
+ return @execution_policy if defined?(@execution_policy) && @execution_policy
26
+
27
+ superclass.respond_to?(:execution_policy_definition) ? superclass.execution_policy_definition : nil
28
+ end
29
+ end
30
+
31
+ def resolved_execution_policy(arguments:, context:)
32
+ policy = execution_policy_definition
33
+ policy.resolve(arguments: arguments, context: context, capability: self)
34
+ end
35
+
36
+ def execution_policy_signature
37
+ execution_policy_definition.signature(self)
38
+ end
39
+
40
+ def execution_policy_definition
41
+ self.class.execution_policy_definition || Execution::Policy.new(
42
+ parallel_safe: false,
43
+ side_effect: :exclusive
44
+ )
45
+ end
46
+
14
47
  # Handle a message and return a response.
15
48
  # @param message [String, Hash] The incoming message
16
49
  # @param context [Context] Execution context with environment reference
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module PromptObjects
8
+ module CLI
9
+ class DiagnosticsCommand
10
+ def run(args)
11
+ json = args.delete("--json")
12
+ return help if args.include?("--help") || args.include?("-h")
13
+
14
+ env_name = args.first
15
+ raise Error, "environment name required" unless env_name
16
+
17
+ env_path = Helpers.resolve_environment(env_name)
18
+ raise Error, "environment '#{env_name}' not found" unless env_path
19
+
20
+ data = server_snapshot(env_path) || local_snapshot(env_path)
21
+ json ? puts(JSON.pretty_generate(data)) : print_summary(data)
22
+ rescue Error => e
23
+ $stderr.puts "Error: #{e.message}"
24
+ 1
25
+ end
26
+
27
+ def help
28
+ puts <<~HELP
29
+ Usage: #{Helpers.program_name} diagnostics <environment> [--json]
30
+
31
+ Show content-free scheduler, timing, failure, usage, and database health metrics.
32
+ HELP
33
+ end
34
+
35
+ private
36
+
37
+ def server_snapshot(env_path)
38
+ require "prompt_objects/server"
39
+ server = PromptObjects::Server.read_server_file(env_path)
40
+ return nil unless server
41
+
42
+ uri = URI("http://#{server[:host]}:#{server[:port]}/api/diagnostics")
43
+ JSON.parse(Net::HTTP.get(uri), symbolize_names: true)
44
+ rescue Errno::ECONNREFUSED
45
+ nil
46
+ end
47
+
48
+ def local_snapshot(env_path)
49
+ path = File.join(env_path, "sessions.db")
50
+ raise Error, "sessions.db not found" unless File.file?(path)
51
+
52
+ store = Session::Store.new(path)
53
+ Operations::Diagnostics.new(store).snapshot
54
+ ensure
55
+ store&.close
56
+ end
57
+
58
+ def print_summary(data)
59
+ puts "Runtime diagnostics (#{data[:generated_at]})"
60
+ puts " database: #{data.dig(:database, :bytes)} bytes, #{data.dig(:database, :integrity)}"
61
+ puts " scheduler: #{data.dig(:scheduler, :running) || 0} running, #{data.dig(:scheduler, :queued) || 0} queued"
62
+ puts " runs: #{data.dig(:counts, :runs)} total, #{data.dig(:runs, :active)} active, #{data.dig(:runs, :failure_rate)} failure rate"
63
+ puts " queue wait: #{data.dig(:runs, :queue_wait_ms, :average)} ms avg / #{data.dig(:runs, :queue_wait_ms, :p95)} ms p95"
64
+ puts " run duration: #{data.dig(:runs, :duration_ms, :average)} ms avg / #{data.dig(:runs, :duration_ms, :p95)} ms p95"
65
+ puts " tool duration: #{data.dig(:tools, :duration_ms, :average)} ms avg / #{data.dig(:tools, :duration_ms, :p95)} ms p95"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,435 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module CLI
5
+ # `env` command: manage environments (list, create, info, export, import,
6
+ # archive, restore, clone, delete, default).
7
+ class EnvCommand
8
+ def initialize(manager: nil)
9
+ @manager = manager || Env::Manager.new
10
+ end
11
+
12
+ # Run the env subcommand.
13
+ # @param args [Array<String>] Arguments after 'env'
14
+ def run(args)
15
+ subcommand = args.shift || "list"
16
+
17
+ case subcommand
18
+ when "list", "ls"
19
+ list(args)
20
+ when "create", "new"
21
+ create(args)
22
+ when "info"
23
+ info(args)
24
+ when "export"
25
+ export(args)
26
+ when "import"
27
+ import(args)
28
+ when "archive"
29
+ archive(args)
30
+ when "restore"
31
+ restore(args)
32
+ when "clone", "cp"
33
+ clone(args)
34
+ when "delete"
35
+ delete(args)
36
+ when "default"
37
+ set_default(args)
38
+ else
39
+ puts "Unknown env command: #{subcommand}"
40
+ puts
41
+ help
42
+ exit 1
43
+ end
44
+ end
45
+
46
+ def list(args = [])
47
+ @manager.setup!
48
+ envs = @manager.list_with_manifests
49
+
50
+ if envs.empty?
51
+ puts "No environments found."
52
+ puts "Create one with: #{prog} env create <name>"
53
+ return
54
+ end
55
+
56
+ default = @manager.default_environment
57
+
58
+ if args.include?("-v") || args.include?("--verbose")
59
+ puts "Environments (#{envs.size}):"
60
+ puts
61
+ envs.each do |manifest|
62
+ default_marker = manifest.name == default ? " (default)" : ""
63
+ last_opened = manifest.last_opened&.strftime("%Y-%m-%d") || "never"
64
+ puts " #{manifest.icon} #{manifest.name}#{default_marker}"
65
+ puts " #{manifest.description}" if manifest.description
66
+ puts " Last opened: #{last_opened}, Objects: #{manifest.stats['po_count']}"
67
+ puts
68
+ end
69
+ return
70
+ end
71
+
72
+ # Concise: one line per environment, aligned for scanning.
73
+ width = [envs.map { |m| m.name.length }.max, 32].min
74
+ archived = @manager.list_archived.size
75
+
76
+ header = "Environments (#{envs.size})"
77
+ header += " · #{archived} archived" if archived.positive?
78
+ puts header
79
+ puts
80
+ envs.each do |m|
81
+ name = m.name.ljust(width)
82
+ last = (m.last_opened&.strftime("%Y-%m-%d") || "never").ljust(10)
83
+ pos = (m.stats["po_count"] || 0).to_s.rjust(2)
84
+ marker = m.name == default ? " (default)" : ""
85
+ puts " #{name} #{last} #{pos} POs#{marker}"
86
+ end
87
+ puts
88
+ puts "Details: env list -v · Delete: env delete <name>... --permanent"
89
+ end
90
+
91
+ def create(args)
92
+ name = args.shift
93
+ unless name
94
+ puts "Usage: #{prog} env create <name> [--template <template>]"
95
+ exit 1
96
+ end
97
+
98
+ template = nil
99
+ args.each_with_index do |arg, i|
100
+ if arg == "--template" || arg == "-t"
101
+ template = args[i + 1]
102
+ end
103
+ end
104
+
105
+ @manager.setup!
106
+
107
+ path = @manager.create(name: name, template: template)
108
+ puts "Created environment: #{name}"
109
+ puts "Location: #{path}"
110
+
111
+ # Print dependency info if template declares gems
112
+ manifest = Env::Manifest.load_from_dir(path)
113
+ if manifest.dependencies&.dig("gems")&.any?
114
+ vendor_dir = File.join(path, "vendor")
115
+ if Dir.exist?(vendor_dir) && !Dir.empty?(vendor_dir)
116
+ puts "\nBundled dependencies (vendored):"
117
+ manifest.dependencies["gems"].each do |gem_info|
118
+ puts " #{gem_info['name']} — #{gem_info['purpose']}"
119
+ end
120
+ else
121
+ puts "\nRequired dependencies (not vendored — install manually):"
122
+ manifest.dependencies["gems"].each do |gem_info|
123
+ puts " gem install #{gem_info['name']} # #{gem_info['purpose']}"
124
+ end
125
+ end
126
+ end
127
+
128
+ if @manager.list.size == 1
129
+ @manager.set_default_environment(name)
130
+ puts "Set as default environment."
131
+ end
132
+ end
133
+
134
+ def info(args)
135
+ name = args.shift || @manager.default_environment
136
+ unless name
137
+ puts "Usage: #{prog} env info <name>"
138
+ exit 1
139
+ end
140
+
141
+ manifest = @manager.manifest_for(name)
142
+ unless manifest
143
+ puts "Environment '#{name}' not found."
144
+ exit 1
145
+ end
146
+
147
+ path = @manager.environment_path(name)
148
+ objects = Dir.glob(File.join(path, "objects", "*.md")).map { |f| File.basename(f, ".md") }
149
+ primitives = Dir.glob(File.join(path, "primitives", "*.rb")).map { |f| File.basename(f, ".rb") }
150
+
151
+ puts manifest.info
152
+ puts
153
+ puts " Location: #{path}"
154
+ puts " Objects: #{objects.join(', ')}" if objects.any?
155
+ puts " Custom primitives: #{primitives.join(', ')}" if primitives.any?
156
+ puts
157
+
158
+ if Env::Git.repo?(path)
159
+ commits = Env::Git.commit_count(path)
160
+ dirty = Env::Git.dirty?(path)
161
+ puts " Git: #{commits} commits#{dirty ? ' (uncommitted changes)' : ''}"
162
+ end
163
+ end
164
+
165
+ def export(args)
166
+ @manager.setup!
167
+
168
+ # Parse arguments
169
+ name = nil
170
+ output = nil
171
+
172
+ i = 0
173
+ while i < args.length
174
+ case args[i]
175
+ when "-o", "--output"
176
+ output = args[i + 1]
177
+ i += 1
178
+ else
179
+ name ||= args[i]
180
+ end
181
+ i += 1
182
+ end
183
+
184
+ # Default to current directory name if no output specified
185
+ name ||= @manager.default_environment
186
+ unless name
187
+ puts "Usage: #{prog} env export <name> [-o output.dump]"
188
+ exit 1
189
+ end
190
+
191
+ unless @manager.environment_exists?(name)
192
+ puts "Environment '#{name}' not found."
193
+ exit 1
194
+ end
195
+
196
+ output ||= "#{name}.dump"
197
+ env_path = @manager.environment_path(name)
198
+
199
+ exporter = Env::Exporter.new(env_path)
200
+ result = exporter.export(output)
201
+
202
+ if result[:success]
203
+ puts "Exported '#{name}' to: #{result[:path]}"
204
+ puts
205
+ puts "Stats:"
206
+ puts " Commits: #{result[:stats][:commits]}"
207
+ puts " Objects: #{result[:stats][:objects]}"
208
+ puts " Primitives: #{result[:stats][:primitives]}"
209
+ else
210
+ puts "Export failed: #{result[:error]}"
211
+ exit 1
212
+ end
213
+ end
214
+
215
+ def import(args)
216
+ @manager.setup!
217
+
218
+ # Parse arguments
219
+ bundle_path = nil
220
+ import_as = nil
221
+ trust = false
222
+
223
+ i = 0
224
+ while i < args.length
225
+ case args[i]
226
+ when "--as"
227
+ import_as = args[i + 1]
228
+ i += 1
229
+ when "--trust"
230
+ trust = true
231
+ else
232
+ bundle_path ||= args[i]
233
+ end
234
+ i += 1
235
+ end
236
+
237
+ unless bundle_path
238
+ puts "Usage: #{prog} env import <bundle.dump> [--as <name>] [--trust]"
239
+ puts
240
+ puts "Options:"
241
+ puts " --as <name> Import with a different name"
242
+ puts " --trust Trust custom primitives (skip sandbox warnings)"
243
+ exit 1
244
+ end
245
+
246
+ importer = Env::Importer.new(bundle_path)
247
+
248
+ # First, inspect and show what's in the bundle
249
+ info = importer.inspect_bundle
250
+ unless info.valid
251
+ puts "Invalid bundle: #{info.error}"
252
+ exit 1
253
+ end
254
+
255
+ puts "Bundle contents:"
256
+ puts " Name: #{info.name}"
257
+ puts " Description: #{info.description}" if info.description
258
+ puts " Objects: #{info.objects.join(', ')}" if info.objects.any?
259
+ puts " Primitives: #{info.primitives.join(', ')}" if info.primitives.any?
260
+ puts " Commits: #{info.commits}"
261
+ puts
262
+
263
+ # Warn about primitives
264
+ if info.primitives.any? && !trust
265
+ puts "⚠️ WARNING: This bundle contains custom primitives."
266
+ puts "Custom primitives can execute arbitrary Ruby code."
267
+ puts "Review the code before running, or use --trust to skip this warning."
268
+ puts
269
+ end
270
+
271
+ # Import
272
+ import_name = import_as || info.name
273
+ result = importer.import(manager: @manager, name: import_name, trust_primitives: trust)
274
+
275
+ if result[:success]
276
+ puts "Imported as '#{result[:name]}'"
277
+ puts "Location: #{result[:path]}"
278
+
279
+ if result[:warnings].any?
280
+ puts
281
+ result[:warnings].each { |w| puts w }
282
+ end
283
+ else
284
+ puts "Import failed: #{result[:error]}"
285
+ exit 1
286
+ end
287
+ end
288
+
289
+ def archive(args)
290
+ name = args.shift
291
+ unless name
292
+ puts "Usage: #{prog} env archive <name>"
293
+ exit 1
294
+ end
295
+
296
+ path = @manager.archive(name)
297
+ puts "Archived environment '#{name}' to:"
298
+ puts " #{path}"
299
+ end
300
+
301
+ def restore(args)
302
+ archived_name = args.shift
303
+ unless archived_name
304
+ # Show available archived environments
305
+ archived = @manager.list_archived
306
+ if archived.empty?
307
+ puts "No archived environments."
308
+ else
309
+ puts "Archived environments:"
310
+ archived.each { |name| puts " - #{name}" }
311
+ puts
312
+ puts "Usage: #{prog} env restore <archived_name> [--as <new_name>]"
313
+ end
314
+ return
315
+ end
316
+
317
+ restore_as = nil
318
+ args.each_with_index do |arg, i|
319
+ restore_as = args[i + 1] if arg == "--as"
320
+ end
321
+
322
+ path = @manager.restore(archived_name, restore_as: restore_as)
323
+ puts "Restored to: #{path}"
324
+ end
325
+
326
+ def clone(args)
327
+ source = args.shift
328
+ target = args.shift
329
+
330
+ unless source && target
331
+ puts "Usage: #{prog} env clone <source> <target>"
332
+ exit 1
333
+ end
334
+
335
+ path = @manager.clone(source, target)
336
+ puts "Cloned '#{source}' to '#{target}'"
337
+ puts "Location: #{path}"
338
+ end
339
+
340
+ def delete(args)
341
+ permanent = args.delete("--permanent")
342
+ names = args
343
+
344
+ if names.empty?
345
+ puts "Usage: #{prog} env delete <name> [<name>...] --permanent"
346
+ puts
347
+ puts "Permanently deletes environments (active or archived) and all their data."
348
+ puts "For a recoverable flow, use `env archive <name>` instead."
349
+ return
350
+ end
351
+
352
+ unless permanent
353
+ puts "This permanently deletes #{names.size} environment#{names.size == 1 ? '' : 's'} and cannot be undone:"
354
+ names.each { |n| puts " - #{n}" }
355
+ puts
356
+ puts "Re-run with --permanent to confirm."
357
+ return
358
+ end
359
+
360
+ names.each do |name|
361
+ if @manager.environment_exists?(name)
362
+ @manager.destroy(name)
363
+ puts "Deleted: #{name}"
364
+ elsif @manager.list_archived.include?(name)
365
+ @manager.delete_archived(name)
366
+ puts "Deleted (archived): #{name}"
367
+ else
368
+ puts "Not found: #{name}"
369
+ end
370
+ rescue => e
371
+ puts "Failed to delete #{name}: #{e.message}"
372
+ end
373
+ end
374
+
375
+ def set_default(args)
376
+ name = args.shift
377
+ unless name
378
+ current = @manager.default_environment
379
+ if current
380
+ puts "Current default: #{current}"
381
+ else
382
+ puts "No default environment set."
383
+ end
384
+ puts
385
+ puts "Usage: #{prog} env default <name>"
386
+ return
387
+ end
388
+
389
+ @manager.set_default_environment(name)
390
+ puts "Default environment set to: #{name}"
391
+ end
392
+
393
+ def help
394
+ puts <<~HELP
395
+ Environment management commands:
396
+
397
+ #{prog} env list List all environments (one line each)
398
+ -v, --verbose Show full details per environment
399
+ #{prog} env create <name> Create new environment
400
+ --template, -t <template> Use one of the templates listed below
401
+ #{prog} env info <name> Show environment details
402
+ #{prog} env export <name> Export environment as .dump bundle
403
+ -o, --output <file> Output file path
404
+ #{prog} env import <file> Import environment from .dump bundle
405
+ --as <name> Import with different name
406
+ --trust Trust custom primitives
407
+ #{prog} env archive <name> Archive (soft delete) environment
408
+ #{prog} env restore <name> Restore archived environment
409
+ --as <new_name> Restore with different name
410
+ #{prog} env clone <src> <dest> Clone environment
411
+ #{prog} env default <name> Set default environment
412
+ #{prog} env delete <name>... --permanent Permanently delete (active or archived; accepts multiple)
413
+
414
+ Available templates:
415
+ basic - No capabilities, learns as needed (great for demos!)
416
+ minimal - Basic assistant with file reading
417
+ developer - Code review, debugging, testing specialists
418
+ writer - Editor, researcher for content creation
419
+ empty - Bootstrap assistant only
420
+ arc-agi-1 - ARC-AGI-1 challenge solving with grid primitives
421
+ arc-agi-3 - Interactive ARC-AGI-3 environment with remote API
422
+ chiptune - Chiptune music composition via Sonic Pi (genre-specific)
423
+ music - Open-ended music composition via Sonic Pi (any genre)
424
+ music-pro - Expressive live-pattern composition via Sonic Pi
425
+ HELP
426
+ end
427
+
428
+ private
429
+
430
+ def prog
431
+ Helpers.program_name
432
+ end
433
+ end
434
+ end
435
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module PromptObjects
6
+ module CLI
7
+ # `events` command: show recent events from the message bus.
8
+ class EventsCommand
9
+ def run(args)
10
+ options = { count: 50, session_id: nil, json: false }
11
+ positional = []
12
+ skip_next = false
13
+
14
+ args.each_with_index do |arg, i|
15
+ if skip_next
16
+ skip_next = false
17
+ next
18
+ end
19
+
20
+ case arg
21
+ when "--session"
22
+ options[:session_id] = args[i + 1]
23
+ skip_next = true
24
+ when "--count"
25
+ options[:count] = args[i + 1].to_i
26
+ skip_next = true
27
+ when "--json"
28
+ options[:json] = true
29
+ when "--help", "-h"
30
+ help
31
+ exit 0
32
+ else
33
+ positional << arg
34
+ end
35
+ end
36
+
37
+ env_name = positional[0]
38
+ unless env_name
39
+ puts "Error: environment name required"
40
+ puts "Run '#{Helpers.program_name} events --help' for usage"
41
+ exit 1
42
+ end
43
+
44
+ env_path = Helpers.resolve_environment(env_name)
45
+ unless env_path
46
+ $stderr.puts "Error: environment '#{env_name}' not found"
47
+ exit 1
48
+ end
49
+
50
+ # Check for running server
51
+ require "prompt_objects/server"
52
+ server_info = PromptObjects::Server.read_server_file(env_path)
53
+
54
+ if server_info
55
+ show_via_http(server_info, options)
56
+ else
57
+ show_standalone(env_path, options)
58
+ end
59
+ end
60
+
61
+ def help
62
+ prog = Helpers.program_name
63
+ puts <<~HELP
64
+ Usage: #{prog} events [options] <environment>
65
+
66
+ Show recent events from the message bus.
67
+
68
+ Arguments:
69
+ environment Environment name or path
70
+
71
+ Options:
72
+ --session ID Show events for a specific session
73
+ --count N Number of events to show (default: 50)
74
+ --json Output as JSON
75
+ --help, -h Show this help
76
+
77
+ Examples:
78
+ #{prog} events my-env
79
+ #{prog} events my-env --session abc-123
80
+ #{prog} events my-env --count 100 --json
81
+ HELP
82
+ end
83
+
84
+ private
85
+
86
+ def show_via_http(server_info, options)
87
+ require "net/http"
88
+ require "uri"
89
+
90
+ if options[:session_id]
91
+ uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events/session/#{options[:session_id]}")
92
+ else
93
+ uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events?count=#{options[:count]}")
94
+ end
95
+
96
+ response = Net::HTTP.get(uri)
97
+ data = JSON.parse(response)
98
+
99
+ if options[:json]
100
+ puts JSON.pretty_generate(data)
101
+ else
102
+ Helpers.print_events(data["events"] || [])
103
+ end
104
+ rescue Errno::ECONNREFUSED
105
+ $stderr.puts "Error: Server not responding"
106
+ exit 1
107
+ end
108
+
109
+ def show_standalone(env_path, options)
110
+ db_path = File.join(env_path, "sessions.db")
111
+ unless File.exist?(db_path)
112
+ puts "No event history (sessions.db not found)"
113
+ exit 0
114
+ end
115
+
116
+ store = PromptObjects::Session::Store.new(db_path)
117
+
118
+ events = if options[:session_id]
119
+ store.get_events(session_id: options[:session_id])
120
+ else
121
+ store.get_recent_events(options[:count])
122
+ end
123
+
124
+ if options[:json]
125
+ formatted = events.map do |e|
126
+ {
127
+ id: e[:id],
128
+ from: e[:from],
129
+ to: e[:to],
130
+ summary: e[:summary],
131
+ message: e[:message],
132
+ timestamp: e[:timestamp]&.iso8601,
133
+ session_id: e[:session_id]
134
+ }
135
+ end
136
+ puts JSON.pretty_generate(formatted)
137
+ else
138
+ Helpers.print_events(events)
139
+ end
140
+
141
+ store.close
142
+ end
143
+ end
144
+ end
145
+ end