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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module PromptObjects
|
|
6
|
+
module CLI
|
|
7
|
+
# `explore` command: open the Thread Explorer visualizer in a browser.
|
|
8
|
+
class ExploreCommand
|
|
9
|
+
def run(args)
|
|
10
|
+
options = { session_id: nil }
|
|
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 "--help", "-h"
|
|
25
|
+
help
|
|
26
|
+
exit 0
|
|
27
|
+
else
|
|
28
|
+
positional << arg
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
explorer_html = File.expand_path("../../../tools/thread-explorer.html", __dir__)
|
|
33
|
+
|
|
34
|
+
# No args: just open the empty visualizer
|
|
35
|
+
unless positional[0]
|
|
36
|
+
Helpers.open_in_browser("file://#{explorer_html}")
|
|
37
|
+
puts "Opened Thread Explorer in browser"
|
|
38
|
+
exit 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
env_path = Helpers.resolve_environment(positional[0])
|
|
42
|
+
unless env_path
|
|
43
|
+
$stderr.puts "Error: environment '#{positional[0]}' not found"
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
db_path = File.join(env_path, "sessions.db")
|
|
48
|
+
unless File.exist?(db_path)
|
|
49
|
+
$stderr.puts "No session data found (sessions.db not found)"
|
|
50
|
+
$stderr.puts "Opening empty Thread Explorer..."
|
|
51
|
+
Helpers.open_in_browser("file://#{explorer_html}")
|
|
52
|
+
exit 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
store = PromptObjects::Session::Store.new(db_path)
|
|
56
|
+
|
|
57
|
+
if options[:session_id]
|
|
58
|
+
# Export a specific thread
|
|
59
|
+
export_and_open(store, options[:session_id], explorer_html)
|
|
60
|
+
else
|
|
61
|
+
# List sessions and let user choose, or export the most recent root thread
|
|
62
|
+
# Query root sessions directly (list_sessions requires po_name)
|
|
63
|
+
rows = store.instance_variable_get(:@db).execute(<<~SQL)
|
|
64
|
+
SELECT * FROM sessions
|
|
65
|
+
WHERE thread_type IS NULL OR thread_type = 'root'
|
|
66
|
+
ORDER BY created_at ASC
|
|
67
|
+
SQL
|
|
68
|
+
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"] } }
|
|
69
|
+
|
|
70
|
+
if root_sessions.empty?
|
|
71
|
+
$stderr.puts "No root sessions found"
|
|
72
|
+
Helpers.open_in_browser("file://#{explorer_html}")
|
|
73
|
+
exit 0
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Show available sessions
|
|
77
|
+
puts "Root threads:"
|
|
78
|
+
root_sessions.last(10).each_with_index do |s, i|
|
|
79
|
+
name = s[:name] || "(unnamed)"
|
|
80
|
+
po = s[:po_name] || "?"
|
|
81
|
+
time = s[:created_at]
|
|
82
|
+
time_str = time.is_a?(Time) ? time.strftime("%Y-%m-%d %H:%M") : time.to_s[0, 16]
|
|
83
|
+
puts " #{i + 1}. [#{po}] #{name} (#{time_str}) #{s[:id]}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
puts
|
|
87
|
+
puts "Opening most recent thread..."
|
|
88
|
+
export_and_open(store, root_sessions.last[:id], explorer_html)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
store.close
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def help
|
|
95
|
+
prog = Helpers.program_name
|
|
96
|
+
puts <<~HELP
|
|
97
|
+
Usage: #{prog} explore [env] [options]
|
|
98
|
+
|
|
99
|
+
Open the Thread Explorer to visualize conversation threads.
|
|
100
|
+
|
|
101
|
+
Arguments:
|
|
102
|
+
env Environment name or path (optional)
|
|
103
|
+
|
|
104
|
+
Options:
|
|
105
|
+
--session ID Open a specific thread by session ID
|
|
106
|
+
--help, -h Show this help message
|
|
107
|
+
|
|
108
|
+
Examples:
|
|
109
|
+
#{prog} explore # Open empty explorer
|
|
110
|
+
#{prog} explore my-env # Open most recent thread
|
|
111
|
+
#{prog} explore my-env --session abc123 # Open specific thread
|
|
112
|
+
HELP
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def export_and_open(store, session_id, explorer_html)
|
|
118
|
+
data = store.export_thread_tree_json(session_id)
|
|
119
|
+
unless data
|
|
120
|
+
$stderr.puts "Error: session '#{session_id}' not found"
|
|
121
|
+
exit 1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
json_str = JSON.generate(data)
|
|
125
|
+
|
|
126
|
+
# Read the explorer HTML and inject the data as auto-load
|
|
127
|
+
html_content = File.read(explorer_html)
|
|
128
|
+
inject_script = <<~JS
|
|
129
|
+
<script>
|
|
130
|
+
// Auto-load embedded data
|
|
131
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
132
|
+
var data = #{json_str};
|
|
133
|
+
if (typeof loadData === 'function') loadData(data);
|
|
134
|
+
});
|
|
135
|
+
</script>
|
|
136
|
+
JS
|
|
137
|
+
|
|
138
|
+
# Insert before </body>
|
|
139
|
+
html_content = html_content.sub("</body>", "#{inject_script}</body>")
|
|
140
|
+
|
|
141
|
+
# Write to temp file
|
|
142
|
+
require "tempfile"
|
|
143
|
+
tmp = Tempfile.new(["thread-explorer-", ".html"])
|
|
144
|
+
tmp.write(html_content)
|
|
145
|
+
tmp.close
|
|
146
|
+
|
|
147
|
+
Helpers.open_in_browser("file://#{tmp.path}")
|
|
148
|
+
puts "Opened Thread Explorer for session #{session_id}"
|
|
149
|
+
|
|
150
|
+
# Keep temp file around briefly so the browser can load it
|
|
151
|
+
sleep 2
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PromptObjects
|
|
4
|
+
module CLI
|
|
5
|
+
# Shared helpers for CLI commands.
|
|
6
|
+
module Helpers
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# The name the CLI was invoked as (e.g. "prompt_objects" or "poop").
|
|
10
|
+
# Used in usage/help text so each executable describes itself.
|
|
11
|
+
def program_name
|
|
12
|
+
name = File.basename($PROGRAM_NAME.to_s)
|
|
13
|
+
name.empty? ? "prompt_objects" : name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Resolve an environment name or path to an environment directory.
|
|
17
|
+
# @param name_or_path [String]
|
|
18
|
+
# @return [String, nil] Path to the environment, or nil if not found
|
|
19
|
+
def resolve_environment(name_or_path)
|
|
20
|
+
# Check if it's already a path
|
|
21
|
+
if File.directory?(name_or_path)
|
|
22
|
+
manifest_path = File.join(name_or_path, "manifest.yml")
|
|
23
|
+
return name_or_path if File.exist?(manifest_path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Check in standard locations
|
|
27
|
+
locations = [
|
|
28
|
+
File.join(Dir.home, ".prompt_objects", "environments", name_or_path),
|
|
29
|
+
File.join(".", name_or_path)
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
locations.each do |path|
|
|
33
|
+
manifest_path = File.join(path, "manifest.yml")
|
|
34
|
+
return path if File.exist?(manifest_path)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Load all prompt objects from an environment into a runtime.
|
|
41
|
+
def load_all_objects(runtime, env_path)
|
|
42
|
+
objects_dir = File.join(env_path, "objects")
|
|
43
|
+
return unless Dir.exist?(objects_dir)
|
|
44
|
+
|
|
45
|
+
Dir.glob(File.join(objects_dir, "*.md")).each do |path|
|
|
46
|
+
runtime.load_prompt_object(path)
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
$stderr.puts "Warning: Failed to load #{path}: #{e.message}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
runtime.registry.prompt_objects.each do |po|
|
|
52
|
+
runtime.load_dependencies(po)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Open a URL in the platform browser.
|
|
57
|
+
def open_in_browser(url)
|
|
58
|
+
case RUBY_PLATFORM
|
|
59
|
+
when /darwin/
|
|
60
|
+
system("open", url)
|
|
61
|
+
when /linux/
|
|
62
|
+
system("xdg-open", url)
|
|
63
|
+
when /mswin|mingw|cygwin/
|
|
64
|
+
system("start", url)
|
|
65
|
+
else
|
|
66
|
+
$stderr.puts "Open this URL in your browser: #{url}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Print a list of bus events in the standard one-line format.
|
|
71
|
+
def print_events(events)
|
|
72
|
+
if events.empty?
|
|
73
|
+
puts "No events found."
|
|
74
|
+
return
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
events.each do |e|
|
|
78
|
+
ts = e[:timestamp] || e["timestamp"]
|
|
79
|
+
ts = ts.is_a?(Time) ? ts.strftime("%H:%M:%S") : ts&.split("T")&.last&.split(".")&.first
|
|
80
|
+
from = e[:from] || e["from"]
|
|
81
|
+
to = e[:to] || e["to"]
|
|
82
|
+
summary = e[:summary] || e["summary"]
|
|
83
|
+
|
|
84
|
+
line = "#{ts} #{from} -> #{to}: #{summary}"
|
|
85
|
+
line = line[0, 120] + "..." if line.length > 123
|
|
86
|
+
puts line
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module PromptObjects
|
|
6
|
+
module CLI
|
|
7
|
+
# `message` command: send a message to a PO and print the response.
|
|
8
|
+
# Routes through a running server when one exists, standalone otherwise.
|
|
9
|
+
class MessageCommand
|
|
10
|
+
def run(args)
|
|
11
|
+
options = {
|
|
12
|
+
json: false,
|
|
13
|
+
events: false,
|
|
14
|
+
new_thread: false,
|
|
15
|
+
session_id: nil
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
positional = []
|
|
19
|
+
skip_next = false
|
|
20
|
+
|
|
21
|
+
args.each_with_index do |arg, i|
|
|
22
|
+
if skip_next
|
|
23
|
+
skip_next = false
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
case arg
|
|
28
|
+
when "--json"
|
|
29
|
+
options[:json] = true
|
|
30
|
+
when "--events"
|
|
31
|
+
options[:events] = true
|
|
32
|
+
when "--new-thread"
|
|
33
|
+
options[:new_thread] = true
|
|
34
|
+
when "--session"
|
|
35
|
+
options[:session_id] = args[i + 1]
|
|
36
|
+
skip_next = true
|
|
37
|
+
when "--help", "-h"
|
|
38
|
+
help
|
|
39
|
+
exit 0
|
|
40
|
+
else
|
|
41
|
+
positional << arg
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
env_name = positional[0]
|
|
46
|
+
po_name = positional[1]
|
|
47
|
+
message_text = positional[2]
|
|
48
|
+
|
|
49
|
+
unless env_name && po_name && message_text
|
|
50
|
+
puts "Error: environment, po_name, and message are required"
|
|
51
|
+
puts "Run '#{Helpers.program_name} message --help' for usage"
|
|
52
|
+
exit 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Resolve environment
|
|
56
|
+
env_path = Helpers.resolve_environment(env_name)
|
|
57
|
+
unless env_path
|
|
58
|
+
$stderr.puts "Error: environment '#{env_name}' not found"
|
|
59
|
+
exit 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Check for running server
|
|
63
|
+
require "prompt_objects/server"
|
|
64
|
+
server_info = PromptObjects::Server.read_server_file(env_path)
|
|
65
|
+
|
|
66
|
+
if server_info
|
|
67
|
+
# Send via HTTP to running server
|
|
68
|
+
send_via_http(server_info, po_name, message_text, options)
|
|
69
|
+
else
|
|
70
|
+
# Standalone mode
|
|
71
|
+
send_standalone(env_path, po_name, message_text, options)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def help
|
|
76
|
+
prog = Helpers.program_name
|
|
77
|
+
puts <<~HELP
|
|
78
|
+
Usage: #{prog} message [options] <environment> <po_name> "message"
|
|
79
|
+
|
|
80
|
+
Send a message to a prompt object and print the response.
|
|
81
|
+
|
|
82
|
+
Arguments:
|
|
83
|
+
environment Environment name or path
|
|
84
|
+
po_name Name of the prompt object to message
|
|
85
|
+
message The message to send (quoted string)
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
--json Output response as JSON
|
|
89
|
+
--events Also print the event log for this interaction
|
|
90
|
+
--new-thread Start a new thread for this message
|
|
91
|
+
--session ID Continue a specific thread ID (session alias)
|
|
92
|
+
--help, -h Show this help
|
|
93
|
+
|
|
94
|
+
If a server is running for the environment, the message is sent via
|
|
95
|
+
HTTP to the running server (and streams to the web UI in real time).
|
|
96
|
+
Otherwise, a standalone runtime is started for the request.
|
|
97
|
+
|
|
98
|
+
Examples:
|
|
99
|
+
#{prog} message my-env solver "What patterns do you see?"
|
|
100
|
+
#{prog} message my-env solver "Solve task 1" --json
|
|
101
|
+
#{prog} message my-env solver "Try again" --new-thread
|
|
102
|
+
HELP
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def send_via_http(server_info, po_name, message_text, options)
|
|
108
|
+
require "net/http"
|
|
109
|
+
require "uri"
|
|
110
|
+
|
|
111
|
+
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/prompt_objects/#{po_name}/message")
|
|
112
|
+
|
|
113
|
+
body = { message: message_text }
|
|
114
|
+
body[:new_thread] = true if options[:new_thread]
|
|
115
|
+
body[:session_id] = options[:session_id] if options[:session_id]
|
|
116
|
+
body[:client_request_id] = SecureRandom.uuid
|
|
117
|
+
body[:from_source] = "api:cli"
|
|
118
|
+
|
|
119
|
+
begin
|
|
120
|
+
response = Net::HTTP.post(uri, body.to_json, "Content-Type" => "application/json")
|
|
121
|
+
data = JSON.parse(response.body)
|
|
122
|
+
|
|
123
|
+
if data["error"]
|
|
124
|
+
$stderr.puts "Error: #{data['error']}"
|
|
125
|
+
exit 1
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if options[:json]
|
|
129
|
+
puts JSON.pretty_generate(data)
|
|
130
|
+
else
|
|
131
|
+
puts data["response"]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if options[:events] && data["session_id"]
|
|
135
|
+
events_uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events/session/#{data['session_id']}")
|
|
136
|
+
events_response = Net::HTTP.get(events_uri)
|
|
137
|
+
events_data = JSON.parse(events_response)
|
|
138
|
+
|
|
139
|
+
if events_data["events"]&.any?
|
|
140
|
+
$stderr.puts
|
|
141
|
+
$stderr.puts "--- Events ---"
|
|
142
|
+
events_data["events"].each do |e|
|
|
143
|
+
$stderr.puts "#{e['timestamp']} #{e['from']} -> #{e['to']}: #{e['summary']}"
|
|
144
|
+
end
|
|
145
|
+
$stderr.puts "--------------"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
rescue Errno::ECONNREFUSED
|
|
149
|
+
$stderr.puts "Error: Server at #{server_info[:host]}:#{server_info[:port]} is not responding"
|
|
150
|
+
$stderr.puts "The .server file may be stale. Try running standalone."
|
|
151
|
+
exit 1
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def send_standalone(env_path, po_name, message_text, options)
|
|
156
|
+
runtime = PromptObjects::Runtime.new(env_path: env_path)
|
|
157
|
+
Helpers.load_all_objects(runtime, env_path)
|
|
158
|
+
|
|
159
|
+
po = runtime.registry.get(po_name)
|
|
160
|
+
unless po.is_a?(PromptObjects::PromptObject)
|
|
161
|
+
$stderr.puts "Error: prompt object '#{po_name}' not found"
|
|
162
|
+
$stderr.puts "Available: #{runtime.loaded_objects.join(', ')}"
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
workspace_id = runtime.active_session(initiated_by: "api:cli")[:id]
|
|
167
|
+
thread_id = options[:session_id]
|
|
168
|
+
if options[:new_thread]
|
|
169
|
+
thread_id = runtime.create_runtime_thread(
|
|
170
|
+
workspace_session_id: workspace_id,
|
|
171
|
+
po_name: po_name,
|
|
172
|
+
thread_type: "api",
|
|
173
|
+
name: "CLI thread"
|
|
174
|
+
).id
|
|
175
|
+
end
|
|
176
|
+
run, response = runtime.execute_message_run(
|
|
177
|
+
target_po: po_name,
|
|
178
|
+
content: message_text,
|
|
179
|
+
from_source: "api:cli",
|
|
180
|
+
workspace_session_id: workspace_id,
|
|
181
|
+
thread_id: thread_id,
|
|
182
|
+
client_request_id: SecureRandom.uuid,
|
|
183
|
+
tui_mode: true
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if options[:json]
|
|
187
|
+
puts JSON.pretty_generate(
|
|
188
|
+
response: response,
|
|
189
|
+
po_name: po_name,
|
|
190
|
+
workspace_session_id: run.workspace_session_id,
|
|
191
|
+
thread_id: run.thread_id,
|
|
192
|
+
session_id: run.thread_id,
|
|
193
|
+
run_id: run.id,
|
|
194
|
+
status: run.status
|
|
195
|
+
)
|
|
196
|
+
else
|
|
197
|
+
puts response
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
print_run_events(runtime, run.id) if options[:events]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def print_run_events(runtime, run_id)
|
|
204
|
+
events = Repositories::EventRepository.new(runtime.session_store).run_events(run_id: run_id)
|
|
205
|
+
return if events.empty?
|
|
206
|
+
|
|
207
|
+
$stderr.puts
|
|
208
|
+
$stderr.puts "--- Run Events ---"
|
|
209
|
+
events.each do |event|
|
|
210
|
+
$stderr.puts "#{event.timestamp.strftime('%H:%M:%S')} #{event.kind}"
|
|
211
|
+
end
|
|
212
|
+
$stderr.puts "------------------"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|