prompt_objects 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -0
- data/README.md +93 -22
- data/exe/poop +5 -0
- data/exe/prompt_objects +1 -1108
- data/exe/prompt_objects_mcp +5 -3
- data/lib/prompt_objects/capability.rb +33 -0
- data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
- data/lib/prompt_objects/cli/env_command.rb +435 -0
- data/lib/prompt_objects/cli/events_command.rb +145 -0
- data/lib/prompt_objects/cli/explore_command.rb +155 -0
- data/lib/prompt_objects/cli/helpers.rb +91 -0
- data/lib/prompt_objects/cli/message_command.rb +216 -0
- data/lib/prompt_objects/cli/repl_command.rb +366 -0
- data/lib/prompt_objects/cli/serve_command.rb +162 -0
- data/lib/prompt_objects/cli.rb +64 -422
- data/lib/prompt_objects/connectors/base.rb +16 -29
- data/lib/prompt_objects/connectors/mcp.rb +82 -98
- data/lib/prompt_objects/domain/records.rb +45 -0
- data/lib/prompt_objects/environment/exporter.rb +5 -4
- data/lib/prompt_objects/environment/git.rb +21 -1
- data/lib/prompt_objects/environment/importer.rb +5 -4
- data/lib/prompt_objects/environment/manager.rb +44 -6
- data/lib/prompt_objects/environment/manifest.rb +26 -4
- data/lib/prompt_objects/environment.rb +1337 -20
- data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
- data/lib/prompt_objects/execution/context.rb +137 -0
- data/lib/prompt_objects/execution/contracts.rb +59 -0
- data/lib/prompt_objects/execution/permit_pool.rb +49 -0
- data/lib/prompt_objects/execution/policy.rb +47 -0
- data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
- data/lib/prompt_objects/execution/runner.rb +498 -0
- data/lib/prompt_objects/execution/scheduler.rb +325 -0
- data/lib/prompt_objects/llm/factory.rb +4 -4
- data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
- data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
- data/lib/prompt_objects/llm/pricing.rb +9 -2
- data/lib/prompt_objects/llm/response.rb +9 -4
- data/lib/prompt_objects/loader.rb +28 -1
- data/lib/prompt_objects/mcp/server.rb +6 -3
- data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
- data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
- data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
- data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
- data/lib/prompt_objects/operations/diagnostics.rb +115 -0
- data/lib/prompt_objects/operations/retention.rb +75 -0
- data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
- data/lib/prompt_objects/primitives/http_get.rb +2 -0
- data/lib/prompt_objects/primitives/list_files.rb +2 -0
- data/lib/prompt_objects/primitives/read_file.rb +2 -0
- data/lib/prompt_objects/primitives/think.rb +36 -0
- data/lib/prompt_objects/primitives/write_file.rb +9 -0
- data/lib/prompt_objects/prompt_object.rb +171 -519
- data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
- data/lib/prompt_objects/repositories/base_repository.rb +37 -0
- data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
- data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
- data/lib/prompt_objects/repositories/event_repository.rb +105 -0
- data/lib/prompt_objects/repositories/message_repository.rb +73 -0
- data/lib/prompt_objects/repositories/run_repository.rb +254 -0
- data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
- data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
- data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
- data/lib/prompt_objects/server/api/routes.rb +549 -53
- data/lib/prompt_objects/server/app.rb +133 -14
- data/lib/prompt_objects/server/file_watcher.rb +9 -0
- data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
- data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
- data/lib/prompt_objects/server/public/index.html +2 -2
- data/lib/prompt_objects/server/websocket_handler.rb +202 -227
- data/lib/prompt_objects/server.rb +170 -1
- data/lib/prompt_objects/service.rb +129 -0
- data/lib/prompt_objects/session/store.rb +600 -13
- data/lib/prompt_objects/universal/add_capability.rb +1 -1
- data/lib/prompt_objects/universal/create_capability.rb +4 -2
- data/lib/prompt_objects/universal/delete_env_data.rb +23 -15
- data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
- data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
- data/lib/prompt_objects/universal/get_env_data.rb +8 -14
- data/lib/prompt_objects/universal/get_run_status.rb +30 -0
- data/lib/prompt_objects/universal/join_runs.rb +35 -0
- data/lib/prompt_objects/universal/list_env_data.rb +10 -14
- data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
- data/lib/prompt_objects/universal/render_artifact.rb +91 -0
- data/lib/prompt_objects/universal/request_primitive.rb +1 -1
- data/lib/prompt_objects/universal/{store_env_data.rb → set_env_data.rb} +34 -20
- data/lib/prompt_objects/version.rb +5 -0
- data/lib/prompt_objects.rb +33 -8
- data/objects/coordinator.md +8 -4
- data/templates/arc-agi-1/objects/data_manager.md +2 -2
- data/templates/arc-agi-1/objects/observer.md +2 -2
- data/templates/arc-agi-1/objects/solver.md +6 -5
- data/templates/arc-agi-1/objects/verifier.md +2 -2
- data/templates/arc-agi-3/manifest.yml +19 -0
- data/templates/arc-agi-3/objects/analyst.md +79 -0
- data/templates/arc-agi-3/objects/player.md +76 -0
- data/templates/arc-agi-3/objects/strategist.md +97 -0
- data/templates/arc-agi-3/primitives/action_log.rb +133 -0
- data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
- data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
- data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
- data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
- data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
- data/templates/basic/objects/basic.md +11 -3
- data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
- data/templates/chiptune/manifest.yml +20 -0
- data/templates/chiptune/objects/conductor.md +184 -0
- data/templates/chiptune/objects/sonic_pi.md +65 -0
- data/templates/chiptune/objects/voice_bass.md +44 -0
- data/templates/chiptune/objects/voice_lead.md +44 -0
- data/templates/chiptune/objects/voice_pad.md +43 -0
- data/templates/chiptune/objects/voice_perc.md +45 -0
- data/templates/chiptune/primitives/play_note.rb +59 -0
- data/templates/chiptune/primitives/play_sequence.rb +55 -0
- data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
- data/templates/chiptune/primitives/set_synth.rb +57 -0
- data/templates/chiptune/primitives/set_tempo.rb +45 -0
- data/templates/chiptune/primitives/stop_voice.rb +46 -0
- data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/developer/objects/code_reviewer.md +1 -1
- data/templates/developer/objects/coordinator.md +16 -3
- data/templates/developer/objects/debugger.md +3 -3
- data/templates/empty/objects/assistant.md +10 -5
- data/templates/minimal/objects/assistant.md +10 -5
- data/templates/music/extras/sonic_pi_boot.rb +220 -0
- data/templates/music/manifest.yml +16 -0
- data/templates/music/objects/conductor.md +154 -0
- data/templates/music/objects/sonic_pi.md +65 -0
- data/templates/music/primitives/play_note.rb +55 -0
- data/templates/music/primitives/play_sequence.rb +51 -0
- data/templates/music/primitives/query_sonic_pi.rb +139 -0
- data/templates/music/primitives/set_synth.rb +53 -0
- data/templates/music/primitives/set_tempo.rb +43 -0
- data/templates/music/primitives/stop_voice.rb +44 -0
- data/templates/music/primitives/support/osc_bridge.rb +33 -0
- data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-beginner/README.md +100 -0
- data/templates/music-beginner/extras/README.md +27 -0
- data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
- data/templates/music-beginner/manifest.yml +25 -0
- data/templates/music-beginner/objects/conductor.md +222 -0
- data/templates/music-beginner/objects/engine_setup.md +54 -0
- data/templates/music-beginner/objects/voice_bass.md +43 -0
- data/templates/music-beginner/objects/voice_keys.md +48 -0
- data/templates/music-beginner/objects/voice_pad.md +46 -0
- data/templates/music-beginner/objects/voice_percussion.md +45 -0
- data/templates/music-beginner/objects/voice_texture.md +37 -0
- data/templates/music-beginner/primitives/play_pattern.rb +118 -0
- data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
- data/templates/music-beginner/primitives/set_instrument.rb +47 -0
- data/templates/music-beginner/primitives/set_tempo.rb +43 -0
- data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
- data/templates/music-beginner/primitives/stop_voice.rb +37 -0
- data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-pro/extras/AUDITION.md +100 -0
- data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
- data/templates/music-pro/manifest.yml +19 -0
- data/templates/music-pro/objects/conductor.md +126 -0
- data/templates/music-pro/objects/sonic_pi.md +35 -0
- data/templates/music-pro/objects/voice_harmony.md +48 -0
- data/templates/music-pro/objects/voice_rhythm.md +43 -0
- data/templates/music-pro/objects/voice_texture.md +42 -0
- data/templates/music-pro/primitives/define_patch.rb +46 -0
- data/templates/music-pro/primitives/query_music_engine.rb +69 -0
- data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
- data/templates/music-pro/primitives/set_tempo.rb +31 -0
- data/templates/music-pro/primitives/stop_voice.rb +31 -0
- data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
- data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
- data/templates/music-pro/primitives/trigger_event.rb +44 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/writer/objects/coordinator.md +14 -3
- data/templates/writer/objects/editor.md +1 -1
- data/templates/writer/objects/researcher.md +2 -2
- data/tools/thread-explorer.html +1 -1
- metadata +172 -65
- data/CLAUDE.md +0 -177
- data/Gemfile +0 -10
- data/Rakefile +0 -27
- data/frontend/.gitignore +0 -3
- data/frontend/index.html +0 -17
- data/frontend/package-lock.json +0 -4540
- data/frontend/package.json +0 -36
- data/frontend/postcss.config.js +0 -6
- data/frontend/src/App.tsx +0 -104
- data/frontend/src/canvas/CanvasView.tsx +0 -113
- data/frontend/src/canvas/ForceLayout.ts +0 -115
- data/frontend/src/canvas/SceneManager.ts +0 -587
- data/frontend/src/canvas/canvasStore.ts +0 -47
- data/frontend/src/canvas/constants.ts +0 -95
- data/frontend/src/canvas/controls/CameraControls.ts +0 -98
- data/frontend/src/canvas/edges/MessageArc.ts +0 -149
- data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
- data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
- data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
- data/frontend/src/canvas/nodes/PONode.ts +0 -249
- data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
- data/frontend/src/canvas/types.ts +0 -24
- data/frontend/src/components/ContextMenu.tsx +0 -68
- data/frontend/src/components/EnvDataPane.tsx +0 -69
- data/frontend/src/components/Inspector.tsx +0 -263
- data/frontend/src/components/MarkdownMessage.tsx +0 -155
- data/frontend/src/components/MethodList.tsx +0 -90
- data/frontend/src/components/ModelSelector.tsx +0 -115
- data/frontend/src/components/NotificationPanel.tsx +0 -130
- data/frontend/src/components/ObjectList.tsx +0 -78
- data/frontend/src/components/PaneSlot.tsx +0 -30
- data/frontend/src/components/SourcePane.tsx +0 -202
- data/frontend/src/components/SystemBar.tsx +0 -74
- data/frontend/src/components/Transcript.tsx +0 -76
- data/frontend/src/components/UsagePanel.tsx +0 -105
- data/frontend/src/components/Workspace.tsx +0 -260
- data/frontend/src/components/index.ts +0 -12
- data/frontend/src/hooks/useResize.ts +0 -55
- data/frontend/src/hooks/useWebSocket.ts +0 -559
- data/frontend/src/index.css +0 -102
- data/frontend/src/main.tsx +0 -10
- data/frontend/src/store/index.ts +0 -311
- data/frontend/src/types/index.ts +0 -176
- data/frontend/tailwind.config.js +0 -44
- data/frontend/tsconfig.json +0 -30
- data/frontend/vite.config.ts +0 -29
- data/lib/prompt_objects/server/public/assets/index-DEPawnfZ.js +0 -4345
- data/lib/prompt_objects/server/public/assets/index-oMrRce1m.css +0 -1
- data/lib/prompt_objects/universal/add_primitive.rb +0 -118
- data/lib/prompt_objects/universal/ask_human.rb +0 -109
- data/lib/prompt_objects/universal/create_primitive.rb +0 -174
- data/lib/prompt_objects/universal/list_primitives.rb +0 -145
- data/lib/prompt_objects/universal/think.rb +0 -41
- data/lib/prompt_objects/universal/update_env_data.rb +0 -88
- data/prompt_objects.gemspec +0 -50
- data/templates/basic/.gitignore +0 -2
- data/templates/developer/.gitignore +0 -5
- data/templates/empty/.gitignore +0 -5
- data/templates/empty/objects/.gitkeep +0 -0
- data/templates/minimal/.gitignore +0 -5
- data/templates/writer/.gitignore +0 -5
data/exe/prompt_objects
CHANGED
|
@@ -8,1112 +8,5 @@ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
|
8
8
|
# the user's project Gemfile if they run this from within a Rails app, etc.
|
|
9
9
|
require "prompt_objects"
|
|
10
10
|
require "prompt_objects/cli"
|
|
11
|
-
require "fileutils"
|
|
12
|
-
require "tmpdir"
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
class Sandbox
|
|
16
|
-
attr_reader :sandbox_dir, :objects_dir, :primitives_dir, :original_objects_dir
|
|
17
|
-
|
|
18
|
-
def initialize(original_objects_dir)
|
|
19
|
-
@original_objects_dir = File.expand_path(original_objects_dir)
|
|
20
|
-
@sandbox_dir = Dir.mktmpdir("prompt_objects_sandbox_")
|
|
21
|
-
@objects_dir = File.join(@sandbox_dir, "objects")
|
|
22
|
-
@primitives_dir = File.join(@sandbox_dir, "primitives")
|
|
23
|
-
|
|
24
|
-
setup
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def setup
|
|
28
|
-
FileUtils.mkdir_p(@objects_dir)
|
|
29
|
-
FileUtils.mkdir_p(@primitives_dir)
|
|
30
|
-
|
|
31
|
-
# Copy existing objects to sandbox
|
|
32
|
-
if Dir.exist?(@original_objects_dir)
|
|
33
|
-
Dir.glob(File.join(@original_objects_dir, "*.md")).each do |src|
|
|
34
|
-
FileUtils.cp(src, @objects_dir)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
puts "Sandbox initialized at: #{@sandbox_dir}"
|
|
39
|
-
puts " Objects copied: #{Dir.glob(File.join(@objects_dir, '*.md')).count}"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def cleanup(keep_changes: false)
|
|
43
|
-
if keep_changes
|
|
44
|
-
copy_back_changes
|
|
45
|
-
end
|
|
46
|
-
FileUtils.rm_rf(@sandbox_dir)
|
|
47
|
-
puts "Sandbox cleaned up"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def copy_back_changes
|
|
51
|
-
# Find new/modified objects
|
|
52
|
-
new_objects = Dir.glob(File.join(@objects_dir, "*.md")).select do |sandbox_file|
|
|
53
|
-
original = File.join(@original_objects_dir, File.basename(sandbox_file))
|
|
54
|
-
!File.exist?(original) || File.read(sandbox_file) != File.read(original)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Find new primitives
|
|
58
|
-
new_primitives = Dir.glob(File.join(@primitives_dir, "*.rb"))
|
|
59
|
-
|
|
60
|
-
return if new_objects.empty? && new_primitives.empty?
|
|
61
|
-
|
|
62
|
-
puts "\nChanges to copy back:"
|
|
63
|
-
new_objects.each { |f| puts " + objects/#{File.basename(f)}" }
|
|
64
|
-
new_primitives.each { |f| puts " + primitives/#{File.basename(f)}" }
|
|
65
|
-
|
|
66
|
-
print "\nCopy these to main project? (y/n): "
|
|
67
|
-
answer = $stdin.gets&.chomp&.downcase
|
|
68
|
-
return unless answer == "y"
|
|
69
|
-
|
|
70
|
-
new_objects.each do |f|
|
|
71
|
-
FileUtils.cp(f, @original_objects_dir)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
if new_primitives.any?
|
|
75
|
-
target_primitives = File.join(File.dirname(@original_objects_dir), "primitives")
|
|
76
|
-
FileUtils.mkdir_p(target_primitives)
|
|
77
|
-
new_primitives.each do |f|
|
|
78
|
-
FileUtils.cp(f, target_primitives)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
puts "Changes copied to main project"
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def show_new_files
|
|
86
|
-
new_objects = Dir.glob(File.join(@objects_dir, "*.md")).reject do |sandbox_file|
|
|
87
|
-
original = File.join(@original_objects_dir, File.basename(sandbox_file))
|
|
88
|
-
File.exist?(original) && File.read(sandbox_file) == File.read(original)
|
|
89
|
-
end
|
|
90
|
-
new_primitives = Dir.glob(File.join(@primitives_dir, "*.rb"))
|
|
91
|
-
|
|
92
|
-
{ objects: new_objects, primitives: new_primitives }
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Simple REPL for interacting with a Prompt Object
|
|
97
|
-
class REPL
|
|
98
|
-
def initialize(prompt_object, env, sandbox: nil)
|
|
99
|
-
@po = prompt_object
|
|
100
|
-
@env = env
|
|
101
|
-
@context = env.context
|
|
102
|
-
@show_log = true # Show message log by default
|
|
103
|
-
@sandbox = sandbox
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def run
|
|
107
|
-
puts header
|
|
108
|
-
if @sandbox
|
|
109
|
-
puts "SANDBOX MODE - changes isolated from main project"
|
|
110
|
-
puts
|
|
111
|
-
end
|
|
112
|
-
puts "Loaded: #{@po.name}"
|
|
113
|
-
puts @po.description
|
|
114
|
-
puts
|
|
115
|
-
puts "Capabilities: #{format_capabilities}"
|
|
116
|
-
puts "-" * 60
|
|
117
|
-
puts "Commands: 'exit', 'history', 'log', 'log on/off', 'sandbox' (if in sandbox mode)"
|
|
118
|
-
puts
|
|
119
|
-
|
|
120
|
-
loop do
|
|
121
|
-
print "You: "
|
|
122
|
-
input = $stdin.gets&.chomp
|
|
123
|
-
|
|
124
|
-
break if input.nil?
|
|
125
|
-
next if input.empty?
|
|
126
|
-
|
|
127
|
-
case input.downcase
|
|
128
|
-
when "exit", "quit"
|
|
129
|
-
handle_exit
|
|
130
|
-
break
|
|
131
|
-
when "history"
|
|
132
|
-
show_history
|
|
133
|
-
next
|
|
134
|
-
when "log"
|
|
135
|
-
show_message_log
|
|
136
|
-
next
|
|
137
|
-
when "log on"
|
|
138
|
-
@show_log = true
|
|
139
|
-
puts "Message log display: ON"
|
|
140
|
-
next
|
|
141
|
-
when "log off"
|
|
142
|
-
@show_log = false
|
|
143
|
-
puts "Message log display: OFF"
|
|
144
|
-
next
|
|
145
|
-
when "sandbox"
|
|
146
|
-
show_sandbox_status
|
|
147
|
-
next
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Clear log before each interaction to show only relevant messages
|
|
151
|
-
log_start = @env.bus.log.length
|
|
152
|
-
|
|
153
|
-
begin
|
|
154
|
-
# Log the human message
|
|
155
|
-
@env.bus.publish(from: "human", to: @po.name, message: input)
|
|
156
|
-
|
|
157
|
-
@context.current_capability = @po.name
|
|
158
|
-
response = @po.receive(input, context: @context)
|
|
159
|
-
|
|
160
|
-
# Log the response back to human
|
|
161
|
-
@env.bus.publish(from: @po.name, to: "human", message: response)
|
|
162
|
-
|
|
163
|
-
# Show the message log for this interaction
|
|
164
|
-
if @show_log
|
|
165
|
-
puts
|
|
166
|
-
show_interaction_log(log_start)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
puts
|
|
170
|
-
puts "#{@po.name}: #{response}"
|
|
171
|
-
puts
|
|
172
|
-
rescue StandardError => e
|
|
173
|
-
puts "\nError: #{e.message}"
|
|
174
|
-
puts e.backtrace.first(5).join("\n") if ENV["DEBUG"]
|
|
175
|
-
puts
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
private
|
|
181
|
-
|
|
182
|
-
def header
|
|
183
|
-
<<~HEADER
|
|
184
|
-
|
|
185
|
-
PromptObjects v0.1.0
|
|
186
|
-
|
|
187
|
-
HEADER
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def format_capabilities
|
|
191
|
-
caps = @po.config["capabilities"] || []
|
|
192
|
-
return "(none)" if caps.empty?
|
|
193
|
-
|
|
194
|
-
caps.map do |cap_name|
|
|
195
|
-
cap = @env.registry.get(cap_name)
|
|
196
|
-
if cap.is_a?(PromptObjects::PromptObject)
|
|
197
|
-
"#{cap_name} (PO)"
|
|
198
|
-
elsif cap.is_a?(PromptObjects::Primitive)
|
|
199
|
-
"#{cap_name}"
|
|
200
|
-
else
|
|
201
|
-
"#{cap_name} (?)"
|
|
202
|
-
end
|
|
203
|
-
end.join(", ")
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def handle_exit
|
|
207
|
-
puts "\nGoodbye!"
|
|
208
|
-
return unless @sandbox
|
|
209
|
-
|
|
210
|
-
new_files = @sandbox.show_new_files
|
|
211
|
-
if new_files[:objects].any? || new_files[:primitives].any?
|
|
212
|
-
puts "\nSession created files:"
|
|
213
|
-
new_files[:objects].each { |f| puts " - #{File.basename(f)}" }
|
|
214
|
-
new_files[:primitives].each { |f| puts " - #{File.basename(f)}" }
|
|
215
|
-
@sandbox.cleanup(keep_changes: true)
|
|
216
|
-
else
|
|
217
|
-
puts "No new files created."
|
|
218
|
-
@sandbox.cleanup(keep_changes: false)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def show_sandbox_status
|
|
223
|
-
unless @sandbox
|
|
224
|
-
puts "Not in sandbox mode. Run with --sandbox to enable."
|
|
225
|
-
return
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
puts "\nSandbox Status"
|
|
229
|
-
puts " Location: #{@sandbox.sandbox_dir}"
|
|
230
|
-
new_files = @sandbox.show_new_files
|
|
231
|
-
if new_files[:objects].any? || new_files[:primitives].any?
|
|
232
|
-
puts " New/modified files this session:"
|
|
233
|
-
new_files[:objects].each { |f| puts " + #{File.basename(f)}" }
|
|
234
|
-
new_files[:primitives].each { |f| puts " + #{File.basename(f)}" }
|
|
235
|
-
else
|
|
236
|
-
puts " No new files created yet."
|
|
237
|
-
end
|
|
238
|
-
puts
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def show_history
|
|
242
|
-
puts "\n--- Conversation History ---"
|
|
243
|
-
@po.history.each_with_index do |msg, i|
|
|
244
|
-
role = msg[:role].to_s.capitalize
|
|
245
|
-
content = msg[:content]&.slice(0, 100)
|
|
246
|
-
content += "..." if msg[:content]&.length.to_i > 100
|
|
247
|
-
puts "#{i + 1}. [#{role}] #{content}"
|
|
248
|
-
end
|
|
249
|
-
puts "----------------------------\n\n"
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def show_message_log
|
|
253
|
-
puts "\n--- Full Message Log ---"
|
|
254
|
-
puts @env.bus.format_log(50)
|
|
255
|
-
puts "------------------------\n\n"
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def show_interaction_log(start_index)
|
|
259
|
-
entries = @env.bus.log[start_index..]
|
|
260
|
-
return if entries.nil? || entries.empty?
|
|
261
|
-
|
|
262
|
-
puts "--- Message Log ---"
|
|
263
|
-
entries.each do |entry|
|
|
264
|
-
time = entry[:timestamp].strftime("%H:%M:%S")
|
|
265
|
-
from = entry[:from]
|
|
266
|
-
to = entry[:to]
|
|
267
|
-
msg = entry[:summary]
|
|
268
|
-
|
|
269
|
-
line = "#{time} #{from} -> #{to}: #{msg}"
|
|
270
|
-
line = line[0, 70] + "..." if line.length > 73
|
|
271
|
-
puts line
|
|
272
|
-
end
|
|
273
|
-
puts "-------------------"
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# === CLI Command Handling ===
|
|
278
|
-
|
|
279
|
-
def print_main_help
|
|
280
|
-
puts <<~HELP
|
|
281
|
-
Usage: prompt_objects <command> [options]
|
|
282
|
-
|
|
283
|
-
Commands:
|
|
284
|
-
env Manage environments (create, list, export, etc.)
|
|
285
|
-
serve <env> Run environment as a web server
|
|
286
|
-
message <env> <po> "text" Send a message to a PO and print the response
|
|
287
|
-
events <env> [--session ID] Show recent events from the message bus
|
|
288
|
-
explore [env] [--session ID] Open Thread Explorer to visualize conversations
|
|
289
|
-
repl [name] [objects_dir] Start interactive REPL with a prompt object
|
|
290
|
-
help Show this help message
|
|
291
|
-
|
|
292
|
-
Run 'prompt_objects <command> --help' for command-specific help.
|
|
293
|
-
|
|
294
|
-
Examples:
|
|
295
|
-
prompt_objects env list # List all environments
|
|
296
|
-
prompt_objects env create demo --template demo # Create from template
|
|
297
|
-
prompt_objects serve my-env # Start web UI
|
|
298
|
-
prompt_objects serve my-env --open # Start and open browser
|
|
299
|
-
prompt_objects message my-env solver "Hello" # Send a message
|
|
300
|
-
prompt_objects message my-env solver "Hello" --json # JSON output
|
|
301
|
-
prompt_objects explore my-env # Visualize conversations
|
|
302
|
-
HELP
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def print_repl_help
|
|
306
|
-
puts <<~HELP
|
|
307
|
-
Usage: prompt_objects repl [options] [name] [objects_dir]
|
|
308
|
-
|
|
309
|
-
Arguments:
|
|
310
|
-
name Prompt object to load (default: greeter)
|
|
311
|
-
objects_dir Directory containing .md files (default: objects)
|
|
312
|
-
|
|
313
|
-
Options:
|
|
314
|
-
--sandbox, -s Run in sandbox mode (isolates changes from main project)
|
|
315
|
-
--help, -h Show this help message
|
|
316
|
-
|
|
317
|
-
Sandbox Mode:
|
|
318
|
-
When running with --sandbox, all new capabilities and prompt objects
|
|
319
|
-
created during the session are stored in a temporary directory.
|
|
320
|
-
On exit, you'll be prompted to copy changes back to the main project.
|
|
321
|
-
HELP
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
def print_serve_help
|
|
325
|
-
puts <<~HELP
|
|
326
|
-
Usage: prompt_objects serve [options] <environment>
|
|
327
|
-
|
|
328
|
-
Run an environment as a server, exposing it through different interfaces.
|
|
329
|
-
|
|
330
|
-
Arguments:
|
|
331
|
-
environment Environment name or path to environment directory
|
|
332
|
-
|
|
333
|
-
Options:
|
|
334
|
-
--web Run as web server with React UI (default)
|
|
335
|
-
--mcp Run as MCP server (stdio transport, for Claude Desktop/Cursor)
|
|
336
|
-
--port PORT Port for web server (default: 3000)
|
|
337
|
-
--host HOST Host for web server (default: localhost)
|
|
338
|
-
--open Open browser automatically
|
|
339
|
-
--help, -h Show this help message
|
|
340
|
-
|
|
341
|
-
Examples:
|
|
342
|
-
prompt_objects serve my-assistant # Web UI (default)
|
|
343
|
-
prompt_objects serve my-assistant --port 4000 # Custom port
|
|
344
|
-
prompt_objects serve --mcp my-assistant # MCP server mode
|
|
345
|
-
prompt_objects serve ./my-environment # Serve from path
|
|
346
|
-
|
|
347
|
-
Claude Desktop Configuration (MCP mode):
|
|
348
|
-
Add to claude_desktop_config.json:
|
|
349
|
-
{
|
|
350
|
-
"mcpServers": {
|
|
351
|
-
"my-assistant": {
|
|
352
|
-
"command": "prompt_objects",
|
|
353
|
-
"args": ["serve", "--mcp", "my-assistant"]
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
HELP
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
def run_repl(args)
|
|
361
|
-
options = {
|
|
362
|
-
name: "greeter",
|
|
363
|
-
objects_dir: "objects",
|
|
364
|
-
sandbox: false
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
positional = []
|
|
368
|
-
args.each do |arg|
|
|
369
|
-
case arg
|
|
370
|
-
when "--sandbox", "-s"
|
|
371
|
-
options[:sandbox] = true
|
|
372
|
-
when "--help", "-h"
|
|
373
|
-
print_repl_help
|
|
374
|
-
exit 0
|
|
375
|
-
else
|
|
376
|
-
positional << arg
|
|
377
|
-
end
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
options[:name] = positional[0] if positional[0]
|
|
381
|
-
options[:objects_dir] = positional[1] if positional[1]
|
|
382
|
-
|
|
383
|
-
sandbox = nil
|
|
384
|
-
primitives_dir = nil
|
|
385
|
-
|
|
386
|
-
if options[:sandbox]
|
|
387
|
-
sandbox = Sandbox.new(options[:objects_dir])
|
|
388
|
-
objects_dir = sandbox.objects_dir
|
|
389
|
-
primitives_dir = sandbox.primitives_dir
|
|
390
|
-
else
|
|
391
|
-
objects_dir = options[:objects_dir]
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
env = PromptObjects::Runtime.new(objects_dir: objects_dir, primitives_dir: primitives_dir)
|
|
395
|
-
|
|
396
|
-
begin
|
|
397
|
-
po = env.load_by_name(options[:name])
|
|
398
|
-
env.load_dependencies(po)
|
|
399
|
-
rescue PromptObjects::Error => e
|
|
400
|
-
puts "Error loading '#{options[:name]}': #{e.message}"
|
|
401
|
-
puts "\nAvailable objects in '#{objects_dir}/':"
|
|
402
|
-
Dir.glob(File.join(objects_dir, "*.md")).each do |path|
|
|
403
|
-
puts " - #{File.basename(path, '.md')}"
|
|
404
|
-
end
|
|
405
|
-
sandbox&.cleanup(keep_changes: false)
|
|
406
|
-
exit 1
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
REPL.new(po, env, sandbox: sandbox).run
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
def run_serve(args)
|
|
413
|
-
options = {
|
|
414
|
-
web: false,
|
|
415
|
-
mcp: false,
|
|
416
|
-
port: 3000,
|
|
417
|
-
host: "localhost",
|
|
418
|
-
open: false,
|
|
419
|
-
environment: nil
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
skip_next = false
|
|
423
|
-
args.each_with_index do |arg, i|
|
|
424
|
-
if skip_next
|
|
425
|
-
skip_next = false
|
|
426
|
-
next
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
case arg
|
|
430
|
-
when "--web"
|
|
431
|
-
options[:web] = true
|
|
432
|
-
when "--mcp"
|
|
433
|
-
options[:mcp] = true
|
|
434
|
-
when "--port"
|
|
435
|
-
options[:port] = args[i + 1].to_i
|
|
436
|
-
skip_next = true
|
|
437
|
-
when "--host"
|
|
438
|
-
options[:host] = args[i + 1]
|
|
439
|
-
skip_next = true
|
|
440
|
-
when "--open", "-o"
|
|
441
|
-
options[:open] = true
|
|
442
|
-
when "--help", "-h"
|
|
443
|
-
print_serve_help
|
|
444
|
-
exit 0
|
|
445
|
-
else
|
|
446
|
-
# Assume it's the environment
|
|
447
|
-
options[:environment] = arg unless arg.start_with?("-")
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
unless options[:environment]
|
|
452
|
-
puts "Error: environment name or path required"
|
|
453
|
-
puts "Run 'prompt_objects serve --help' for usage"
|
|
454
|
-
exit 1
|
|
455
|
-
end
|
|
456
|
-
|
|
457
|
-
# Default to web if no interface specified
|
|
458
|
-
options[:web] = true unless options[:mcp]
|
|
459
|
-
|
|
460
|
-
# Redirect stderr to a log file to avoid polluting MCP stdio
|
|
461
|
-
if options[:mcp] && ENV["PROMPT_OBJECTS_LOG"]
|
|
462
|
-
$stderr.reopen(ENV["PROMPT_OBJECTS_LOG"], "a")
|
|
463
|
-
$stderr.sync = true
|
|
464
|
-
end
|
|
465
|
-
|
|
466
|
-
# Resolve environment
|
|
467
|
-
env_path = resolve_environment(options[:environment])
|
|
468
|
-
unless env_path
|
|
469
|
-
$stderr.puts "Error: environment '#{options[:environment]}' not found"
|
|
470
|
-
exit 1
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
# Load the runtime
|
|
474
|
-
runtime = PromptObjects::Runtime.new(env_path: env_path)
|
|
475
|
-
|
|
476
|
-
# Load all prompt objects
|
|
477
|
-
load_all_objects(runtime, env_path)
|
|
478
|
-
|
|
479
|
-
if options[:mcp]
|
|
480
|
-
connector = PromptObjects::Connectors::MCP.new(runtime: runtime)
|
|
481
|
-
connector.start
|
|
482
|
-
elsif options[:web]
|
|
483
|
-
require "prompt_objects/server"
|
|
484
|
-
|
|
485
|
-
# Rebuild frontend to ensure built assets match source
|
|
486
|
-
rebuild_frontend
|
|
487
|
-
|
|
488
|
-
# Open browser if requested
|
|
489
|
-
if options[:open]
|
|
490
|
-
url = "http://#{options[:host]}:#{options[:port]}"
|
|
491
|
-
open_browser(url)
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
PromptObjects::Server.start(
|
|
495
|
-
runtime: runtime,
|
|
496
|
-
host: options[:host],
|
|
497
|
-
port: options[:port],
|
|
498
|
-
env_path: env_path
|
|
499
|
-
)
|
|
500
|
-
end
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
def rebuild_frontend
|
|
504
|
-
# Find the frontend directory relative to this executable
|
|
505
|
-
gem_root = File.expand_path("../..", __FILE__)
|
|
506
|
-
frontend_dir = File.join(gem_root, "frontend")
|
|
507
|
-
|
|
508
|
-
unless Dir.exist?(frontend_dir)
|
|
509
|
-
# No frontend source (e.g., installed as gem without source) — skip
|
|
510
|
-
return
|
|
511
|
-
end
|
|
512
|
-
|
|
513
|
-
node_modules = File.join(frontend_dir, "node_modules")
|
|
514
|
-
unless Dir.exist?(node_modules)
|
|
515
|
-
puts "Frontend dependencies not installed. Run: cd frontend && npm install"
|
|
516
|
-
return
|
|
517
|
-
end
|
|
518
|
-
|
|
519
|
-
print "Building frontend... "
|
|
520
|
-
result = system("npm run build --prefix #{frontend_dir}", out: File::NULL, err: File::NULL)
|
|
521
|
-
if result
|
|
522
|
-
puts "done"
|
|
523
|
-
else
|
|
524
|
-
puts "failed (serving stale assets)"
|
|
525
|
-
end
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
def open_browser(url)
|
|
529
|
-
case RUBY_PLATFORM
|
|
530
|
-
when /darwin/
|
|
531
|
-
system("open", url)
|
|
532
|
-
when /linux/
|
|
533
|
-
system("xdg-open", url)
|
|
534
|
-
when /mswin|mingw/
|
|
535
|
-
system("start", url)
|
|
536
|
-
end
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
def resolve_environment(name_or_path)
|
|
540
|
-
# Check if it's already a path
|
|
541
|
-
if File.directory?(name_or_path)
|
|
542
|
-
manifest_path = File.join(name_or_path, "manifest.yml")
|
|
543
|
-
return name_or_path if File.exist?(manifest_path)
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
# Check in standard locations
|
|
547
|
-
locations = [
|
|
548
|
-
File.join(Dir.home, ".prompt_objects", "environments", name_or_path),
|
|
549
|
-
File.join(".", name_or_path)
|
|
550
|
-
]
|
|
551
|
-
|
|
552
|
-
locations.each do |path|
|
|
553
|
-
manifest_path = File.join(path, "manifest.yml")
|
|
554
|
-
return path if File.exist?(manifest_path)
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
nil
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
def load_all_objects(runtime, env_path)
|
|
561
|
-
objects_dir = File.join(env_path, "objects")
|
|
562
|
-
return unless Dir.exist?(objects_dir)
|
|
563
|
-
|
|
564
|
-
Dir.glob(File.join(objects_dir, "*.md")).each do |path|
|
|
565
|
-
runtime.load_prompt_object(path)
|
|
566
|
-
rescue StandardError => e
|
|
567
|
-
$stderr.puts "Warning: Failed to load #{path}: #{e.message}"
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
runtime.registry.prompt_objects.each do |po|
|
|
571
|
-
runtime.load_dependencies(po)
|
|
572
|
-
end
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
def print_message_help
|
|
576
|
-
puts <<~HELP
|
|
577
|
-
Usage: prompt_objects message [options] <environment> <po_name> "message"
|
|
578
|
-
|
|
579
|
-
Send a message to a prompt object and print the response.
|
|
580
|
-
|
|
581
|
-
Arguments:
|
|
582
|
-
environment Environment name or path
|
|
583
|
-
po_name Name of the prompt object to message
|
|
584
|
-
message The message to send (quoted string)
|
|
585
|
-
|
|
586
|
-
Options:
|
|
587
|
-
--json Output response as JSON
|
|
588
|
-
--events Also print the event log for this interaction
|
|
589
|
-
--new-thread Start a new thread for this message
|
|
590
|
-
--session ID Use a specific session ID
|
|
591
|
-
--help, -h Show this help
|
|
592
|
-
|
|
593
|
-
If a server is running for the environment, the message is sent via
|
|
594
|
-
HTTP to the running server (and streams to the web UI in real time).
|
|
595
|
-
Otherwise, a standalone runtime is started for the request.
|
|
596
|
-
|
|
597
|
-
Examples:
|
|
598
|
-
prompt_objects message my-env solver "What patterns do you see?"
|
|
599
|
-
prompt_objects message my-env solver "Solve task 1" --json
|
|
600
|
-
prompt_objects message my-env solver "Try again" --new-thread
|
|
601
|
-
HELP
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
def run_message(args)
|
|
605
|
-
options = {
|
|
606
|
-
json: false,
|
|
607
|
-
events: false,
|
|
608
|
-
new_thread: false,
|
|
609
|
-
session_id: nil
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
positional = []
|
|
613
|
-
skip_next = false
|
|
614
|
-
|
|
615
|
-
args.each_with_index do |arg, i|
|
|
616
|
-
if skip_next
|
|
617
|
-
skip_next = false
|
|
618
|
-
next
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
case arg
|
|
622
|
-
when "--json"
|
|
623
|
-
options[:json] = true
|
|
624
|
-
when "--events"
|
|
625
|
-
options[:events] = true
|
|
626
|
-
when "--new-thread"
|
|
627
|
-
options[:new_thread] = true
|
|
628
|
-
when "--session"
|
|
629
|
-
options[:session_id] = args[i + 1]
|
|
630
|
-
skip_next = true
|
|
631
|
-
when "--help", "-h"
|
|
632
|
-
print_message_help
|
|
633
|
-
exit 0
|
|
634
|
-
else
|
|
635
|
-
positional << arg
|
|
636
|
-
end
|
|
637
|
-
end
|
|
638
|
-
|
|
639
|
-
env_name = positional[0]
|
|
640
|
-
po_name = positional[1]
|
|
641
|
-
message_text = positional[2]
|
|
642
|
-
|
|
643
|
-
unless env_name && po_name && message_text
|
|
644
|
-
puts "Error: environment, po_name, and message are required"
|
|
645
|
-
puts "Run 'prompt_objects message --help' for usage"
|
|
646
|
-
exit 1
|
|
647
|
-
end
|
|
648
|
-
|
|
649
|
-
# Resolve environment
|
|
650
|
-
env_path = resolve_environment(env_name)
|
|
651
|
-
unless env_path
|
|
652
|
-
$stderr.puts "Error: environment '#{env_name}' not found"
|
|
653
|
-
exit 1
|
|
654
|
-
end
|
|
655
|
-
|
|
656
|
-
# Check for running server
|
|
657
|
-
require "prompt_objects/server"
|
|
658
|
-
server_info = PromptObjects::Server.read_server_file(env_path)
|
|
659
|
-
|
|
660
|
-
if server_info
|
|
661
|
-
# Send via HTTP to running server
|
|
662
|
-
send_message_via_http(server_info, po_name, message_text, options)
|
|
663
|
-
else
|
|
664
|
-
# Standalone mode
|
|
665
|
-
send_message_standalone(env_path, po_name, message_text, options)
|
|
666
|
-
end
|
|
667
|
-
end
|
|
668
|
-
|
|
669
|
-
def send_message_via_http(server_info, po_name, message_text, options)
|
|
670
|
-
require "net/http"
|
|
671
|
-
require "uri"
|
|
672
|
-
|
|
673
|
-
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/prompt_objects/#{po_name}/message")
|
|
674
|
-
|
|
675
|
-
body = { message: message_text }
|
|
676
|
-
body[:new_thread] = true if options[:new_thread]
|
|
677
|
-
body[:session_id] = options[:session_id] if options[:session_id]
|
|
678
|
-
|
|
679
|
-
begin
|
|
680
|
-
response = Net::HTTP.post(uri, body.to_json, "Content-Type" => "application/json")
|
|
681
|
-
data = JSON.parse(response.body)
|
|
682
|
-
|
|
683
|
-
if data["error"]
|
|
684
|
-
$stderr.puts "Error: #{data['error']}"
|
|
685
|
-
exit 1
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
if options[:json]
|
|
689
|
-
puts JSON.pretty_generate(data)
|
|
690
|
-
else
|
|
691
|
-
puts data["response"]
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
if options[:events] && data["session_id"]
|
|
695
|
-
events_uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events/session/#{data['session_id']}")
|
|
696
|
-
events_response = Net::HTTP.get(events_uri)
|
|
697
|
-
events_data = JSON.parse(events_response)
|
|
698
|
-
|
|
699
|
-
if events_data["events"]&.any?
|
|
700
|
-
$stderr.puts
|
|
701
|
-
$stderr.puts "--- Events ---"
|
|
702
|
-
events_data["events"].each do |e|
|
|
703
|
-
$stderr.puts "#{e['timestamp']} #{e['from']} -> #{e['to']}: #{e['summary']}"
|
|
704
|
-
end
|
|
705
|
-
$stderr.puts "--------------"
|
|
706
|
-
end
|
|
707
|
-
end
|
|
708
|
-
rescue Errno::ECONNREFUSED
|
|
709
|
-
$stderr.puts "Error: Server at #{server_info[:host]}:#{server_info[:port]} is not responding"
|
|
710
|
-
$stderr.puts "The .server file may be stale. Try running standalone."
|
|
711
|
-
exit 1
|
|
712
|
-
end
|
|
713
|
-
end
|
|
714
|
-
|
|
715
|
-
def send_message_standalone(env_path, po_name, message_text, options)
|
|
716
|
-
runtime = PromptObjects::Runtime.new(env_path: env_path)
|
|
717
|
-
load_all_objects(runtime, env_path)
|
|
718
|
-
|
|
719
|
-
po = runtime.registry.get(po_name)
|
|
720
|
-
unless po.is_a?(PromptObjects::PromptObject)
|
|
721
|
-
$stderr.puts "Error: prompt object '#{po_name}' not found"
|
|
722
|
-
$stderr.puts "Available: #{runtime.loaded_objects.join(', ')}"
|
|
723
|
-
exit 1
|
|
724
|
-
end
|
|
725
|
-
|
|
726
|
-
# Create new thread if requested
|
|
727
|
-
po.new_thread if options[:new_thread]
|
|
728
|
-
|
|
729
|
-
# Switch session if specified
|
|
730
|
-
if options[:session_id]
|
|
731
|
-
po.switch_session(options[:session_id])
|
|
732
|
-
end
|
|
733
|
-
|
|
734
|
-
session_id = po.session_id
|
|
735
|
-
log_start = runtime.bus.log.length
|
|
736
|
-
|
|
737
|
-
context = runtime.context
|
|
738
|
-
context.current_capability = "human"
|
|
739
|
-
|
|
740
|
-
# Log to bus
|
|
741
|
-
runtime.bus.publish(from: "human", to: po.name, message: message_text, session_id: session_id)
|
|
742
|
-
|
|
743
|
-
response = po.receive(message_text, context: context)
|
|
744
|
-
|
|
745
|
-
# Log response to bus
|
|
746
|
-
runtime.bus.publish(from: po.name, to: "human", message: response, session_id: session_id)
|
|
747
|
-
|
|
748
|
-
if options[:json]
|
|
749
|
-
data = {
|
|
750
|
-
response: response,
|
|
751
|
-
po_name: po.name,
|
|
752
|
-
session_id: session_id,
|
|
753
|
-
event_count: runtime.bus.log.length - log_start
|
|
754
|
-
}
|
|
755
|
-
puts JSON.pretty_generate(data)
|
|
756
|
-
else
|
|
757
|
-
puts response
|
|
758
|
-
end
|
|
759
|
-
|
|
760
|
-
if options[:events]
|
|
761
|
-
entries = runtime.bus.log[log_start..]
|
|
762
|
-
if entries&.any?
|
|
763
|
-
$stderr.puts
|
|
764
|
-
$stderr.puts "--- Events ---"
|
|
765
|
-
entries.each do |entry|
|
|
766
|
-
time = entry[:timestamp].strftime("%H:%M:%S")
|
|
767
|
-
$stderr.puts "#{time} #{entry[:from]} -> #{entry[:to]}: #{entry[:summary]}"
|
|
768
|
-
end
|
|
769
|
-
$stderr.puts "--------------"
|
|
770
|
-
end
|
|
771
|
-
end
|
|
772
|
-
end
|
|
773
|
-
|
|
774
|
-
def print_events_help
|
|
775
|
-
puts <<~HELP
|
|
776
|
-
Usage: prompt_objects events [options] <environment>
|
|
777
|
-
|
|
778
|
-
Show recent events from the message bus.
|
|
779
|
-
|
|
780
|
-
Arguments:
|
|
781
|
-
environment Environment name or path
|
|
782
|
-
|
|
783
|
-
Options:
|
|
784
|
-
--session ID Show events for a specific session
|
|
785
|
-
--count N Number of events to show (default: 50)
|
|
786
|
-
--json Output as JSON
|
|
787
|
-
--help, -h Show this help
|
|
788
|
-
|
|
789
|
-
Examples:
|
|
790
|
-
prompt_objects events my-env
|
|
791
|
-
prompt_objects events my-env --session abc-123
|
|
792
|
-
prompt_objects events my-env --count 100 --json
|
|
793
|
-
HELP
|
|
794
|
-
end
|
|
795
|
-
|
|
796
|
-
def run_events(args)
|
|
797
|
-
options = { count: 50, session_id: nil, json: false }
|
|
798
|
-
positional = []
|
|
799
|
-
skip_next = false
|
|
800
|
-
|
|
801
|
-
args.each_with_index do |arg, i|
|
|
802
|
-
if skip_next
|
|
803
|
-
skip_next = false
|
|
804
|
-
next
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
case arg
|
|
808
|
-
when "--session"
|
|
809
|
-
options[:session_id] = args[i + 1]
|
|
810
|
-
skip_next = true
|
|
811
|
-
when "--count"
|
|
812
|
-
options[:count] = args[i + 1].to_i
|
|
813
|
-
skip_next = true
|
|
814
|
-
when "--json"
|
|
815
|
-
options[:json] = true
|
|
816
|
-
when "--help", "-h"
|
|
817
|
-
print_events_help
|
|
818
|
-
exit 0
|
|
819
|
-
else
|
|
820
|
-
positional << arg
|
|
821
|
-
end
|
|
822
|
-
end
|
|
823
|
-
|
|
824
|
-
env_name = positional[0]
|
|
825
|
-
unless env_name
|
|
826
|
-
puts "Error: environment name required"
|
|
827
|
-
puts "Run 'prompt_objects events --help' for usage"
|
|
828
|
-
exit 1
|
|
829
|
-
end
|
|
830
|
-
|
|
831
|
-
env_path = resolve_environment(env_name)
|
|
832
|
-
unless env_path
|
|
833
|
-
$stderr.puts "Error: environment '#{env_name}' not found"
|
|
834
|
-
exit 1
|
|
835
|
-
end
|
|
836
|
-
|
|
837
|
-
# Check for running server
|
|
838
|
-
require "prompt_objects/server"
|
|
839
|
-
server_info = PromptObjects::Server.read_server_file(env_path)
|
|
840
|
-
|
|
841
|
-
if server_info
|
|
842
|
-
show_events_via_http(server_info, options)
|
|
843
|
-
else
|
|
844
|
-
show_events_standalone(env_path, options)
|
|
845
|
-
end
|
|
846
|
-
end
|
|
847
|
-
|
|
848
|
-
def show_events_via_http(server_info, options)
|
|
849
|
-
require "net/http"
|
|
850
|
-
require "uri"
|
|
851
|
-
|
|
852
|
-
if options[:session_id]
|
|
853
|
-
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events/session/#{options[:session_id]}")
|
|
854
|
-
else
|
|
855
|
-
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events?count=#{options[:count]}")
|
|
856
|
-
end
|
|
857
|
-
|
|
858
|
-
response = Net::HTTP.get(uri)
|
|
859
|
-
data = JSON.parse(response)
|
|
860
|
-
|
|
861
|
-
if options[:json]
|
|
862
|
-
puts JSON.pretty_generate(data)
|
|
863
|
-
else
|
|
864
|
-
print_events(data["events"] || [])
|
|
865
|
-
end
|
|
866
|
-
rescue Errno::ECONNREFUSED
|
|
867
|
-
$stderr.puts "Error: Server not responding"
|
|
868
|
-
exit 1
|
|
869
|
-
end
|
|
870
|
-
|
|
871
|
-
def show_events_standalone(env_path, options)
|
|
872
|
-
db_path = File.join(env_path, "sessions.db")
|
|
873
|
-
unless File.exist?(db_path)
|
|
874
|
-
puts "No event history (sessions.db not found)"
|
|
875
|
-
exit 0
|
|
876
|
-
end
|
|
877
|
-
|
|
878
|
-
store = PromptObjects::Session::Store.new(db_path)
|
|
879
|
-
|
|
880
|
-
events = if options[:session_id]
|
|
881
|
-
store.get_events(session_id: options[:session_id])
|
|
882
|
-
else
|
|
883
|
-
store.get_recent_events(options[:count])
|
|
884
|
-
end
|
|
885
|
-
|
|
886
|
-
if options[:json]
|
|
887
|
-
formatted = events.map do |e|
|
|
888
|
-
{
|
|
889
|
-
id: e[:id],
|
|
890
|
-
from: e[:from],
|
|
891
|
-
to: e[:to],
|
|
892
|
-
summary: e[:summary],
|
|
893
|
-
message: e[:message],
|
|
894
|
-
timestamp: e[:timestamp]&.iso8601,
|
|
895
|
-
session_id: e[:session_id]
|
|
896
|
-
}
|
|
897
|
-
end
|
|
898
|
-
puts JSON.pretty_generate(formatted)
|
|
899
|
-
else
|
|
900
|
-
print_events(events)
|
|
901
|
-
end
|
|
902
|
-
|
|
903
|
-
store.close
|
|
904
|
-
end
|
|
905
|
-
|
|
906
|
-
def print_events(events)
|
|
907
|
-
if events.empty?
|
|
908
|
-
puts "No events found."
|
|
909
|
-
return
|
|
910
|
-
end
|
|
911
|
-
|
|
912
|
-
events.each do |e|
|
|
913
|
-
ts = e[:timestamp] || e["timestamp"]
|
|
914
|
-
ts = ts.is_a?(Time) ? ts.strftime("%H:%M:%S") : ts&.split("T")&.last&.split(".")&.first
|
|
915
|
-
from = e[:from] || e["from"]
|
|
916
|
-
to = e[:to] || e["to"]
|
|
917
|
-
summary = e[:summary] || e["summary"]
|
|
918
|
-
|
|
919
|
-
line = "#{ts} #{from} -> #{to}: #{summary}"
|
|
920
|
-
line = line[0, 120] + "..." if line.length > 123
|
|
921
|
-
puts line
|
|
922
|
-
end
|
|
923
|
-
end
|
|
924
|
-
|
|
925
|
-
# === Explore Command ===
|
|
926
|
-
|
|
927
|
-
def run_explore(args)
|
|
928
|
-
options = { session_id: nil }
|
|
929
|
-
positional = []
|
|
930
|
-
skip_next = false
|
|
931
|
-
|
|
932
|
-
args.each_with_index do |arg, i|
|
|
933
|
-
if skip_next
|
|
934
|
-
skip_next = false
|
|
935
|
-
next
|
|
936
|
-
end
|
|
937
|
-
|
|
938
|
-
case arg
|
|
939
|
-
when "--session"
|
|
940
|
-
options[:session_id] = args[i + 1]
|
|
941
|
-
skip_next = true
|
|
942
|
-
when "--help", "-h"
|
|
943
|
-
print_explore_help
|
|
944
|
-
exit 0
|
|
945
|
-
else
|
|
946
|
-
positional << arg
|
|
947
|
-
end
|
|
948
|
-
end
|
|
949
|
-
|
|
950
|
-
explorer_html = File.expand_path("../tools/thread-explorer.html", __dir__)
|
|
951
|
-
|
|
952
|
-
# No args: just open the empty visualizer
|
|
953
|
-
unless positional[0]
|
|
954
|
-
open_in_browser("file://#{explorer_html}")
|
|
955
|
-
puts "Opened Thread Explorer in browser"
|
|
956
|
-
exit 0
|
|
957
|
-
end
|
|
958
|
-
|
|
959
|
-
env_path = resolve_environment(positional[0])
|
|
960
|
-
unless env_path
|
|
961
|
-
$stderr.puts "Error: environment '#{positional[0]}' not found"
|
|
962
|
-
exit 1
|
|
963
|
-
end
|
|
964
|
-
|
|
965
|
-
db_path = File.join(env_path, "sessions.db")
|
|
966
|
-
unless File.exist?(db_path)
|
|
967
|
-
$stderr.puts "No session data found (sessions.db not found)"
|
|
968
|
-
$stderr.puts "Opening empty Thread Explorer..."
|
|
969
|
-
open_in_browser("file://#{explorer_html}")
|
|
970
|
-
exit 0
|
|
971
|
-
end
|
|
972
|
-
|
|
973
|
-
store = PromptObjects::Session::Store.new(db_path)
|
|
974
|
-
|
|
975
|
-
if options[:session_id]
|
|
976
|
-
# Export a specific thread
|
|
977
|
-
export_and_open(store, options[:session_id], explorer_html)
|
|
978
|
-
else
|
|
979
|
-
# List sessions and let user choose, or export the most recent root thread
|
|
980
|
-
# Query root sessions directly (list_sessions requires po_name)
|
|
981
|
-
rows = store.instance_variable_get(:@db).execute(<<~SQL)
|
|
982
|
-
SELECT * FROM sessions
|
|
983
|
-
WHERE thread_type IS NULL OR thread_type = 'root'
|
|
984
|
-
ORDER BY created_at ASC
|
|
985
|
-
SQL
|
|
986
|
-
root_sessions = rows.map { |r| { id: r["id"], po_name: r["po_name"], name: r["name"], thread_type: r["thread_type"], created_at: r["created_at"] } }
|
|
987
|
-
|
|
988
|
-
if root_sessions.empty?
|
|
989
|
-
$stderr.puts "No root sessions found"
|
|
990
|
-
open_in_browser("file://#{explorer_html}")
|
|
991
|
-
exit 0
|
|
992
|
-
end
|
|
993
|
-
|
|
994
|
-
# Show available sessions
|
|
995
|
-
puts "Root threads:"
|
|
996
|
-
root_sessions.last(10).each_with_index do |s, i|
|
|
997
|
-
name = s[:name] || "(unnamed)"
|
|
998
|
-
po = s[:po_name] || "?"
|
|
999
|
-
time = s[:created_at]
|
|
1000
|
-
time_str = time.is_a?(Time) ? time.strftime("%Y-%m-%d %H:%M") : time.to_s[0, 16]
|
|
1001
|
-
puts " #{i + 1}. [#{po}] #{name} (#{time_str}) #{s[:id]}"
|
|
1002
|
-
end
|
|
1003
|
-
|
|
1004
|
-
puts
|
|
1005
|
-
puts "Opening most recent thread..."
|
|
1006
|
-
export_and_open(store, root_sessions.last[:id], explorer_html)
|
|
1007
|
-
end
|
|
1008
|
-
|
|
1009
|
-
store.close
|
|
1010
|
-
end
|
|
1011
|
-
|
|
1012
|
-
def export_and_open(store, session_id, explorer_html)
|
|
1013
|
-
data = store.export_thread_tree_json(session_id)
|
|
1014
|
-
unless data
|
|
1015
|
-
$stderr.puts "Error: session '#{session_id}' not found"
|
|
1016
|
-
exit 1
|
|
1017
|
-
end
|
|
1018
|
-
|
|
1019
|
-
json_str = JSON.generate(data)
|
|
1020
|
-
|
|
1021
|
-
# Read the explorer HTML and inject the data as auto-load
|
|
1022
|
-
html_content = File.read(explorer_html)
|
|
1023
|
-
inject_script = <<~JS
|
|
1024
|
-
<script>
|
|
1025
|
-
// Auto-load embedded data
|
|
1026
|
-
window.addEventListener('DOMContentLoaded', function() {
|
|
1027
|
-
var data = #{json_str};
|
|
1028
|
-
if (typeof loadData === 'function') loadData(data);
|
|
1029
|
-
});
|
|
1030
|
-
</script>
|
|
1031
|
-
JS
|
|
1032
|
-
|
|
1033
|
-
# Insert before </body>
|
|
1034
|
-
html_content = html_content.sub("</body>", "#{inject_script}</body>")
|
|
1035
|
-
|
|
1036
|
-
# Write to temp file
|
|
1037
|
-
require "tempfile"
|
|
1038
|
-
tmp = Tempfile.new(["thread-explorer-", ".html"])
|
|
1039
|
-
tmp.write(html_content)
|
|
1040
|
-
tmp.close
|
|
1041
|
-
|
|
1042
|
-
open_in_browser("file://#{tmp.path}")
|
|
1043
|
-
puts "Opened Thread Explorer for session #{session_id}"
|
|
1044
|
-
|
|
1045
|
-
# Keep temp file around briefly so the browser can load it
|
|
1046
|
-
sleep 2
|
|
1047
|
-
end
|
|
1048
|
-
|
|
1049
|
-
def open_in_browser(url)
|
|
1050
|
-
case RUBY_PLATFORM
|
|
1051
|
-
when /darwin/
|
|
1052
|
-
system("open", url)
|
|
1053
|
-
when /linux/
|
|
1054
|
-
system("xdg-open", url)
|
|
1055
|
-
when /mswin|mingw|cygwin/
|
|
1056
|
-
system("start", url)
|
|
1057
|
-
else
|
|
1058
|
-
$stderr.puts "Open this URL in your browser: #{url}"
|
|
1059
|
-
end
|
|
1060
|
-
end
|
|
1061
|
-
|
|
1062
|
-
def print_explore_help
|
|
1063
|
-
puts <<~HELP
|
|
1064
|
-
Usage: prompt_objects explore [env] [options]
|
|
1065
|
-
|
|
1066
|
-
Open the Thread Explorer to visualize conversation threads.
|
|
1067
|
-
|
|
1068
|
-
Arguments:
|
|
1069
|
-
env Environment name or path (optional)
|
|
1070
|
-
|
|
1071
|
-
Options:
|
|
1072
|
-
--session ID Open a specific thread by session ID
|
|
1073
|
-
--help, -h Show this help message
|
|
1074
|
-
|
|
1075
|
-
Examples:
|
|
1076
|
-
prompt_objects explore # Open empty explorer
|
|
1077
|
-
prompt_objects explore my-env # Open most recent thread
|
|
1078
|
-
prompt_objects explore my-env --session abc123 # Open specific thread
|
|
1079
|
-
HELP
|
|
1080
|
-
end
|
|
1081
|
-
|
|
1082
|
-
# === Main Entry Point ===
|
|
1083
|
-
|
|
1084
|
-
def run_env(args)
|
|
1085
|
-
cmd = PromptObjects::CLI::EnvCommand.new
|
|
1086
|
-
cmd.run(args)
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
|
-
def main
|
|
1090
|
-
if ARGV.empty?
|
|
1091
|
-
print_main_help
|
|
1092
|
-
exit 0
|
|
1093
|
-
end
|
|
1094
|
-
|
|
1095
|
-
command = ARGV[0]
|
|
1096
|
-
args = ARGV[1..]
|
|
1097
|
-
|
|
1098
|
-
case command
|
|
1099
|
-
when "env"
|
|
1100
|
-
run_env(args)
|
|
1101
|
-
when "repl"
|
|
1102
|
-
run_repl(args)
|
|
1103
|
-
when "serve"
|
|
1104
|
-
run_serve(args)
|
|
1105
|
-
when "message", "msg"
|
|
1106
|
-
run_message(args)
|
|
1107
|
-
when "events"
|
|
1108
|
-
run_events(args)
|
|
1109
|
-
when "explore"
|
|
1110
|
-
run_explore(args)
|
|
1111
|
-
when "help", "--help", "-h"
|
|
1112
|
-
print_main_help
|
|
1113
|
-
else
|
|
1114
|
-
# Legacy behavior: treat as repl with name argument
|
|
1115
|
-
run_repl(ARGV)
|
|
1116
|
-
end
|
|
1117
|
-
end
|
|
1118
|
-
|
|
1119
|
-
main
|
|
12
|
+
PromptObjects::CLI.run(ARGV)
|