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.
- checksums.yaml +7 -0
- data/CLAUDE.md +108 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +231 -0
- data/IMPLEMENTATION_PLAN.md +1073 -0
- data/LICENSE +21 -0
- data/README.md +73 -0
- data/Rakefile +27 -0
- data/design-doc-v2.md +1232 -0
- data/exe/prompt_objects +572 -0
- data/exe/prompt_objects_mcp +34 -0
- data/frontend/.gitignore +3 -0
- data/frontend/index.html +13 -0
- data/frontend/package-lock.json +4417 -0
- data/frontend/package.json +32 -0
- data/frontend/postcss.config.js +6 -0
- data/frontend/src/App.tsx +95 -0
- data/frontend/src/components/CapabilitiesPanel.tsx +44 -0
- data/frontend/src/components/ChatPanel.tsx +251 -0
- data/frontend/src/components/Dashboard.tsx +83 -0
- data/frontend/src/components/Header.tsx +141 -0
- data/frontend/src/components/MarkdownMessage.tsx +153 -0
- data/frontend/src/components/MessageBus.tsx +55 -0
- data/frontend/src/components/ModelSelector.tsx +112 -0
- data/frontend/src/components/NotificationPanel.tsx +134 -0
- data/frontend/src/components/POCard.tsx +56 -0
- data/frontend/src/components/PODetail.tsx +117 -0
- data/frontend/src/components/PromptPanel.tsx +51 -0
- data/frontend/src/components/SessionsPanel.tsx +174 -0
- data/frontend/src/components/ThreadsSidebar.tsx +119 -0
- data/frontend/src/components/index.ts +11 -0
- data/frontend/src/hooks/useWebSocket.ts +363 -0
- data/frontend/src/index.css +37 -0
- data/frontend/src/main.tsx +10 -0
- data/frontend/src/store/index.ts +246 -0
- data/frontend/src/types/index.ts +146 -0
- data/frontend/tailwind.config.js +25 -0
- data/frontend/tsconfig.json +30 -0
- data/frontend/vite.config.ts +29 -0
- data/lib/prompt_objects/capability.rb +46 -0
- data/lib/prompt_objects/cli.rb +431 -0
- data/lib/prompt_objects/connectors/base.rb +73 -0
- data/lib/prompt_objects/connectors/mcp.rb +524 -0
- data/lib/prompt_objects/environment/exporter.rb +83 -0
- data/lib/prompt_objects/environment/git.rb +118 -0
- data/lib/prompt_objects/environment/importer.rb +159 -0
- data/lib/prompt_objects/environment/manager.rb +401 -0
- data/lib/prompt_objects/environment/manifest.rb +218 -0
- data/lib/prompt_objects/environment.rb +283 -0
- data/lib/prompt_objects/human_queue.rb +144 -0
- data/lib/prompt_objects/llm/anthropic_adapter.rb +137 -0
- data/lib/prompt_objects/llm/factory.rb +84 -0
- data/lib/prompt_objects/llm/gemini_adapter.rb +209 -0
- data/lib/prompt_objects/llm/openai_adapter.rb +104 -0
- data/lib/prompt_objects/llm/response.rb +61 -0
- data/lib/prompt_objects/loader.rb +32 -0
- data/lib/prompt_objects/mcp/server.rb +167 -0
- data/lib/prompt_objects/mcp/tools/get_conversation.rb +60 -0
- data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +54 -0
- data/lib/prompt_objects/mcp/tools/inspect_po.rb +73 -0
- data/lib/prompt_objects/mcp/tools/list_prompt_objects.rb +37 -0
- data/lib/prompt_objects/mcp/tools/respond_to_request.rb +68 -0
- data/lib/prompt_objects/mcp/tools/send_message.rb +71 -0
- data/lib/prompt_objects/message_bus.rb +97 -0
- data/lib/prompt_objects/primitive.rb +13 -0
- data/lib/prompt_objects/primitives/http_get.rb +72 -0
- data/lib/prompt_objects/primitives/list_files.rb +95 -0
- data/lib/prompt_objects/primitives/read_file.rb +81 -0
- data/lib/prompt_objects/primitives/write_file.rb +73 -0
- data/lib/prompt_objects/prompt_object.rb +415 -0
- data/lib/prompt_objects/registry.rb +88 -0
- data/lib/prompt_objects/server/api/routes.rb +297 -0
- data/lib/prompt_objects/server/app.rb +174 -0
- data/lib/prompt_objects/server/file_watcher.rb +113 -0
- data/lib/prompt_objects/server/public/assets/index-2acS2FYZ.js +77 -0
- data/lib/prompt_objects/server/public/assets/index-DXU5uRXQ.css +1 -0
- data/lib/prompt_objects/server/public/index.html +14 -0
- data/lib/prompt_objects/server/websocket_handler.rb +619 -0
- data/lib/prompt_objects/server.rb +166 -0
- data/lib/prompt_objects/session/store.rb +826 -0
- data/lib/prompt_objects/universal/add_capability.rb +74 -0
- data/lib/prompt_objects/universal/add_primitive.rb +113 -0
- data/lib/prompt_objects/universal/ask_human.rb +109 -0
- data/lib/prompt_objects/universal/create_capability.rb +219 -0
- data/lib/prompt_objects/universal/create_primitive.rb +170 -0
- data/lib/prompt_objects/universal/list_capabilities.rb +55 -0
- data/lib/prompt_objects/universal/list_primitives.rb +145 -0
- data/lib/prompt_objects/universal/modify_primitive.rb +180 -0
- data/lib/prompt_objects/universal/request_primitive.rb +287 -0
- data/lib/prompt_objects/universal/think.rb +41 -0
- data/lib/prompt_objects/universal/verify_primitive.rb +173 -0
- data/lib/prompt_objects.rb +62 -0
- data/objects/coordinator.md +48 -0
- data/objects/greeter.md +30 -0
- data/objects/reader.md +33 -0
- data/prompt_objects.gemspec +50 -0
- data/templates/basic/.gitignore +2 -0
- data/templates/basic/manifest.yml +7 -0
- data/templates/basic/objects/basic.md +32 -0
- data/templates/developer/.gitignore +5 -0
- data/templates/developer/manifest.yml +17 -0
- data/templates/developer/objects/code_reviewer.md +33 -0
- data/templates/developer/objects/coordinator.md +39 -0
- data/templates/developer/objects/debugger.md +35 -0
- data/templates/empty/.gitignore +5 -0
- data/templates/empty/manifest.yml +14 -0
- data/templates/empty/objects/.gitkeep +0 -0
- data/templates/empty/objects/assistant.md +41 -0
- data/templates/minimal/.gitignore +5 -0
- data/templates/minimal/manifest.yml +7 -0
- data/templates/minimal/objects/assistant.md +41 -0
- data/templates/writer/.gitignore +5 -0
- data/templates/writer/manifest.yml +17 -0
- data/templates/writer/objects/coordinator.md +33 -0
- data/templates/writer/objects/editor.md +33 -0
- data/templates/writer/objects/researcher.md +34 -0
- metadata +343 -0
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
# PromptObjects Implementation Plan
|
|
2
|
+
|
|
3
|
+
Detailed phased implementation plan for the PromptObjects MVP.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Phase 1: Core Loop
|
|
8
|
+
|
|
9
|
+
**Goal**: Get a single Prompt-Object (greeter) responding to human input via LLM.
|
|
10
|
+
|
|
11
|
+
### 1.1 Project Setup
|
|
12
|
+
|
|
13
|
+
- [ ] Initialize Ruby project structure
|
|
14
|
+
```
|
|
15
|
+
prompt_objects/
|
|
16
|
+
├── Gemfile
|
|
17
|
+
├── lib/
|
|
18
|
+
│ ├── prompt_objects.rb
|
|
19
|
+
│ └── prompt_objects/
|
|
20
|
+
└── exe/
|
|
21
|
+
└── prompt_objects
|
|
22
|
+
```
|
|
23
|
+
- [ ] Gemfile dependencies:
|
|
24
|
+
- `ruby-openai` (or `anthropic` SDK)
|
|
25
|
+
- `front_matter_parser` (YAML frontmatter parsing)
|
|
26
|
+
- Standard library: `optparse`, `readline`
|
|
27
|
+
|
|
28
|
+
### 1.2 Capability Base Class
|
|
29
|
+
|
|
30
|
+
**File**: `lib/prompt_objects/capability.rb`
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
module PromptObjects
|
|
34
|
+
class Capability
|
|
35
|
+
attr_reader :name, :description
|
|
36
|
+
|
|
37
|
+
def receive(message, context:)
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def descriptor
|
|
42
|
+
{
|
|
43
|
+
name: name,
|
|
44
|
+
description: description,
|
|
45
|
+
parameters: parameters
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parameters
|
|
50
|
+
{ type: "object", properties: {}, required: [] }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 1.3 Loader
|
|
57
|
+
|
|
58
|
+
**File**: `lib/prompt_objects/loader.rb`
|
|
59
|
+
|
|
60
|
+
- [ ] Parse markdown file with YAML frontmatter
|
|
61
|
+
- [ ] Extract config (name, description, capabilities list)
|
|
62
|
+
- [ ] Extract body (everything after frontmatter)
|
|
63
|
+
- [ ] Return structured data for PromptObject initialization
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
module PromptObjects
|
|
67
|
+
class Loader
|
|
68
|
+
def self.load(path)
|
|
69
|
+
content = File.read(path)
|
|
70
|
+
parsed = FrontMatterParser::Parser.new(:md).call(content)
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
config: parsed.front_matter,
|
|
74
|
+
body: parsed.content,
|
|
75
|
+
path: path
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 1.4 LLM Adapter (OpenAI)
|
|
83
|
+
|
|
84
|
+
**File**: `lib/prompt_objects/llm/openai_adapter.rb`
|
|
85
|
+
|
|
86
|
+
- [ ] Initialize with API key (from ENV)
|
|
87
|
+
- [ ] `chat(system:, messages:, tools:)` method
|
|
88
|
+
- [ ] Handle tool_calls in response
|
|
89
|
+
- [ ] Return structured response object with `content` and `tool_calls`
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
module PromptObjects
|
|
93
|
+
module LLM
|
|
94
|
+
class OpenAIAdapter
|
|
95
|
+
def initialize(api_key: ENV["OPENAI_API_KEY"], model: "gpt-4")
|
|
96
|
+
@client = OpenAI::Client.new(access_token: api_key)
|
|
97
|
+
@model = model
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def chat(system:, messages:, tools: [])
|
|
101
|
+
response = @client.chat(
|
|
102
|
+
parameters: {
|
|
103
|
+
model: @model,
|
|
104
|
+
messages: format_messages(system, messages),
|
|
105
|
+
tools: format_tools(tools)
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
Response.new(response)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class Response
|
|
114
|
+
attr_reader :content, :tool_calls
|
|
115
|
+
# Parse OpenAI response format
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 1.5 PromptObject Class
|
|
122
|
+
|
|
123
|
+
**File**: `lib/prompt_objects/prompt_object.rb`
|
|
124
|
+
|
|
125
|
+
- [ ] Initialize with config, body, environment reference, LLM adapter
|
|
126
|
+
- [ ] Implement `receive(message, context:)`:
|
|
127
|
+
1. Add message to history
|
|
128
|
+
2. Call LLM with system prompt (body) + history
|
|
129
|
+
3. If tool_calls: execute and loop
|
|
130
|
+
4. If no tool_calls: return content
|
|
131
|
+
- [ ] Track conversation history
|
|
132
|
+
|
|
133
|
+
### 1.6 Simple REPL
|
|
134
|
+
|
|
135
|
+
**File**: `exe/prompt_objects`
|
|
136
|
+
|
|
137
|
+
- [ ] Parse command line args (prompt object name)
|
|
138
|
+
- [ ] Load the specified .md file from `objects/` directory
|
|
139
|
+
- [ ] Create PromptObject instance
|
|
140
|
+
- [ ] Loop: read input → call receive → print response
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
#!/usr/bin/env ruby
|
|
144
|
+
require "prompt_objects"
|
|
145
|
+
|
|
146
|
+
name = ARGV[0] || "greeter"
|
|
147
|
+
path = File.join("objects", "#{name}.md")
|
|
148
|
+
|
|
149
|
+
env = PromptObjects::Environment.new
|
|
150
|
+
po = env.load_prompt_object(path)
|
|
151
|
+
|
|
152
|
+
loop do
|
|
153
|
+
print "You: "
|
|
154
|
+
input = gets&.chomp
|
|
155
|
+
break if input.nil? || input == "exit"
|
|
156
|
+
|
|
157
|
+
response = po.receive(input, context: env.context)
|
|
158
|
+
puts "\n#{po.name}: #{response}\n\n"
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 1.7 Test Prompt Object
|
|
163
|
+
|
|
164
|
+
**File**: `objects/greeter.md`
|
|
165
|
+
|
|
166
|
+
Create the greeter from the design doc to test with.
|
|
167
|
+
|
|
168
|
+
### Phase 1 Deliverable
|
|
169
|
+
|
|
170
|
+
- Run `./exe/prompt_objects greeter`
|
|
171
|
+
- Type "hello"
|
|
172
|
+
- Get a response from the greeter personality
|
|
173
|
+
- **Demo 1 achievable**
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Conceptual Model: The PromptObject Hierarchy
|
|
178
|
+
|
|
179
|
+
PromptObjects follow an object-oriented capability model inspired by Smalltalk/Ruby:
|
|
180
|
+
|
|
181
|
+
### Universal Capabilities = "BasicObject/Kernel"
|
|
182
|
+
|
|
183
|
+
The absolute minimum every PO inherits to function in the environment:
|
|
184
|
+
|
|
185
|
+
| Capability | Purpose |
|
|
186
|
+
|------------|---------|
|
|
187
|
+
| `ask_human` | Communicate with the human operator |
|
|
188
|
+
| `think` | Internal reasoning (chain-of-thought) |
|
|
189
|
+
| `create_capability` | Self-modification - create new POs or primitives |
|
|
190
|
+
| `add_capability` | Extend self with new capabilities at runtime |
|
|
191
|
+
| `list_capabilities` | Introspection - discover available tools |
|
|
192
|
+
|
|
193
|
+
These are **always available** - POs don't need to declare them.
|
|
194
|
+
|
|
195
|
+
### Stdlib Primitives = "Standard Library"
|
|
196
|
+
|
|
197
|
+
Opt-in deterministic Ruby capabilities, organized by category:
|
|
198
|
+
|
|
199
|
+
| Category | Primitives |
|
|
200
|
+
|----------|------------|
|
|
201
|
+
| **File System** | `read_file`, `write_file`, `list_files`, `delete_file`, `move_file`, `file_exists` |
|
|
202
|
+
| **Network** | `http_get`, `http_post`, `http_request`, `download_file` |
|
|
203
|
+
| **Process** | `run_command`, `spawn_process` |
|
|
204
|
+
| **Data** | `json_parse`, `json_stringify`, `yaml_parse`, `yaml_stringify`, `csv_parse` |
|
|
205
|
+
| **Environment** | `get_env`, `current_time`, `sleep` |
|
|
206
|
+
| **Text** | `regex_match`, `regex_replace`, `string_split` |
|
|
207
|
+
| **Crypto** | `hash_string`, `random_uuid`, `random_string` |
|
|
208
|
+
|
|
209
|
+
POs declare which stdlib primitives they need in their frontmatter.
|
|
210
|
+
|
|
211
|
+
### Custom Primitives = "User-Defined Functions"
|
|
212
|
+
|
|
213
|
+
Runtime-generated primitives created via `create_capability`. These are project-specific tools.
|
|
214
|
+
|
|
215
|
+
### Delegate POs = "Collaborators"
|
|
216
|
+
|
|
217
|
+
Other prompt objects this PO can call for help. Listed in `capabilities` frontmatter.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Phase 2: Primitives & Binding
|
|
222
|
+
|
|
223
|
+
**Goal**: Add primitive capabilities that PromptObjects can call. Reader uses `read_file` and `list_files`.
|
|
224
|
+
|
|
225
|
+
### 2.1 Primitive Base Class
|
|
226
|
+
|
|
227
|
+
**File**: `lib/prompt_objects/primitive.rb`
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
module PromptObjects
|
|
231
|
+
class Primitive < Capability
|
|
232
|
+
# Primitives are Capabilities with deterministic Ruby implementations
|
|
233
|
+
# They define parameters more strictly than POs
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 2.2 Built-in Primitives
|
|
239
|
+
|
|
240
|
+
**Directory**: `lib/prompt_objects/primitives/`
|
|
241
|
+
|
|
242
|
+
#### read_file.rb
|
|
243
|
+
- [ ] Parameters: `{ path: string }`
|
|
244
|
+
- [ ] Validate path (prevent directory traversal)
|
|
245
|
+
- [ ] Read and return file contents
|
|
246
|
+
- [ ] Handle errors gracefully
|
|
247
|
+
|
|
248
|
+
#### list_files.rb
|
|
249
|
+
- [ ] Parameters: `{ path: string }` (defaults to ".")
|
|
250
|
+
- [ ] Return array of filenames/directories
|
|
251
|
+
- [ ] Optional: include file types/sizes
|
|
252
|
+
|
|
253
|
+
#### write_file.rb
|
|
254
|
+
- [ ] Parameters: `{ path: string, content: string }`
|
|
255
|
+
- [ ] Validate path
|
|
256
|
+
- [ ] Write content to file
|
|
257
|
+
- [ ] Return confirmation
|
|
258
|
+
|
|
259
|
+
### 2.3 Registry
|
|
260
|
+
|
|
261
|
+
**File**: `lib/prompt_objects/registry.rb`
|
|
262
|
+
|
|
263
|
+
- [ ] Store capabilities by name
|
|
264
|
+
- [ ] `register(capability)` - add to registry
|
|
265
|
+
- [ ] `get(name)` - retrieve by name
|
|
266
|
+
- [ ] `all` - list all capabilities
|
|
267
|
+
- [ ] `prompt_objects` - filter to just POs
|
|
268
|
+
- [ ] `primitives` - filter to just primitives
|
|
269
|
+
- [ ] Generate tool descriptors for LLM
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
module PromptObjects
|
|
273
|
+
class Registry
|
|
274
|
+
def initialize
|
|
275
|
+
@capabilities = {}
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def register(capability)
|
|
279
|
+
@capabilities[capability.name] = capability
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def get(name)
|
|
283
|
+
@capabilities[name] or raise "Unknown capability: #{name}"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def descriptors_for(names)
|
|
287
|
+
names.map { |n| get(n).descriptor }
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### 2.4 Environment Updates
|
|
294
|
+
|
|
295
|
+
**File**: `lib/prompt_objects/environment.rb`
|
|
296
|
+
|
|
297
|
+
- [ ] Hold registry instance
|
|
298
|
+
- [ ] Auto-register built-in primitives on init
|
|
299
|
+
- [ ] Load prompt objects from `objects/` directory
|
|
300
|
+
- [ ] Provide context object for capability execution
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
module PromptObjects
|
|
304
|
+
class Environment
|
|
305
|
+
attr_reader :registry, :llm
|
|
306
|
+
|
|
307
|
+
def initialize
|
|
308
|
+
@registry = Registry.new
|
|
309
|
+
@llm = LLM::OpenAIAdapter.new
|
|
310
|
+
register_primitives
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def register_primitives
|
|
314
|
+
registry.register(Primitives::ReadFile.new)
|
|
315
|
+
registry.register(Primitives::ListFiles.new)
|
|
316
|
+
registry.register(Primitives::WriteFile.new)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def load_prompt_object(path)
|
|
320
|
+
data = Loader.load(path)
|
|
321
|
+
po = PromptObject.new(
|
|
322
|
+
config: data[:config],
|
|
323
|
+
body: data[:body],
|
|
324
|
+
env: self,
|
|
325
|
+
llm: @llm
|
|
326
|
+
)
|
|
327
|
+
registry.register(po)
|
|
328
|
+
po
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 2.5 PromptObject Updates
|
|
335
|
+
|
|
336
|
+
- [ ] Look up declared capabilities from registry
|
|
337
|
+
- [ ] Pass capability descriptors to LLM as tools
|
|
338
|
+
- [ ] Execute capability calls through registry
|
|
339
|
+
- [ ] Handle capability results in conversation loop
|
|
340
|
+
|
|
341
|
+
### 2.6 Test Prompt Object
|
|
342
|
+
|
|
343
|
+
**File**: `objects/reader.md`
|
|
344
|
+
|
|
345
|
+
Create the reader from the design doc with `read_file` and `list_files` capabilities.
|
|
346
|
+
|
|
347
|
+
### Phase 2 Deliverable
|
|
348
|
+
|
|
349
|
+
- Run `./exe/prompt_objects reader`
|
|
350
|
+
- Ask "what's in here?"
|
|
351
|
+
- Reader calls `list_files`, interprets results, responds
|
|
352
|
+
- Ask "tell me about the README"
|
|
353
|
+
- Reader calls `read_file`, summarizes content
|
|
354
|
+
- **Demo 2 achievable** - semantic binding visible in output
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Phase 3: Multi-Capability
|
|
359
|
+
|
|
360
|
+
**Goal**: Prompt-Objects can call other Prompt-Objects. Coordinator delegates to reader.
|
|
361
|
+
|
|
362
|
+
### 3.1 Message Bus
|
|
363
|
+
|
|
364
|
+
**File**: `lib/prompt_objects/message_bus.rb`
|
|
365
|
+
|
|
366
|
+
- [ ] Log all messages: `{ timestamp, from, to, message }`
|
|
367
|
+
- [ ] Subscribe mechanism for UI updates
|
|
368
|
+
- [ ] `recent(n)` to get last n messages
|
|
369
|
+
- [ ] Truncate long messages for display
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
module PromptObjects
|
|
373
|
+
class MessageBus
|
|
374
|
+
attr_reader :log
|
|
375
|
+
|
|
376
|
+
def initialize
|
|
377
|
+
@log = []
|
|
378
|
+
@subscribers = []
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def publish(from:, to:, message:)
|
|
382
|
+
entry = {
|
|
383
|
+
timestamp: Time.now,
|
|
384
|
+
from: from,
|
|
385
|
+
to: to,
|
|
386
|
+
message: message
|
|
387
|
+
}
|
|
388
|
+
@log << entry
|
|
389
|
+
@subscribers.each { |s| s.call(entry) }
|
|
390
|
+
entry
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def subscribe(&block)
|
|
394
|
+
@subscribers << block
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def recent(n = 20)
|
|
398
|
+
@log.last(n)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### 3.2 Context Object
|
|
405
|
+
|
|
406
|
+
**File**: `lib/prompt_objects/context.rb`
|
|
407
|
+
|
|
408
|
+
- [ ] Hold reference to environment
|
|
409
|
+
- [ ] Hold reference to message bus
|
|
410
|
+
- [ ] Track current execution chain (for loop detection)
|
|
411
|
+
- [ ] Provide callbacks for streaming/deltas
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
module PromptObjects
|
|
415
|
+
class Context
|
|
416
|
+
attr_reader :env, :bus
|
|
417
|
+
attr_accessor :on_delta
|
|
418
|
+
|
|
419
|
+
def initialize(env:, bus:)
|
|
420
|
+
@env = env
|
|
421
|
+
@bus = bus
|
|
422
|
+
@call_stack = []
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def push(capability_name)
|
|
426
|
+
if @call_stack.include?(capability_name)
|
|
427
|
+
raise "Capability loop detected: #{@call_stack.join(' → ')} → #{capability_name}"
|
|
428
|
+
end
|
|
429
|
+
@call_stack.push(capability_name)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def pop
|
|
433
|
+
@call_stack.pop
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### 3.3 PO → PO Communication
|
|
440
|
+
|
|
441
|
+
Update PromptObject to:
|
|
442
|
+
- [ ] When calling a capability, check if it's a PO or primitive
|
|
443
|
+
- [ ] Log message to bus before calling
|
|
444
|
+
- [ ] Log response to bus after receiving
|
|
445
|
+
- [ ] POs receive natural language messages from other POs
|
|
446
|
+
|
|
447
|
+
### 3.4 REPL Updates
|
|
448
|
+
|
|
449
|
+
- [ ] Print message bus entries as they happen
|
|
450
|
+
- [ ] Show the cascade of messages
|
|
451
|
+
- [ ] Format: `HH:MM:SS from → to: "message..."`
|
|
452
|
+
|
|
453
|
+
### 3.5 Test Prompt Object
|
|
454
|
+
|
|
455
|
+
**File**: `objects/coordinator.md`
|
|
456
|
+
|
|
457
|
+
Create coordinator with capabilities: `greeter`, `reader`, `list_files`
|
|
458
|
+
|
|
459
|
+
### Phase 3 Deliverable
|
|
460
|
+
|
|
461
|
+
- Run `./exe/prompt_objects coordinator`
|
|
462
|
+
- Ask "help me understand this codebase"
|
|
463
|
+
- See coordinator delegate to reader
|
|
464
|
+
- See reader use primitives
|
|
465
|
+
- See message cascade in output
|
|
466
|
+
- **Demo 3 achievable**
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Phase 4: Self-Modification & Human Interaction Queue
|
|
471
|
+
|
|
472
|
+
**Goal**: System can create new Prompt-Objects at runtime. Human interactions are non-blocking with a notification queue.
|
|
473
|
+
|
|
474
|
+
### 4.1 Universal Capabilities
|
|
475
|
+
|
|
476
|
+
**Directory**: `lib/prompt_objects/universal/`
|
|
477
|
+
|
|
478
|
+
These are automatically available to ALL prompt objects.
|
|
479
|
+
|
|
480
|
+
#### ask_human.rb
|
|
481
|
+
- [ ] Parameters: `{ question: string, options: array (optional) }`
|
|
482
|
+
- [ ] **Non-blocking**: Suspends PO execution, queues notification
|
|
483
|
+
- [ ] Returns when human eventually responds
|
|
484
|
+
- [ ] Use **Huh** gem for rendering the prompt when human engages
|
|
485
|
+
|
|
486
|
+
```ruby
|
|
487
|
+
module PromptObjects
|
|
488
|
+
module Universal
|
|
489
|
+
class AskHuman < Capability
|
|
490
|
+
def name = "ask_human"
|
|
491
|
+
def description = "Pause and ask the human a question"
|
|
492
|
+
|
|
493
|
+
def parameters
|
|
494
|
+
{
|
|
495
|
+
type: "object",
|
|
496
|
+
properties: {
|
|
497
|
+
question: { type: "string", description: "Question to ask" },
|
|
498
|
+
options: {
|
|
499
|
+
type: "array",
|
|
500
|
+
items: { type: "string" },
|
|
501
|
+
description: "Optional choices to present"
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
required: ["question"]
|
|
505
|
+
}
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def receive(message, context:)
|
|
509
|
+
question = message[:question] || message["question"]
|
|
510
|
+
options = message[:options] || message["options"]
|
|
511
|
+
|
|
512
|
+
# Create a pending request and suspend until human responds
|
|
513
|
+
request = HumanRequest.new(
|
|
514
|
+
capability: context.current_capability,
|
|
515
|
+
type: :ask_human,
|
|
516
|
+
question: question,
|
|
517
|
+
options: options
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# Queue the request - this suspends the PO's fiber
|
|
521
|
+
context.env.human_queue.enqueue(request)
|
|
522
|
+
|
|
523
|
+
# When we resume, the response is in the request
|
|
524
|
+
request.response
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### think.rb
|
|
532
|
+
- [ ] Parameters: `{ thought: string }`
|
|
533
|
+
- [ ] Log to bus but don't show to human (or show dimmed)
|
|
534
|
+
- [ ] Return acknowledgment
|
|
535
|
+
- [ ] Useful for chain-of-thought reasoning
|
|
536
|
+
|
|
537
|
+
#### create_capability.rb
|
|
538
|
+
- [ ] Parameters: `{ type, name, description, capabilities, body }`
|
|
539
|
+
- [ ] For type "prompt_object":
|
|
540
|
+
- Build frontmatter YAML
|
|
541
|
+
- Combine with body markdown
|
|
542
|
+
- Write to `objects/` directory
|
|
543
|
+
- Load into registry
|
|
544
|
+
- [ ] For type "primitive": return error (not supported in MVP)
|
|
545
|
+
|
|
546
|
+
### 4.2 Human Interaction Queue
|
|
547
|
+
|
|
548
|
+
**File**: `lib/prompt_objects/human_queue.rb`
|
|
549
|
+
|
|
550
|
+
Non-blocking system for POs to request human attention. Multiple POs can be waiting simultaneously.
|
|
551
|
+
|
|
552
|
+
```ruby
|
|
553
|
+
module PromptObjects
|
|
554
|
+
class HumanRequest
|
|
555
|
+
attr_reader :id, :capability, :type, :question, :options, :created_at
|
|
556
|
+
attr_accessor :response
|
|
557
|
+
|
|
558
|
+
def initialize(capability:, type:, question:, options: nil)
|
|
559
|
+
@id = SecureRandom.uuid
|
|
560
|
+
@capability = capability
|
|
561
|
+
@type = type
|
|
562
|
+
@question = question
|
|
563
|
+
@options = options
|
|
564
|
+
@created_at = Time.now
|
|
565
|
+
@response = nil
|
|
566
|
+
@fiber = Fiber.current
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def respond!(value)
|
|
570
|
+
@response = value
|
|
571
|
+
@fiber.resume(value)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def pending?
|
|
575
|
+
@response.nil?
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
class HumanQueue
|
|
580
|
+
attr_reader :pending
|
|
581
|
+
|
|
582
|
+
def initialize
|
|
583
|
+
@pending = []
|
|
584
|
+
@subscribers = []
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def enqueue(request)
|
|
588
|
+
@pending << request
|
|
589
|
+
notify_subscribers(:added, request)
|
|
590
|
+
# Suspend the calling fiber until human responds
|
|
591
|
+
Fiber.yield
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def respond(request_id, value)
|
|
595
|
+
request = @pending.find { |r| r.id == request_id }
|
|
596
|
+
return unless request
|
|
597
|
+
|
|
598
|
+
@pending.delete(request)
|
|
599
|
+
notify_subscribers(:resolved, request)
|
|
600
|
+
request.respond!(value)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def pending_for(capability_name)
|
|
604
|
+
@pending.select { |r| r.capability == capability_name }
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def subscribe(&block)
|
|
608
|
+
@subscribers << block
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
private
|
|
612
|
+
|
|
613
|
+
def notify_subscribers(event, request)
|
|
614
|
+
@subscribers.each { |s| s.call(event, request) }
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### 4.3 PO States & Concurrent Execution
|
|
621
|
+
|
|
622
|
+
**File**: `lib/prompt_objects/executor.rb`
|
|
623
|
+
|
|
624
|
+
POs run in Fibers for cooperative concurrency. States:
|
|
625
|
+
- `idle` - Not doing anything
|
|
626
|
+
- `working` - Processing (LLM call in progress)
|
|
627
|
+
- `waiting_for_human` - Suspended, has pending HumanRequest
|
|
628
|
+
- `active` - Currently being interacted with by human
|
|
629
|
+
|
|
630
|
+
```ruby
|
|
631
|
+
module PromptObjects
|
|
632
|
+
class Executor
|
|
633
|
+
def initialize(env:)
|
|
634
|
+
@env = env
|
|
635
|
+
@fibers = {} # capability_name => Fiber
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def run(capability, message, context:)
|
|
639
|
+
fiber = Fiber.new do
|
|
640
|
+
capability.receive(message, context: context)
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
@fibers[capability.name] = fiber
|
|
644
|
+
capability.state = :working
|
|
645
|
+
|
|
646
|
+
result = fiber.resume
|
|
647
|
+
|
|
648
|
+
if fiber.alive?
|
|
649
|
+
# Fiber yielded (waiting for human)
|
|
650
|
+
capability.state = :waiting_for_human
|
|
651
|
+
nil # Result pending
|
|
652
|
+
else
|
|
653
|
+
# Fiber completed
|
|
654
|
+
capability.state = :idle
|
|
655
|
+
@fibers.delete(capability.name)
|
|
656
|
+
result
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def resume(capability_name)
|
|
661
|
+
fiber = @fibers[capability_name]
|
|
662
|
+
return unless fiber&.alive?
|
|
663
|
+
|
|
664
|
+
capability = @env.registry.get(capability_name)
|
|
665
|
+
capability.state = :working
|
|
666
|
+
|
|
667
|
+
result = fiber.resume
|
|
668
|
+
|
|
669
|
+
unless fiber.alive?
|
|
670
|
+
capability.state = :idle
|
|
671
|
+
@fibers.delete(capability_name)
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
result
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### 4.4 Extensible Request Types
|
|
681
|
+
|
|
682
|
+
The `HumanRequest` system is designed to support future interaction types beyond `ask_human`:
|
|
683
|
+
|
|
684
|
+
```ruby
|
|
685
|
+
module PromptObjects
|
|
686
|
+
class HumanRequest
|
|
687
|
+
TYPES = {
|
|
688
|
+
ask_human: {
|
|
689
|
+
icon: "❓",
|
|
690
|
+
renderer: :render_question
|
|
691
|
+
},
|
|
692
|
+
confirm_action: {
|
|
693
|
+
icon: "⚠️",
|
|
694
|
+
renderer: :render_confirmation
|
|
695
|
+
},
|
|
696
|
+
review_output: {
|
|
697
|
+
icon: "👁",
|
|
698
|
+
renderer: :render_review
|
|
699
|
+
},
|
|
700
|
+
provide_input: {
|
|
701
|
+
icon: "✏️",
|
|
702
|
+
renderer: :render_input_form
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
Future universal capabilities can use the same queue:
|
|
710
|
+
- `confirm_action` - "About to delete 5 files. Proceed?"
|
|
711
|
+
- `review_output` - "Here's the code I generated. Approve?"
|
|
712
|
+
- `provide_input` - "I need the API key for service X"
|
|
713
|
+
|
|
714
|
+
### 4.5 Environment Updates
|
|
715
|
+
|
|
716
|
+
- [ ] Auto-register universal capabilities
|
|
717
|
+
- [ ] Add `human_queue` instance
|
|
718
|
+
- [ ] Add `executor` for fiber management
|
|
719
|
+
- [ ] Method to add universal caps to any PO's available tools
|
|
720
|
+
- [ ] `prompt_objects_dir` configuration
|
|
721
|
+
|
|
722
|
+
### 4.6 PromptObject Updates
|
|
723
|
+
|
|
724
|
+
- [ ] Merge universal capabilities with declared capabilities
|
|
725
|
+
- [ ] Universal caps don't need to be in frontmatter
|
|
726
|
+
- [ ] Add `state` attribute for tracking execution state
|
|
727
|
+
|
|
728
|
+
### 4.7 Update Coordinator
|
|
729
|
+
|
|
730
|
+
Add `create_capability` to coordinator's capabilities list.
|
|
731
|
+
|
|
732
|
+
### 4.8 Known Issues to Address in Phase 5
|
|
733
|
+
|
|
734
|
+
During Phase 4 testing, several issues were identified with capability creation:
|
|
735
|
+
- [ ] LLM sometimes creates POs when primitives would be more appropriate
|
|
736
|
+
- [ ] Dynamically created capabilities may not be immediately available to existing POs
|
|
737
|
+
- [ ] Need better visibility into what capabilities each PO has access to
|
|
738
|
+
- [ ] Modifying existing PO capabilities at runtime needs cleaner UX
|
|
739
|
+
|
|
740
|
+
These will be addressed in Phase 5 with:
|
|
741
|
+
- **PO Inspector** showing exactly what tools each PO has
|
|
742
|
+
- Better prompting/guidance for the LLM on when to create POs vs primitives
|
|
743
|
+
- Visual feedback when capabilities are added/modified
|
|
744
|
+
|
|
745
|
+
### Phase 4 Deliverable
|
|
746
|
+
|
|
747
|
+
- Run `./exe/prompt_objects coordinator`
|
|
748
|
+
- Ask for help with something requiring a new specialist
|
|
749
|
+
- Coordinator uses `ask_human` to get permission
|
|
750
|
+
- **Request appears as notification badge on coordinator** (not blocking UI)
|
|
751
|
+
- Human can navigate to notification, respond via Huh prompt
|
|
752
|
+
- Coordinator resumes and creates new PO via `create_capability`
|
|
753
|
+
- New PO appears and can be interacted with
|
|
754
|
+
- **Demo 4 achievable** with non-blocking human interaction
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
## Phase 5: Polish & UI
|
|
759
|
+
|
|
760
|
+
**Goal**: Beautiful Charm-based terminal UI with capability bar, message log, conversation.
|
|
761
|
+
|
|
762
|
+
### 5.1 Charm Ruby Gems (Marco Roth's Ports)
|
|
763
|
+
|
|
764
|
+
Use Marco Roth's Ruby ports of the Charm libraries:
|
|
765
|
+
- **bubbletea** - Elm-inspired TUI framework (Model-View-Update)
|
|
766
|
+
- **lipgloss** - CSS-like terminal styling
|
|
767
|
+
- **glamour** - Markdown rendering in terminal
|
|
768
|
+
- **bubbles** - Pre-built components (spinners, text inputs, progress bars)
|
|
769
|
+
- **huh** - Interactive forms/prompts (perfect for `ask_human`!)
|
|
770
|
+
|
|
771
|
+
GitHub: https://github.com/marcoroth/bubbletea-ruby (and related repos)
|
|
772
|
+
Reference: https://marcoroth.dev/posts/glamorous-christmas
|
|
773
|
+
|
|
774
|
+
### 5.2 UI Components (Bubble Tea approach)
|
|
775
|
+
|
|
776
|
+
**Directory**: `lib/prompt_objects/ui/`
|
|
777
|
+
|
|
778
|
+
#### app.rb
|
|
779
|
+
- [ ] Main application loop
|
|
780
|
+
- [ ] Handle keyboard input
|
|
781
|
+
- [ ] Manage screen layout
|
|
782
|
+
- [ ] Coordinate component updates
|
|
783
|
+
|
|
784
|
+
#### capability_bar.rb
|
|
785
|
+
- [ ] Display all registered capabilities
|
|
786
|
+
- [ ] Show state: ○ idle, ◐ working, ● active, ⚠ waiting_for_human
|
|
787
|
+
- [ ] Distinguish POs from primitives (▪)
|
|
788
|
+
- [ ] **Notification badges**: Show count of pending requests per PO
|
|
789
|
+
- [ ] Keyboard navigation: Tab/arrow keys to select PO
|
|
790
|
+
- [ ] Click or Enter to focus a PO (especially ones with pending requests)
|
|
791
|
+
- [ ] Update in real-time as state changes
|
|
792
|
+
|
|
793
|
+
```
|
|
794
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
795
|
+
│ greeter │ │ reader │ │ coord │ │ debugger │
|
|
796
|
+
│ ○ │ │ ◐ │ │ ⚠ 2 │ │ ⚠ 1 │
|
|
797
|
+
└──────────┘ └──────────┘ └──────────┘ └──────────┘
|
|
798
|
+
^ 2 pending ^ 1 pending
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### message_log.rb
|
|
802
|
+
- [ ] Subscribe to message bus
|
|
803
|
+
- [ ] Display scrolling log of messages
|
|
804
|
+
- [ ] Color-code by capability type
|
|
805
|
+
- [ ] Truncate long messages
|
|
806
|
+
- [ ] Timestamp formatting
|
|
807
|
+
|
|
808
|
+
#### conversation.rb
|
|
809
|
+
- [ ] Show current conversation with active PO
|
|
810
|
+
- [ ] Render markdown responses (via `tty-markdown` or similar)
|
|
811
|
+
- [ ] Handle streaming output
|
|
812
|
+
|
|
813
|
+
#### input.rb
|
|
814
|
+
- [ ] Text input field
|
|
815
|
+
- [ ] History (up/down arrows)
|
|
816
|
+
- [ ] Handle special commands
|
|
817
|
+
|
|
818
|
+
#### notification_panel.rb
|
|
819
|
+
- [ ] List all pending HumanRequests across all POs
|
|
820
|
+
- [ ] Show: PO name, request type, question preview, age
|
|
821
|
+
- [ ] Keyboard navigation to select a request
|
|
822
|
+
- [ ] Enter to engage with selected request (opens Huh prompt)
|
|
823
|
+
- [ ] Toggle visibility with hotkey (e.g., `n` for notifications)
|
|
824
|
+
- [ ] Sort by age or PO name
|
|
825
|
+
|
|
826
|
+
```
|
|
827
|
+
┌─ PENDING REQUESTS (3) ────────────────────────────────────┐
|
|
828
|
+
│ │
|
|
829
|
+
│ ▸ coordinator "Create Ruby debugging specialist?" 2m │
|
|
830
|
+
│ debugger "Which bug should I focus on?" 45s │
|
|
831
|
+
│ reader "Delete all files in spec/?" 10s │
|
|
832
|
+
│ │
|
|
833
|
+
│ [Enter] Respond [Esc] Close [↑↓] Navigate │
|
|
834
|
+
└───────────────────────────────────────────────────────────┘
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
#### request_responder.rb
|
|
838
|
+
- [ ] Modal that appears when engaging with a HumanRequest
|
|
839
|
+
- [ ] Renders the full question with context
|
|
840
|
+
- [ ] Uses **Huh** components for input (select, text input, confirm)
|
|
841
|
+
- [ ] On submit: calls `human_queue.respond(request_id, value)`
|
|
842
|
+
- [ ] PO resumes execution automatically
|
|
843
|
+
|
|
844
|
+
#### po_inspector.rb
|
|
845
|
+
- [ ] Modal/panel that appears when inspecting a selected PO
|
|
846
|
+
- [ ] Shows the PO's full prompt (markdown body rendered via glamour)
|
|
847
|
+
- [ ] Shows capabilities with the "object model" framing:
|
|
848
|
+
- **Universal (inherited)**: The "BasicObject" - always available
|
|
849
|
+
- **Stdlib Primitives**: Opt-in standard library capabilities
|
|
850
|
+
- **Custom Primitives**: Runtime-generated primitives
|
|
851
|
+
- **Delegates (POs)**: Other prompt objects this one can call
|
|
852
|
+
- [ ] Each tool shows: name, description, parameters schema
|
|
853
|
+
- [ ] Keyboard shortcut to toggle (e.g., `i` for inspect)
|
|
854
|
+
- [ ] **Edit mode** to modify the PO's capabilities:
|
|
855
|
+
- Browse & add stdlib primitives
|
|
856
|
+
- Generate new primitive (PO describes need, we create it)
|
|
857
|
+
- Add/remove PO delegates
|
|
858
|
+
- [ ] Maybe show conversation history / message log filtered to this PO
|
|
859
|
+
|
|
860
|
+
```
|
|
861
|
+
┌─ INSPECT: coordinator ─────────────────────────────────────────────┐
|
|
862
|
+
│ │
|
|
863
|
+
│ PROMPT │
|
|
864
|
+
│ ──────────────────────────────────────────────────────────────── │
|
|
865
|
+
│ # Coordinator │
|
|
866
|
+
│ You orchestrate work by delegating to specialists... │
|
|
867
|
+
│ │
|
|
868
|
+
│ CAPABILITIES │
|
|
869
|
+
│ ──────────────────────────────────────────────────────────────── │
|
|
870
|
+
│ Universal (inherited): ask_human, think, create_capability, ... │
|
|
871
|
+
│ Stdlib: list_files, read_file, write_file, http_get │
|
|
872
|
+
│ Custom: (none) │
|
|
873
|
+
│ Delegates: greeter, reader │
|
|
874
|
+
│ │
|
|
875
|
+
│ [e] Edit Capabilities [Esc] Close [p] Prompt [h] History │
|
|
876
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
#### capability_editor.rb
|
|
880
|
+
- [ ] Sub-modal for editing a PO's capabilities
|
|
881
|
+
- [ ] Three tabs/sections:
|
|
882
|
+
1. **Add from Stdlib**: Browse categorized primitives, toggle on/off
|
|
883
|
+
2. **Generate New**: Describe what you need, system creates primitive
|
|
884
|
+
3. **Manage Delegates**: Add/remove other POs as delegates
|
|
885
|
+
- [ ] Changes update PO config in real-time
|
|
886
|
+
- [ ] Option to persist changes back to .md file
|
|
887
|
+
|
|
888
|
+
```
|
|
889
|
+
┌─ EDIT CAPABILITIES: coordinator ───────────────────────────────────┐
|
|
890
|
+
│ │
|
|
891
|
+
│ [Stdlib] [Generate] [Delegates] │
|
|
892
|
+
│ ───────────────────────────────────────────────────────────────── │
|
|
893
|
+
│ │
|
|
894
|
+
│ FILE SYSTEM NETWORK │
|
|
895
|
+
│ ───────────── ─────── │
|
|
896
|
+
│ [✓] read_file [✓] http_get │
|
|
897
|
+
│ [✓] write_file [ ] http_post │
|
|
898
|
+
│ [✓] list_files [ ] http_request │
|
|
899
|
+
│ [ ] delete_file │
|
|
900
|
+
│ [ ] move_file PROCESS │
|
|
901
|
+
│ ─────── │
|
|
902
|
+
│ DATA [ ] run_command │
|
|
903
|
+
│ ──── [ ] spawn_process │
|
|
904
|
+
│ [ ] json_parse │
|
|
905
|
+
│ [ ] yaml_parse ENVIRONMENT │
|
|
906
|
+
│ [ ] csv_parse ─────────── │
|
|
907
|
+
│ [ ] get_env │
|
|
908
|
+
│ [ ] current_time │
|
|
909
|
+
│ │
|
|
910
|
+
│ [Enter] Toggle [Tab] Next Section [Esc] Done │
|
|
911
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### 5.3 Streaming Support
|
|
915
|
+
|
|
916
|
+
- [ ] Update LLM adapter to support streaming
|
|
917
|
+
- [ ] Pass deltas through context callbacks
|
|
918
|
+
- [ ] UI updates character-by-character
|
|
919
|
+
|
|
920
|
+
### 5.4 State Management
|
|
921
|
+
|
|
922
|
+
- [ ] Track which PO is "active" (talking to human)
|
|
923
|
+
- [ ] Track which POs are "working" (processing)
|
|
924
|
+
- [ ] Broadcast state changes to UI
|
|
925
|
+
|
|
926
|
+
### 5.5 Spinners & Polish
|
|
927
|
+
|
|
928
|
+
- [ ] Show spinner during LLM calls
|
|
929
|
+
- [ ] Graceful handling of slow responses
|
|
930
|
+
- [ ] Keyboard shortcuts (e.g., `m` to toggle message log)
|
|
931
|
+
|
|
932
|
+
### Phase 5 Deliverable
|
|
933
|
+
|
|
934
|
+
- Full TUI experience
|
|
935
|
+
- See capability bar at top with **notification badges**
|
|
936
|
+
- See message log showing all traffic
|
|
937
|
+
- See conversation area with current PO
|
|
938
|
+
- **Notification panel** accessible via hotkey
|
|
939
|
+
- Navigate between POs, respond to pending requests
|
|
940
|
+
- Multiple POs can be waiting simultaneously without blocking
|
|
941
|
+
- Input at bottom
|
|
942
|
+
- **Demo 5 achievable** - human-in-the-loop feels natural and scalable
|
|
943
|
+
|
|
944
|
+
---
|
|
945
|
+
|
|
946
|
+
## Phase 6: Demo Ready
|
|
947
|
+
|
|
948
|
+
**Goal**: Everything works smoothly for an 8-minute demo.
|
|
949
|
+
|
|
950
|
+
### 6.1 Error Handling
|
|
951
|
+
|
|
952
|
+
- [ ] Graceful LLM API errors (rate limits, timeouts)
|
|
953
|
+
- [ ] Invalid capability references
|
|
954
|
+
- [ ] File system errors in primitives
|
|
955
|
+
- [ ] Malformed prompt object files
|
|
956
|
+
- [ ] Capability loop detection
|
|
957
|
+
|
|
958
|
+
### 6.2 Demo Script
|
|
959
|
+
|
|
960
|
+
- [ ] Write exact script for 8-minute demo
|
|
961
|
+
- [ ] Prepare prompt objects for each demo
|
|
962
|
+
- [ ] Time each section
|
|
963
|
+
- [ ] Identify where things could go wrong
|
|
964
|
+
|
|
965
|
+
### 6.3 Demo Prompt Objects
|
|
966
|
+
|
|
967
|
+
Finalize and test:
|
|
968
|
+
- [ ] `objects/greeter.md` - warm, welcoming, no capabilities
|
|
969
|
+
- [ ] `objects/reader.md` - file exploration specialist
|
|
970
|
+
- [ ] `objects/coordinator.md` - delegates to specialists
|
|
971
|
+
|
|
972
|
+
### 6.4 Backup Plan
|
|
973
|
+
|
|
974
|
+
- [ ] Record backup video of successful run
|
|
975
|
+
- [ ] Prepare fallback if live demo fails
|
|
976
|
+
- [ ] Have reset script to restore clean state
|
|
977
|
+
|
|
978
|
+
### 6.5 Practice
|
|
979
|
+
|
|
980
|
+
- [ ] Run through demo 5+ times
|
|
981
|
+
- [ ] Time it precisely
|
|
982
|
+
- [ ] Identify common failure points
|
|
983
|
+
- [ ] Prepare recovery strategies
|
|
984
|
+
|
|
985
|
+
### Phase 6 Deliverable
|
|
986
|
+
|
|
987
|
+
- Polished, tested demo
|
|
988
|
+
- Backup video ready
|
|
989
|
+
- Confident presenter
|
|
990
|
+
- **All 5 demos work smoothly**
|
|
991
|
+
|
|
992
|
+
---
|
|
993
|
+
|
|
994
|
+
## File Checklist
|
|
995
|
+
|
|
996
|
+
```
|
|
997
|
+
prompt_objects/
|
|
998
|
+
├── Gemfile
|
|
999
|
+
├── CLAUDE.md
|
|
1000
|
+
├── IMPLEMENTATION_PLAN.md
|
|
1001
|
+
├── design-doc-v2.md
|
|
1002
|
+
│
|
|
1003
|
+
├── exe/
|
|
1004
|
+
│ └── prompt_objects
|
|
1005
|
+
│
|
|
1006
|
+
├── lib/
|
|
1007
|
+
│ ├── prompt_objects.rb
|
|
1008
|
+
│ └── prompt_objects/
|
|
1009
|
+
│ ├── capability.rb
|
|
1010
|
+
│ ├── context.rb
|
|
1011
|
+
│ ├── environment.rb
|
|
1012
|
+
│ ├── executor.rb
|
|
1013
|
+
│ ├── human_queue.rb
|
|
1014
|
+
│ ├── loader.rb
|
|
1015
|
+
│ ├── message_bus.rb
|
|
1016
|
+
│ ├── primitive.rb
|
|
1017
|
+
│ ├── prompt_object.rb
|
|
1018
|
+
│ ├── registry.rb
|
|
1019
|
+
│ │
|
|
1020
|
+
│ ├── llm/
|
|
1021
|
+
│ │ ├── adapter.rb
|
|
1022
|
+
│ │ ├── openai_adapter.rb
|
|
1023
|
+
│ │ └── response.rb
|
|
1024
|
+
│ │
|
|
1025
|
+
│ ├── primitives/
|
|
1026
|
+
│ │ ├── list_files.rb
|
|
1027
|
+
│ │ ├── read_file.rb
|
|
1028
|
+
│ │ └── write_file.rb
|
|
1029
|
+
│ │
|
|
1030
|
+
│ ├── universal/
|
|
1031
|
+
│ │ ├── ask_human.rb
|
|
1032
|
+
│ │ ├── create_capability.rb
|
|
1033
|
+
│ │ └── think.rb
|
|
1034
|
+
│ │
|
|
1035
|
+
│ └── ui/
|
|
1036
|
+
│ ├── app.rb
|
|
1037
|
+
│ ├── capability_bar.rb
|
|
1038
|
+
│ ├── capability_editor.rb
|
|
1039
|
+
│ ├── conversation.rb
|
|
1040
|
+
│ ├── input.rb
|
|
1041
|
+
│ ├── message_log.rb
|
|
1042
|
+
│ ├── notification_panel.rb
|
|
1043
|
+
│ ├── po_inspector.rb
|
|
1044
|
+
│ └── request_responder.rb
|
|
1045
|
+
│
|
|
1046
|
+
├── objects/
|
|
1047
|
+
│ ├── coordinator.md
|
|
1048
|
+
│ ├── greeter.md
|
|
1049
|
+
│ └── reader.md
|
|
1050
|
+
│
|
|
1051
|
+
└── spec/
|
|
1052
|
+
└── (tests as needed)
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
---
|
|
1056
|
+
|
|
1057
|
+
## Dependencies Summary
|
|
1058
|
+
|
|
1059
|
+
```ruby
|
|
1060
|
+
# Gemfile
|
|
1061
|
+
source "https://rubygems.org"
|
|
1062
|
+
|
|
1063
|
+
# Core
|
|
1064
|
+
gem "ruby-openai" # LLM API (or anthropic SDK)
|
|
1065
|
+
gem "front_matter_parser" # YAML frontmatter parsing
|
|
1066
|
+
|
|
1067
|
+
# Charm TUI (Marco Roth's Ruby ports)
|
|
1068
|
+
gem "bubbletea" # Elm-inspired TUI framework
|
|
1069
|
+
gem "lipgloss" # CSS-like terminal styling
|
|
1070
|
+
gem "glamour" # Markdown rendering
|
|
1071
|
+
gem "bubbles" # Pre-built components (spinners, inputs, etc.)
|
|
1072
|
+
gem "huh" # Interactive forms/prompts
|
|
1073
|
+
```
|