prompt_objects 0.1.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 (117) hide show
  1. checksums.yaml +7 -0
  2. data/CLAUDE.md +108 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +231 -0
  5. data/IMPLEMENTATION_PLAN.md +1073 -0
  6. data/LICENSE +21 -0
  7. data/README.md +73 -0
  8. data/Rakefile +27 -0
  9. data/design-doc-v2.md +1232 -0
  10. data/exe/prompt_objects +572 -0
  11. data/exe/prompt_objects_mcp +34 -0
  12. data/frontend/.gitignore +3 -0
  13. data/frontend/index.html +13 -0
  14. data/frontend/package-lock.json +4417 -0
  15. data/frontend/package.json +32 -0
  16. data/frontend/postcss.config.js +6 -0
  17. data/frontend/src/App.tsx +95 -0
  18. data/frontend/src/components/CapabilitiesPanel.tsx +44 -0
  19. data/frontend/src/components/ChatPanel.tsx +251 -0
  20. data/frontend/src/components/Dashboard.tsx +83 -0
  21. data/frontend/src/components/Header.tsx +141 -0
  22. data/frontend/src/components/MarkdownMessage.tsx +153 -0
  23. data/frontend/src/components/MessageBus.tsx +55 -0
  24. data/frontend/src/components/ModelSelector.tsx +112 -0
  25. data/frontend/src/components/NotificationPanel.tsx +134 -0
  26. data/frontend/src/components/POCard.tsx +56 -0
  27. data/frontend/src/components/PODetail.tsx +117 -0
  28. data/frontend/src/components/PromptPanel.tsx +51 -0
  29. data/frontend/src/components/SessionsPanel.tsx +174 -0
  30. data/frontend/src/components/ThreadsSidebar.tsx +119 -0
  31. data/frontend/src/components/index.ts +11 -0
  32. data/frontend/src/hooks/useWebSocket.ts +363 -0
  33. data/frontend/src/index.css +37 -0
  34. data/frontend/src/main.tsx +10 -0
  35. data/frontend/src/store/index.ts +246 -0
  36. data/frontend/src/types/index.ts +146 -0
  37. data/frontend/tailwind.config.js +25 -0
  38. data/frontend/tsconfig.json +30 -0
  39. data/frontend/vite.config.ts +29 -0
  40. data/lib/prompt_objects/capability.rb +46 -0
  41. data/lib/prompt_objects/cli.rb +431 -0
  42. data/lib/prompt_objects/connectors/base.rb +73 -0
  43. data/lib/prompt_objects/connectors/mcp.rb +524 -0
  44. data/lib/prompt_objects/environment/exporter.rb +83 -0
  45. data/lib/prompt_objects/environment/git.rb +118 -0
  46. data/lib/prompt_objects/environment/importer.rb +159 -0
  47. data/lib/prompt_objects/environment/manager.rb +401 -0
  48. data/lib/prompt_objects/environment/manifest.rb +218 -0
  49. data/lib/prompt_objects/environment.rb +283 -0
  50. data/lib/prompt_objects/human_queue.rb +144 -0
  51. data/lib/prompt_objects/llm/anthropic_adapter.rb +137 -0
  52. data/lib/prompt_objects/llm/factory.rb +84 -0
  53. data/lib/prompt_objects/llm/gemini_adapter.rb +209 -0
  54. data/lib/prompt_objects/llm/openai_adapter.rb +104 -0
  55. data/lib/prompt_objects/llm/response.rb +61 -0
  56. data/lib/prompt_objects/loader.rb +32 -0
  57. data/lib/prompt_objects/mcp/server.rb +167 -0
  58. data/lib/prompt_objects/mcp/tools/get_conversation.rb +60 -0
  59. data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +54 -0
  60. data/lib/prompt_objects/mcp/tools/inspect_po.rb +73 -0
  61. data/lib/prompt_objects/mcp/tools/list_prompt_objects.rb +37 -0
  62. data/lib/prompt_objects/mcp/tools/respond_to_request.rb +68 -0
  63. data/lib/prompt_objects/mcp/tools/send_message.rb +71 -0
  64. data/lib/prompt_objects/message_bus.rb +97 -0
  65. data/lib/prompt_objects/primitive.rb +13 -0
  66. data/lib/prompt_objects/primitives/http_get.rb +72 -0
  67. data/lib/prompt_objects/primitives/list_files.rb +95 -0
  68. data/lib/prompt_objects/primitives/read_file.rb +81 -0
  69. data/lib/prompt_objects/primitives/write_file.rb +73 -0
  70. data/lib/prompt_objects/prompt_object.rb +415 -0
  71. data/lib/prompt_objects/registry.rb +88 -0
  72. data/lib/prompt_objects/server/api/routes.rb +297 -0
  73. data/lib/prompt_objects/server/app.rb +174 -0
  74. data/lib/prompt_objects/server/file_watcher.rb +113 -0
  75. data/lib/prompt_objects/server/public/assets/index-2acS2FYZ.js +77 -0
  76. data/lib/prompt_objects/server/public/assets/index-DXU5uRXQ.css +1 -0
  77. data/lib/prompt_objects/server/public/index.html +14 -0
  78. data/lib/prompt_objects/server/websocket_handler.rb +619 -0
  79. data/lib/prompt_objects/server.rb +166 -0
  80. data/lib/prompt_objects/session/store.rb +826 -0
  81. data/lib/prompt_objects/universal/add_capability.rb +74 -0
  82. data/lib/prompt_objects/universal/add_primitive.rb +113 -0
  83. data/lib/prompt_objects/universal/ask_human.rb +109 -0
  84. data/lib/prompt_objects/universal/create_capability.rb +219 -0
  85. data/lib/prompt_objects/universal/create_primitive.rb +170 -0
  86. data/lib/prompt_objects/universal/list_capabilities.rb +55 -0
  87. data/lib/prompt_objects/universal/list_primitives.rb +145 -0
  88. data/lib/prompt_objects/universal/modify_primitive.rb +180 -0
  89. data/lib/prompt_objects/universal/request_primitive.rb +287 -0
  90. data/lib/prompt_objects/universal/think.rb +41 -0
  91. data/lib/prompt_objects/universal/verify_primitive.rb +173 -0
  92. data/lib/prompt_objects.rb +62 -0
  93. data/objects/coordinator.md +48 -0
  94. data/objects/greeter.md +30 -0
  95. data/objects/reader.md +33 -0
  96. data/prompt_objects.gemspec +50 -0
  97. data/templates/basic/.gitignore +2 -0
  98. data/templates/basic/manifest.yml +7 -0
  99. data/templates/basic/objects/basic.md +32 -0
  100. data/templates/developer/.gitignore +5 -0
  101. data/templates/developer/manifest.yml +17 -0
  102. data/templates/developer/objects/code_reviewer.md +33 -0
  103. data/templates/developer/objects/coordinator.md +39 -0
  104. data/templates/developer/objects/debugger.md +35 -0
  105. data/templates/empty/.gitignore +5 -0
  106. data/templates/empty/manifest.yml +14 -0
  107. data/templates/empty/objects/.gitkeep +0 -0
  108. data/templates/empty/objects/assistant.md +41 -0
  109. data/templates/minimal/.gitignore +5 -0
  110. data/templates/minimal/manifest.yml +7 -0
  111. data/templates/minimal/objects/assistant.md +41 -0
  112. data/templates/writer/.gitignore +5 -0
  113. data/templates/writer/manifest.yml +17 -0
  114. data/templates/writer/objects/coordinator.md +33 -0
  115. data/templates/writer/objects/editor.md +33 -0
  116. data/templates/writer/objects/researcher.md +34 -0
  117. metadata +343 -0
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+
6
+ module PromptObjects
7
+ module Primitives
8
+ # Primitive capability to fetch content from a URL.
9
+ class HttpGet < Primitive
10
+ def name
11
+ "http_get"
12
+ end
13
+
14
+ def description
15
+ "Fetch content from a URL via HTTP GET request"
16
+ end
17
+
18
+ def parameters
19
+ {
20
+ type: "object",
21
+ properties: {
22
+ url: {
23
+ type: "string",
24
+ description: "The URL to fetch"
25
+ }
26
+ },
27
+ required: ["url"]
28
+ }
29
+ end
30
+
31
+ def receive(message, context:)
32
+ url = message[:url] || message["url"]
33
+
34
+ return "Error: URL is required" if url.nil? || url.empty?
35
+
36
+ begin
37
+ uri = URI.parse(url)
38
+
39
+ unless %w[http https].include?(uri.scheme)
40
+ return "Error: Only http and https URLs are supported"
41
+ end
42
+
43
+ response = Net::HTTP.get_response(uri)
44
+
45
+ case response
46
+ when Net::HTTPSuccess
47
+ content = response.body.to_s
48
+
49
+ # Truncate very large responses
50
+ if content.length > 50_000
51
+ content = content[0, 50_000] + "\n\n... [truncated, response is #{content.length} bytes]"
52
+ end
53
+
54
+ content
55
+ when Net::HTTPRedirection
56
+ "Redirected to: #{response['location']}"
57
+ else
58
+ "HTTP Error: #{response.code} #{response.message}"
59
+ end
60
+ rescue URI::InvalidURIError
61
+ "Error: Invalid URL format"
62
+ rescue SocketError => e
63
+ "Error: Could not connect - #{e.message}"
64
+ rescue Timeout::Error
65
+ "Error: Request timed out"
66
+ rescue StandardError => e
67
+ "Error fetching URL: #{e.message}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Primitives
5
+ # Primitive capability to list files in a directory.
6
+ class ListFiles < Primitive
7
+ def name
8
+ "list_files"
9
+ end
10
+
11
+ def description
12
+ "List files and directories in a given path"
13
+ end
14
+
15
+ def parameters
16
+ {
17
+ type: "object",
18
+ properties: {
19
+ path: {
20
+ type: "string",
21
+ description: "The directory path to list (defaults to current directory)"
22
+ }
23
+ },
24
+ required: []
25
+ }
26
+ end
27
+
28
+ def receive(message, context:)
29
+ path = extract_path(message)
30
+ path = "." if path.nil? || path.empty?
31
+
32
+ safe_path = File.expand_path(path)
33
+
34
+ unless File.exist?(safe_path)
35
+ return "Error: Path not found: #{path}"
36
+ end
37
+
38
+ unless File.directory?(safe_path)
39
+ return "Error: Not a directory: #{path}"
40
+ end
41
+
42
+ entries = Dir.entries(safe_path)
43
+ .reject { |e| e.start_with?(".") } # Hide hidden files
44
+ .sort
45
+ .map { |entry| format_entry(safe_path, entry) }
46
+
47
+ if entries.empty?
48
+ "Directory is empty: #{path}"
49
+ else
50
+ entries.join("\n")
51
+ end
52
+ rescue Errno::EACCES
53
+ "Error: Permission denied: #{path}"
54
+ rescue StandardError => e
55
+ "Error listing directory: #{e.message}"
56
+ end
57
+
58
+ private
59
+
60
+ def extract_path(message)
61
+ case message
62
+ when Hash
63
+ message[:path] || message["path"]
64
+ when String
65
+ message
66
+ when NilClass
67
+ "."
68
+ else
69
+ message.to_s
70
+ end
71
+ end
72
+
73
+ def format_entry(base_path, entry)
74
+ full_path = File.join(base_path, entry)
75
+
76
+ if File.directory?(full_path)
77
+ "#{entry}/"
78
+ else
79
+ size = File.size(full_path)
80
+ "#{entry} (#{human_size(size)})"
81
+ end
82
+ end
83
+
84
+ def human_size(bytes)
85
+ return "0 B" if bytes == 0
86
+
87
+ units = ["B", "KB", "MB", "GB"]
88
+ exp = (Math.log(bytes) / Math.log(1024)).to_i
89
+ exp = units.length - 1 if exp >= units.length
90
+
91
+ "%.1f %s" % [bytes.to_f / (1024 ** exp), units[exp]]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Primitives
5
+ # Primitive capability to read file contents.
6
+ class ReadFile < Primitive
7
+ def name
8
+ "read_file"
9
+ end
10
+
11
+ def description
12
+ "Read the contents of a text file"
13
+ end
14
+
15
+ def parameters
16
+ {
17
+ type: "object",
18
+ properties: {
19
+ path: {
20
+ type: "string",
21
+ description: "The path to the file to read"
22
+ }
23
+ },
24
+ required: ["path"]
25
+ }
26
+ end
27
+
28
+ def receive(message, context:)
29
+ path = extract_path(message)
30
+ safe_path = validate_path(path)
31
+
32
+ unless File.exist?(safe_path)
33
+ return "Error: File not found: #{path}"
34
+ end
35
+
36
+ unless File.file?(safe_path)
37
+ return "Error: Not a file: #{path}"
38
+ end
39
+
40
+ content = File.read(safe_path, encoding: "UTF-8")
41
+
42
+ # Truncate very large files
43
+ if content.length > 50_000
44
+ content = content[0, 50_000] + "\n\n... [truncated, file is #{content.length} bytes]"
45
+ end
46
+
47
+ content
48
+ rescue Errno::EACCES
49
+ "Error: Permission denied: #{path}"
50
+ rescue StandardError => e
51
+ "Error reading file: #{e.message}"
52
+ end
53
+
54
+ private
55
+
56
+ def extract_path(message)
57
+ case message
58
+ when Hash
59
+ message[:path] || message["path"]
60
+ when String
61
+ message
62
+ else
63
+ message.to_s
64
+ end
65
+ end
66
+
67
+ def validate_path(path)
68
+ # Expand and normalize the path
69
+ expanded = File.expand_path(path)
70
+
71
+ # Basic safety: don't allow reading outside current directory tree
72
+ # In a real implementation, you'd want more robust sandboxing
73
+ cwd = File.expand_path(".")
74
+
75
+ # Allow absolute paths but warn if outside cwd
76
+ # For now, we'll be permissive but could restrict this
77
+ expanded
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PromptObjects
4
+ module Primitives
5
+ # Primitive capability to write content to a file.
6
+ class WriteFile < Primitive
7
+ def name
8
+ "write_file"
9
+ end
10
+
11
+ def description
12
+ "Write content to a file (creates or overwrites)"
13
+ end
14
+
15
+ def parameters
16
+ {
17
+ type: "object",
18
+ properties: {
19
+ path: {
20
+ type: "string",
21
+ description: "The path to the file to write"
22
+ },
23
+ content: {
24
+ type: "string",
25
+ description: "The content to write to the file"
26
+ }
27
+ },
28
+ required: ["path", "content"]
29
+ }
30
+ end
31
+
32
+ def receive(message, context:)
33
+ path = extract_param(message, :path)
34
+ content = extract_param(message, :content)
35
+
36
+ if path.nil? || path.empty?
37
+ return "Error: path is required"
38
+ end
39
+
40
+ if content.nil?
41
+ return "Error: content is required"
42
+ end
43
+
44
+ safe_path = File.expand_path(path)
45
+
46
+ # Create parent directories if they don't exist
47
+ dir = File.dirname(safe_path)
48
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
49
+
50
+ File.write(safe_path, content, encoding: "UTF-8")
51
+
52
+ "Successfully wrote #{content.length} bytes to #{path}"
53
+ rescue Errno::EACCES
54
+ "Error: Permission denied: #{path}"
55
+ rescue StandardError => e
56
+ "Error writing file: #{e.message}"
57
+ end
58
+
59
+ private
60
+
61
+ def extract_param(message, key)
62
+ case message
63
+ when Hash
64
+ message[key] || message[key.to_s]
65
+ else
66
+ nil
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ require "fileutils"