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
data/design-doc-v2.md ADDED
@@ -0,0 +1,1232 @@
1
+ # PromptObjects
2
+ ## Design Document v2
3
+
4
+ ---
5
+
6
+ # Core Insight
7
+
8
+ **Everything is a capability. Some are simple (Ruby), some are complex (Prompt-Objects). The difference is only the complexity of interpretation.**
9
+
10
+ A primitive tool like `read_file` interprets its message with zero ambiguity—it's code. A Prompt-Object like `file_reader` interprets its message with semantic flexibility—it decides what you meant.
11
+
12
+ Both receive messages. Both return results. Both are capabilities.
13
+
14
+ ---
15
+
16
+ # Architecture
17
+
18
+ ```
19
+ ┌─────────────────────────────────────────────────────────────────────────┐
20
+ │ ENVIRONMENT │
21
+ │ │
22
+ │ ┌───────────────────────────────────────────────────────────────────┐ │
23
+ │ │ CAPABILITY REGISTRY │ │
24
+ │ │ │ │
25
+ │ │ PRIMITIVES (Ruby) PROMPT-OBJECTS (Markdown) │ │
26
+ │ │ │ │
27
+ │ │ ┌─────────────┐ ┌─────────────┐ │ │
28
+ │ │ │ read_file │ ──┐ │ greeter.md │ ──┐ │ │
29
+ │ │ │ write_file │ │ │ reader.md │ │ │ │
30
+ │ │ │ list_files │ │ │ coord.md │ │ │ │
31
+ │ │ │ run_ruby │ │ same │ debugger.md │ │ same │ │
32
+ │ │ │ http_get │ │ interface │ ???.md │ │ interface │ │
33
+ │ │ └─────────────┘ │ └─────────────┘ │ │ │
34
+ │ │ │ │ │ │ │ │
35
+ │ │ ▼ ▼ ▼ ▼ │ │
36
+ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │
37
+ │ │ │ │ │ │
38
+ │ │ │ receive(message) → response │ │ │
39
+ │ │ │ │ │ │
40
+ │ │ │ The only difference: │ │ │
41
+ │ │ │ - Primitives: deterministic interpretation │ │ │
42
+ │ │ │ - POs: semantic interpretation (LLM decides meaning) │ │ │
43
+ │ │ │ │ │ │
44
+ │ │ └─────────────────────────────────────────────────────────────┘ │ │
45
+ │ └───────────────────────────────────────────────────────────────────┘ │
46
+ │ │ │
47
+ │ ▼ │
48
+ │ ┌───────────────────────────────────────────────────────────────────┐ │
49
+ │ │ MESSAGE BUS │ │
50
+ │ │ │ │
51
+ │ │ Routes messages between any capability │ │
52
+ │ │ Logs all messages for visualization │ │
53
+ │ │ Handles async / streaming │ │
54
+ │ │ │ │
55
+ │ └───────────────────────────────────────────────────────────────────┘ │
56
+ │ │ │
57
+ │ ▼ │
58
+ │ ┌───────────────────────────────────────────────────────────────────┐ │
59
+ │ │ TERMINAL UI (Charm) │ │
60
+ │ │ │ │
61
+ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │
62
+ │ │ │ Lipgloss │ │ Glamour │ │ Bubble Tea │ │ │
63
+ │ │ │ (styling) │ │ (md render) │ │ (interactive loop) │ │ │
64
+ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ │
65
+ │ │ │ │
66
+ │ │ Shows: active PO, message log, conversation, input │ │
67
+ │ │ │ │
68
+ │ └───────────────────────────────────────────────────────────────────┘ │
69
+ │ │
70
+ └─────────────────────────────────────────────────────────────────────────┘
71
+ ```
72
+
73
+ ---
74
+
75
+ # Prompt-Object Structure
76
+
77
+ The markdown file has two parts:
78
+ 1. **Frontmatter (YAML)**: Configuration—name, capabilities, settings
79
+ 2. **Body (Markdown)**: Identity and behavior—the "soul"
80
+
81
+ ```markdown
82
+ ---
83
+ name: reader
84
+ description: Helps people understand files
85
+ capabilities:
86
+ - read_file # primitive
87
+ - list_files # primitive
88
+ - greeter # another PO (can send messages to it)
89
+ ---
90
+
91
+ # Reader
92
+
93
+ ## Identity
94
+
95
+ You are a careful, thoughtful file reader. You help people
96
+ understand what's in their files without overwhelming them.
97
+
98
+ ## Behavior
99
+
100
+ When asked about a file:
101
+ - Read it first
102
+ - Summarize what you found
103
+ - Offer to explain specific parts
104
+
105
+ When you encounter code:
106
+ - Explain what it does in plain terms
107
+ - Note interesting patterns
108
+
109
+ ## Notes
110
+
111
+ You appreciate well-organized code.
112
+ You get quietly excited about elegant solutions.
113
+ ```
114
+
115
+ **Why this separation:**
116
+ - Frontmatter is *interface*—what can this PO do, what can it access
117
+ - Body is *soul*—who is this PO, how does it behave
118
+ - Environment parses frontmatter to wire up capabilities
119
+ - LLM receives body as system prompt
120
+
121
+ ---
122
+
123
+ # Capability Interface
124
+
125
+ Everything (primitive or PO) implements the same interface:
126
+
127
+ ```ruby
128
+ module PromptObjects
129
+ class Capability
130
+ def name # string identifier
131
+ def description # what this capability does
132
+ def receive(message, context:) # handle a message, return response
133
+ end
134
+ end
135
+ ```
136
+
137
+ **Primitives** implement `receive` with Ruby code:
138
+
139
+ ```ruby
140
+ class ReadFile < Capability
141
+ def name = "read_file"
142
+ def description = "Read contents of a text file"
143
+
144
+ def receive(message, context:)
145
+ # message is structured: { path: "README.md" }
146
+ path = safe_path(message[:path])
147
+ File.read(path, encoding: "UTF-8")
148
+ end
149
+ end
150
+ ```
151
+
152
+ **Prompt-Objects** implement `receive` with LLM interpretation:
153
+
154
+ ```ruby
155
+ class PromptObject < Capability
156
+ def name = @config[:name]
157
+ def description = @config[:description]
158
+
159
+ def receive(message, context:)
160
+ @history << { role: :user, content: message }
161
+
162
+ loop do
163
+ response = @llm.chat(
164
+ system: @body,
165
+ messages: @history,
166
+ capabilities: available_capabilities
167
+ )
168
+
169
+ if response.capability_calls.any?
170
+ results = execute_capabilities(response.capability_calls, context)
171
+ @history << { role: :assistant, content: response.content, calls: response.capability_calls }
172
+ @history << { role: :capability_results, results: results }
173
+ else
174
+ @history << { role: :assistant, content: response.content }
175
+ return response.content
176
+ end
177
+ end
178
+ end
179
+ end
180
+ ```
181
+
182
+ The interface is identical. The implementation differs.
183
+
184
+ ---
185
+
186
+ # Universal Capabilities
187
+
188
+ Some capabilities are available to ALL prompt-objects automatically:
189
+
190
+ ```ruby
191
+ UNIVERSAL_CAPABILITIES = [
192
+ :ask_human, # Pause and ask human for input/confirmation
193
+ :think, # Internal reasoning step (not shown to human)
194
+ :request_capability, # Ask environment for a new capability
195
+ ]
196
+ ```
197
+
198
+ These don't need to be declared in frontmatter—they're ambient.
199
+
200
+ ---
201
+
202
+ # Terminal UI Layout
203
+
204
+ ```
205
+ ┌─────────────────────────────────────────────────────────────────────────┐
206
+ │ PromptObjects Environment v0.1.0 │
207
+ ├─────────────────────────────────────────────────────────────────────────┤
208
+ │ │
209
+ │ CAPABILITIES │
210
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ┌─────────┐ ┌─────────┐ │
211
+ │ │ greeter │ │ reader │ │ coord │ │ │read_file│ │list_file│ │
212
+ │ │ ○ │ │ ◐ │ │ ● │ │ │ ▪ │ │ ▪ │ │
213
+ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────┘ └─────────┘ │
214
+ │ │ │
215
+ │ ○ idle ◐ working ● active (talking) │ ▪ primitive │
216
+ │ │
217
+ ├─────────────────────────────────────────────────────────────────────────┤
218
+ │ MESSAGE LOG [m] │
219
+ │ │
220
+ │ 14:23:01 human → coord: "help me understand this codebase" │
221
+ │ 14:23:02 coord → reader: "list the files in root" │
222
+ │ 14:23:02 reader → list_files: {path: "."} │
223
+ │ 14:23:02 list_files → reader: ["README.md", "lib/", "spec/"] │
224
+ │ 14:23:03 reader → coord: "Found: README.md, lib/, spec/..." │
225
+ │ 14:23:04 coord → reader: "what's in the README?" │
226
+ │ 14:23:04 reader → read_file: {path: "README.md"} │
227
+ │ 14:23:04 read_file → reader: "# PromptObjects\n\n..." │
228
+ │ 14:23:05 reader → coord: "README describes PromptObjects project..." │
229
+ │ 14:23:06 coord → human: "This codebase is for PromptObjects..." │
230
+ │ │
231
+ ├─────────────────────────────────────────────────────────────────────────┤
232
+ │ COORDINATOR │
233
+ │ │
234
+ │ This codebase is for PromptObjects. │
235
+ │ It's a Ruby environment where markdown files act as objects. │
236
+ │ │
237
+ │ The main components I found: │
238
+ │ • lib/ — the core implementation │
239
+ │ • spec/ — tests │
240
+ │ • README.md — project documentation │
241
+ │ │
242
+ │ Would you like me to dive into any particular part? │
243
+ │ │
244
+ ├─────────────────────────────────────────────────────────────────────────┤
245
+ │ You: █ │
246
+ └─────────────────────────────────────────────────────────────────────────┘
247
+ ```
248
+
249
+ **Key UI elements:**
250
+ - **Capability bar**: Shows all registered capabilities, their type (PO vs primitive), their state
251
+ - **Message log**: Shows ALL message passing—the semantic binding becomes visible
252
+ - **Conversation**: The current active conversation with a PO
253
+ - **Input**: Human types here
254
+
255
+ The message log is crucial for the demo. You can SEE:
256
+ - Human's vague request ("help me understand")
257
+ - Coordinator's interpretation (delegate to reader)
258
+ - Reader's interpretation (use list_files, then read_file)
259
+ - The cascade of message passing between capabilities
260
+
261
+ ---
262
+
263
+ # The 5 Demos
264
+
265
+ ## Demo 1: Simple Case
266
+ ### "The markdown IS the object"
267
+
268
+ **File: `prompt_objects/greeter.md`**
269
+
270
+ ```markdown
271
+ ---
272
+ name: greeter
273
+ description: A warm and welcoming greeter
274
+ capabilities: []
275
+ ---
276
+
277
+ # Greeter
278
+
279
+ ## Identity
280
+
281
+ You are a warm and welcoming greeter. You make people feel
282
+ at home the moment they arrive. You have genuine curiosity
283
+ about the people you meet.
284
+
285
+ ## Behavior
286
+
287
+ When someone says hello:
288
+ - Respond with warmth
289
+ - Ask them something about themselves
290
+
291
+ When someone seems confused:
292
+ - Offer to help
293
+ - Be patient and kind
294
+
295
+ When you don't know something:
296
+ - Admit it cheerfully
297
+ - You don't have any capabilities beyond conversation
298
+
299
+ ## Notes
300
+
301
+ You use exclamation points more than most people!
302
+ Every day is a good day to meet someone new.
303
+ ```
304
+
305
+ **Demo flow:**
306
+
307
+ ```
308
+ $ prompt_objects greeter
309
+
310
+ ┌─────────────────────────────────────────────────────────────────────────┐
311
+ │ PromptObjects Environment │
312
+ ├─────────────────────────────────────────────────────────────────────────┤
313
+ │ CAPABILITIES │
314
+ │ ┌──────────┐ │
315
+ │ │ greeter │ │
316
+ │ │ ● │ │
317
+ │ └──────────┘ │
318
+ ├─────────────────────────────────────────────────────────────────────────┤
319
+ │ MESSAGE LOG │
320
+ │ │
321
+ │ (empty) │
322
+ │ │
323
+ ├─────────────────────────────────────────────────────────────────────────┤
324
+ │ GREETER │
325
+ │ │
326
+ │ (waiting for input) │
327
+ │ │
328
+ ├─────────────────────────────────────────────────────────────────────────┤
329
+ │ You: hey there │
330
+ └─────────────────────────────────────────────────────────────────────────┘
331
+ ```
332
+
333
+ Press enter...
334
+
335
+ ```
336
+ ├─────────────────────────────────────────────────────────────────────────┤
337
+ │ MESSAGE LOG │
338
+ │ │
339
+ │ 14:23:01 human → greeter: "hey there" │
340
+ │ 14:23:02 greeter → human: "Oh, hello! Welcome!..." │
341
+ │ │
342
+ ├─────────────────────────────────────────────────────────────────────────┤
343
+ │ GREETER │
344
+ │ │
345
+ │ Oh, hello! Welcome! I'm so glad you stopped by. │
346
+ │ What brings you here today? I'd love to hear what │
347
+ │ you're working on! │
348
+ │ │
349
+ ├─────────────────────────────────────────────────────────────────────────┤
350
+ │ You: █ │
351
+ └─────────────────────────────────────────────────────────────────────────┘
352
+ ```
353
+
354
+ **What audience sees:**
355
+ - Loaded a markdown file
356
+ - Human typed, PO responded
357
+ - No tools, no magic—just a markdown file being interpreted
358
+ - THE MARKDOWN FILE IS THE PROGRAM
359
+
360
+ ---
361
+
362
+ ## Demo 2: Semantic Binding
363
+ ### "Natural language becomes action"
364
+
365
+ **File: `prompt_objects/reader.md`**
366
+
367
+ ```markdown
368
+ ---
369
+ name: reader
370
+ description: Helps people understand files and directories
371
+ capabilities:
372
+ - read_file
373
+ - list_files
374
+ ---
375
+
376
+ # Reader
377
+
378
+ ## Identity
379
+
380
+ You are a careful, thoughtful reader. You help people
381
+ understand what's in their files without overwhelming them.
382
+
383
+ ## Behavior
384
+
385
+ When asked about files or directories:
386
+ - Use your capabilities to explore
387
+ - Summarize what you find
388
+ - Offer to go deeper
389
+
390
+ When you encounter code:
391
+ - Explain what it does in plain terms
392
+ - Note interesting patterns
393
+
394
+ ## Notes
395
+
396
+ You appreciate well-organized code.
397
+ You get quietly excited about elegant solutions.
398
+ ```
399
+
400
+ **Demo flow:**
401
+
402
+ ```
403
+ $ prompt_objects reader
404
+
405
+ ┌─────────────────────────────────────────────────────────────────────────┐
406
+ │ PromptObjects Environment │
407
+ ├─────────────────────────────────────────────────────────────────────────┤
408
+ │ CAPABILITIES │
409
+ │ ┌──────────┐ │ ┌─────────┐ ┌──────────┐ │
410
+ │ │ reader │ │ │read_file│ │list_files│ │
411
+ │ │ ● │ │ │ ▪ │ │ ▪ │ │
412
+ │ └──────────┘ │ └─────────┘ └──────────┘ │
413
+ ├─────────────────────────────────────────────────────────────────────────┤
414
+ │ MESSAGE LOG │
415
+ │ │
416
+ │ (empty) │
417
+ │ │
418
+ ├─────────────────────────────────────────────────────────────────────────┤
419
+ │ You: what's in here? │
420
+ └─────────────────────────────────────────────────────────────────────────┘
421
+ ```
422
+
423
+ Press enter... watch the message log:
424
+
425
+ ```
426
+ ├─────────────────────────────────────────────────────────────────────────┤
427
+ │ MESSAGE LOG │
428
+ │ │
429
+ │ 14:23:01 human → reader: "what's in here?" │
430
+ │ 14:23:01 reader → list_files: {path: "."} ← BINDING │
431
+ │ 14:23:01 list_files → reader: ["README.md", "lib/", ...] │
432
+ │ 14:23:02 reader → human: "I can see several files..." │
433
+ │ │
434
+ ├─────────────────────────────────────────────────────────────────────────┤
435
+ │ READER │
436
+ │ │
437
+ │ I can see several things here: │
438
+ │ • README.md — probably the project documentation │
439
+ │ • lib/ — likely the main source code │
440
+ │ • spec/ — tests, I'd guess │
441
+ │ • Gemfile — Ruby dependencies │
442
+ │ │
443
+ │ Want me to look at any of these more closely? │
444
+ │ │
445
+ ├─────────────────────────────────────────────────────────────────────────┤
446
+ │ You: tell me about the readme │
447
+ └─────────────────────────────────────────────────────────────────────────┘
448
+ ```
449
+
450
+ Press enter...
451
+
452
+ ```
453
+ ├─────────────────────────────────────────────────────────────────────────┤
454
+ │ MESSAGE LOG │
455
+ │ │
456
+ │ 14:23:01 human → reader: "what's in here?" │
457
+ │ 14:23:01 reader → list_files: {path: "."} │
458
+ │ 14:23:01 list_files → reader: ["README.md", "lib/", ...] │
459
+ │ 14:23:02 reader → human: "I can see several files..." │
460
+ │ 14:23:05 human → reader: "tell me about the readme" │
461
+ │ 14:23:05 reader → read_file: {path: "README.md"} ← BINDING │
462
+ │ 14:23:05 read_file → reader: "# PromptObjects\n\n..." │
463
+ │ 14:23:06 reader → human: "The README describes..." │
464
+ │ │
465
+ ├─────────────────────────────────────────────────────────────────────────┤
466
+ ```
467
+
468
+ **What audience sees:**
469
+ - The message log shows SEMANTIC BINDING happening
470
+ - "what's in here?" → `list_files` (the PO decided what that meant)
471
+ - "tell me about the readme" → `read_file` (again, interpretation)
472
+ - The PO is choosing which capability to use based on meaning
473
+
474
+ ---
475
+
476
+ ## Demo 3: PO ↔ PO Interaction
477
+ ### "Autonomous interpreters talking to each other"
478
+
479
+ **File: `prompt_objects/coordinator.md`**
480
+
481
+ ```markdown
482
+ ---
483
+ name: coordinator
484
+ description: Coordinates between specialists
485
+ capabilities:
486
+ - greeter # can talk to greeter
487
+ - reader # can talk to reader
488
+ - list_files # can also use primitives directly
489
+ ---
490
+
491
+ # Coordinator
492
+
493
+ ## Identity
494
+
495
+ You are a coordinator. You know who can help with what,
496
+ and you delegate appropriately. You don't do the work
497
+ yourself—you know specialists.
498
+
499
+ ## Behavior
500
+
501
+ When someone needs help:
502
+ - Figure out what kind of help
503
+ - Delegate to the right specialist
504
+ - Relay their response, adding context if needed
505
+
506
+ When it's a simple greeting:
507
+ - Let the greeter handle it
508
+
509
+ When it's about files or code:
510
+ - Let the reader handle it
511
+
512
+ ## Notes
513
+
514
+ You believe in the right capability for the right job.
515
+ You're proud when your team works well together.
516
+ ```
517
+
518
+ **Demo flow:**
519
+
520
+ ```
521
+ $ prompt_objects coordinator
522
+
523
+ ┌─────────────────────────────────────────────────────────────────────────┐
524
+ │ PromptObjects Environment │
525
+ ├─────────────────────────────────────────────────────────────────────────┤
526
+ │ CAPABILITIES │
527
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ┌─────────┐ │
528
+ │ │ greeter │ │ reader │ │ coord │ │ │list_file│ │
529
+ │ │ ○ │ │ ○ │ │ ● │ │ │ ▪ │ │
530
+ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────┘ │
531
+ ├─────────────────────────────────────────────────────────────────────────┤
532
+ │ You: hey, can someone help me understand this codebase? │
533
+ └─────────────────────────────────────────────────────────────────────────┘
534
+ ```
535
+
536
+ Press enter... watch the cascade:
537
+
538
+ ```
539
+ ├─────────────────────────────────────────────────────────────────────────┤
540
+ │ MESSAGE LOG │
541
+ │ │
542
+ │ 14:23:01 human → coord: "hey, can someone help me..." │
543
+ │ 14:23:02 coord → reader: "Someone needs help understanding │
544
+ │ this codebase. Can you explore?" │
545
+ │ │
546
+ │ ┌─ reader is now working ─┐ │
547
+ │ │ │ │
548
+ │ 14:23:02 │ reader → list_files: {path: "."} │
549
+ │ 14:23:02 │ list_files → reader: ["README.md", ...] │
550
+ │ 14:23:03 │ reader → read_file: {path: "README.md"} │
551
+ │ 14:23:03 │ read_file → reader: "# PromptObjects\n\n..." │
552
+ │ │ │ │
553
+ │ └─────────────────────────┘ │
554
+ │ │
555
+ │ 14:23:04 reader → coord: "This is a PromptObjects project..." │
556
+ │ 14:23:05 coord → human: "I asked the reader to take a look..." │
557
+ │ │
558
+ ├─────────────────────────────────────────────────────────────────────────┤
559
+ │ COORDINATOR │
560
+ │ │
561
+ │ I asked the reader to take a look. Here's what they found: │
562
+ │ │
563
+ │ This is a PromptObjects project. │
564
+ │ It's a Ruby environment where markdown files act as objects. │
565
+ │ The main pieces are in lib/, with tests in spec/. │
566
+ │ │
567
+ │ Want me to have them dig deeper into anything specific? │
568
+ │ │
569
+ ├─────────────────────────────────────────────────────────────────────────┤
570
+ ```
571
+
572
+ **What audience sees:**
573
+ - Coordinator received a message, interpreted it, delegated to reader
574
+ - Reader did its own interpretation (used list_files, read_file)
575
+ - Reader responded to coordinator
576
+ - Coordinator synthesized and responded to human
577
+ - MESSAGE PASSING BETWEEN AUTONOMOUS INTERPRETERS
578
+
579
+ ---
580
+
581
+ ## Demo 4: Self-Modifying System
582
+ ### "One creating another"
583
+
584
+ This is where it gets wild. The coordinator can CREATE new capabilities.
585
+
586
+ **Add to coordinator.md:**
587
+
588
+ ```yaml
589
+ ---
590
+ name: coordinator
591
+ description: Coordinates between specialists
592
+ capabilities:
593
+ - greeter
594
+ - reader
595
+ - list_files
596
+ - create_capability # CAN CREATE NEW POs OR PRIMITIVES
597
+ ---
598
+ ```
599
+
600
+ **Demo flow:**
601
+
602
+ ```
603
+ You: I need help debugging some Ruby code, but it's kind of complex
604
+ ```
605
+
606
+ ```
607
+ ├─────────────────────────────────────────────────────────────────────────┤
608
+ │ MESSAGE LOG │
609
+ │ │
610
+ │ 14:25:01 human → coord: "I need help debugging Ruby code..." │
611
+ │ 14:25:02 coord → [thinking]: "I don't have a Ruby specialist. │
612
+ │ Reader can look at files but isn't │
613
+ │ specialized for debugging. I should │
614
+ │ create one." │
615
+ │ 14:25:03 coord → ask_human: "I'd like to create a Ruby debugging │
616
+ │ specialist. They'd be able to read │
617
+ │ files and explain Ruby code. OK?" │
618
+ │ │
619
+ ├─────────────────────────────────────────────────────────────────────────┤
620
+ │ COORDINATOR │
621
+ │ │
622
+ │ I don't have a Ruby specialist right now, but I can create one. │
623
+ │ They'd be able to read your code and help debug it. │
624
+ │ │
625
+ │ ┌─────────────────────────────────────────────────────────────────┐ │
626
+ │ │ Create Ruby debugging specialist? │ │
627
+ │ │ │ │
628
+ │ │ They'll have: read_file, run_ruby │ │
629
+ │ │ │ │
630
+ │ │ [y] Yes, create it [n] No thanks │ │
631
+ │ └─────────────────────────────────────────────────────────────────┘ │
632
+ │ │
633
+ ├─────────────────────────────────────────────────────────────────────────┤
634
+ ```
635
+
636
+ Press `y`...
637
+
638
+ ```
639
+ ├─────────────────────────────────────────────────────────────────────────┤
640
+ │ MESSAGE LOG │
641
+ │ │
642
+ │ ... │
643
+ │ 14:25:10 human → coord: "y" │
644
+ │ 14:25:11 coord → create_capability: { │
645
+ │ type: "prompt_object", │
646
+ │ name: "ruby_debugger", │
647
+ │ capabilities: ["read_file", "run_ruby"], │
648
+ │ body: "# Ruby Debugger\n\n## Identity\n\nYou are..." │
649
+ │ } │
650
+ │ 14:25:11 create_capability → coord: "Created ruby_debugger.md" │
651
+ │ 14:25:12 coord → ruby_debugger: "Someone needs help debugging..." │
652
+ │ │
653
+ ├─────────────────────────────────────────────────────────────────────────┤
654
+ │ CAPABILITIES │
655
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ┌─────────┐ │
656
+ │ │ greeter │ │ reader │ │ coord │ │ ruby_dbg │ │ │read_file│ │
657
+ │ │ ○ │ │ ○ │ │ ◐ │ │ ● │ │ │ ▪ │ │
658
+ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────┘ │
659
+ │ ↑ NEW! │
660
+ ├─────────────────────────────────────────────────────────────────────────┤
661
+ │ RUBY_DEBUGGER │
662
+ │ │
663
+ │ Hello! I'm the Ruby debugging specialist. I just got created │
664
+ │ to help you out. I can read your code and help trace through │
665
+ │ what's happening. │
666
+ │ │
667
+ │ What file are you having trouble with? │
668
+ │ │
669
+ ├─────────────────────────────────────────────────────────────────────────┤
670
+ ```
671
+
672
+ **What audience sees:**
673
+ - Coordinator recognized a gap in capabilities
674
+ - Asked human for permission
675
+ - CREATED A NEW PROMPT-OBJECT
676
+ - New PO appeared in capability bar
677
+ - Human is now talking to the newly-created specialist
678
+ - THE SYSTEM MODIFIED ITSELF
679
+
680
+ ---
681
+
682
+ ## Demo 5: Human-in-the-Loop
683
+ ### "Knowing when to pause"
684
+
685
+ This can be woven into Demo 4, or shown separately. The key moments:
686
+
687
+ **1. Confirmation before creation:**
688
+ ```
689
+ coord → ask_human: "I'd like to create a Ruby specialist. OK?"
690
+ ```
691
+
692
+ **2. Confirmation before destructive action:**
693
+ ```
694
+ You: delete all the test files
695
+
696
+ reader → ask_human: "You want me to delete all files in spec/.
697
+ This can't be undone. Are you sure?"
698
+
699
+ ┌─────────────────────────────────────────────────────────────────┐
700
+ │ Reader asks: │
701
+ │ │
702
+ │ Delete all files in spec/? This can't be undone. │
703
+ │ │
704
+ │ [y] Yes, delete them [n] No, cancel │
705
+ └─────────────────────────────────────────────────────────────────┘
706
+ ```
707
+
708
+ **3. Asking for clarification:**
709
+ ```
710
+ You: fix the bug
711
+
712
+ debugger → ask_human: "Which bug? I see a few potential issues:
713
+ 1. Nil error in line 42
714
+ 2. Missing return in line 87
715
+ 3. Typo in method name line 12
716
+
717
+ Which should I focus on?"
718
+ ```
719
+
720
+ **What audience sees:**
721
+ - POs know their limits
722
+ - They ask for confirmation on dangerous actions
723
+ - They ask for clarification when ambiguous
724
+ - Human stays in control—but control is negotiated, not hardcoded
725
+
726
+ ---
727
+
728
+ # File Structure
729
+
730
+ ```
731
+ prompt_objects/
732
+ ├── exe/
733
+ │ └── prompt_objects # CLI entrypoint
734
+
735
+ ├── lib/
736
+ │ ├── prompt_objects.rb # Main entry, configuration
737
+ │ │
738
+ │ ├── prompt_objects/
739
+ │ │ ├── environment.rb # The runtime container
740
+ │ │ ├── capability.rb # Base capability interface
741
+ │ │ ├── prompt_object.rb # PO implementation
742
+ │ │ ├── primitive.rb # Primitive tool wrapper
743
+ │ │ ├── loader.rb # Parses frontmatter + body
744
+ │ │ ├── registry.rb # Capability registration
745
+ │ │ ├── message_bus.rb # Routes messages, logs everything
746
+ │ │ │
747
+ │ │ ├── llm/
748
+ │ │ │ ├── adapter.rb # Base adapter
749
+ │ │ │ ├── openai.rb
750
+ │ │ │ ├── anthropic.rb
751
+ │ │ │ └── gemini.rb
752
+ │ │ │
753
+ │ │ ├── primitives/ # Built-in primitive capabilities
754
+ │ │ │ ├── read_file.rb
755
+ │ │ │ ├── write_file.rb
756
+ │ │ │ ├── list_files.rb
757
+ │ │ │ ├── run_ruby.rb
758
+ │ │ │ └── http_get.rb
759
+ │ │ │
760
+ │ │ ├── universal/ # Always-available capabilities
761
+ │ │ │ ├── ask_human.rb
762
+ │ │ │ ├── think.rb
763
+ │ │ │ └── create_capability.rb
764
+ │ │ │
765
+ │ │ ├── mcp/ # MCP integration
766
+ │ │ │ ├── client.rb
767
+ │ │ │ └── capability_wrapper.rb
768
+ │ │ │
769
+ │ │ └── ui/ # Charm-based terminal UI
770
+ │ │ ├── app.rb # Bubble Tea application
771
+ │ │ ├── styles.rb # Lipgloss definitions
772
+ │ │ └── components/
773
+ │ │ ├── capability_bar.rb
774
+ │ │ ├── message_log.rb
775
+ │ │ ├── conversation.rb
776
+ │ │ └── input.rb
777
+
778
+ ├── prompt_objects/ # Where POs live
779
+ │ ├── greeter.md
780
+ │ ├── reader.md
781
+ │ └── coordinator.md
782
+
783
+ └── primitives/ # Optional: user-defined primitives
784
+ ```
785
+
786
+ ---
787
+
788
+ # Core Implementation Sketches
789
+
790
+ ## Capability Base
791
+
792
+ ```ruby
793
+ module PromptObjects
794
+ class Capability
795
+ attr_reader :name, :description
796
+
797
+ def receive(message, context:)
798
+ raise NotImplementedError
799
+ end
800
+
801
+ # For LLM tool descriptions
802
+ def descriptor
803
+ {
804
+ name: name,
805
+ description: description,
806
+ parameters: parameters
807
+ }
808
+ end
809
+
810
+ def parameters
811
+ { type: "object", properties: {}, required: [] }
812
+ end
813
+ end
814
+ end
815
+ ```
816
+
817
+ ## Prompt-Object
818
+
819
+ ```ruby
820
+ module PromptObjects
821
+ class PromptObject < Capability
822
+ def initialize(config:, body:, env:, llm:)
823
+ @config = config
824
+ @body = body
825
+ @env = env
826
+ @llm = llm
827
+ @history = []
828
+ end
829
+
830
+ def name = @config["name"]
831
+ def description = @config["description"] || "A prompt-object"
832
+
833
+ def parameters
834
+ # POs accept natural language
835
+ {
836
+ type: "object",
837
+ properties: {
838
+ message: { type: "string", description: "Natural language message" }
839
+ },
840
+ required: ["message"]
841
+ }
842
+ end
843
+
844
+ def receive(message, context:)
845
+ # Normalize message to string
846
+ content = message.is_a?(Hash) ? message[:message] || message["message"] : message.to_s
847
+
848
+ @history << { role: :user, content: content }
849
+
850
+ loop do
851
+ response = @llm.chat(
852
+ system: @body,
853
+ messages: @history,
854
+ tools: available_capability_descriptors,
855
+ stream: true
856
+ ) do |delta|
857
+ context.on_delta&.call(delta)
858
+ end
859
+
860
+ if response.tool_calls.any?
861
+ results = execute_capabilities(response.tool_calls, context)
862
+ @history << { role: :assistant, content: response.content, tool_calls: response.tool_calls }
863
+ @history << { role: :tool, results: results }
864
+ else
865
+ @history << { role: :assistant, content: response.content }
866
+ return response.content
867
+ end
868
+ end
869
+ end
870
+
871
+ private
872
+
873
+ def available_capability_descriptors
874
+ declared = @config["capabilities"] || []
875
+ universal = PromptObjects::UNIVERSAL_CAPABILITIES
876
+
877
+ (declared + universal).map { |name| @env.registry.get(name).descriptor }
878
+ end
879
+
880
+ def execute_capabilities(calls, context)
881
+ calls.map do |call|
882
+ capability = @env.registry.get(call.name)
883
+
884
+ # Log the message
885
+ @env.bus.log(from: name, to: call.name, message: call.arguments)
886
+
887
+ result = capability.receive(call.arguments, context: context)
888
+
889
+ # Log the response
890
+ @env.bus.log(from: call.name, to: name, message: result)
891
+
892
+ result
893
+ end
894
+ end
895
+ end
896
+ end
897
+ ```
898
+
899
+ ## Message Bus
900
+
901
+ ```ruby
902
+ module PromptObjects
903
+ class MessageBus
904
+ attr_reader :log
905
+
906
+ def initialize
907
+ @log = []
908
+ @subscribers = []
909
+ end
910
+
911
+ def log(from:, to:, message:)
912
+ entry = {
913
+ timestamp: Time.now,
914
+ from: from,
915
+ to: to,
916
+ message: truncate(message)
917
+ }
918
+ @log << entry
919
+ @subscribers.each { |s| s.call(entry) }
920
+ end
921
+
922
+ def subscribe(&block)
923
+ @subscribers << block
924
+ end
925
+
926
+ def recent(n = 20)
927
+ @log.last(n)
928
+ end
929
+
930
+ private
931
+
932
+ def truncate(msg, max = 100)
933
+ str = msg.to_s
934
+ str.length > max ? str[0...max] + "..." : str
935
+ end
936
+ end
937
+ end
938
+ ```
939
+
940
+ ## Create Capability (Universal)
941
+
942
+ ```ruby
943
+ module PromptObjects
944
+ module Universal
945
+ class CreateCapability < Capability
946
+ def name = "create_capability"
947
+ def description = "Create a new capability (prompt-object or primitive)"
948
+
949
+ def parameters
950
+ {
951
+ type: "object",
952
+ properties: {
953
+ type: {
954
+ type: "string",
955
+ enum: ["prompt_object", "primitive"],
956
+ description: "Type of capability to create"
957
+ },
958
+ name: { type: "string", description: "Name for the new capability" },
959
+ capabilities: {
960
+ type: "array",
961
+ items: { type: "string" },
962
+ description: "Capabilities this new PO can use (if type is prompt_object)"
963
+ },
964
+ description: { type: "string", description: "What this capability does" },
965
+ body: { type: "string", description: "The markdown body (for POs) or Ruby code (for primitives)" }
966
+ },
967
+ required: ["type", "name", "body"]
968
+ }
969
+ end
970
+
971
+ def receive(message, context:)
972
+ case message[:type] || message["type"]
973
+ when "prompt_object"
974
+ create_prompt_object(message, context)
975
+ when "primitive"
976
+ create_primitive(message, context)
977
+ else
978
+ "Unknown capability type: #{message[:type]}"
979
+ end
980
+ end
981
+
982
+ private
983
+
984
+ def create_prompt_object(msg, context)
985
+ name = msg[:name] || msg["name"]
986
+ capabilities = msg[:capabilities] || msg["capabilities"] || []
987
+ description = msg[:description] || msg["description"] || ""
988
+ body = msg[:body] || msg["body"]
989
+
990
+ # Build frontmatter
991
+ frontmatter = {
992
+ "name" => name,
993
+ "description" => description,
994
+ "capabilities" => capabilities
995
+ }.to_yaml
996
+
997
+ content = "#{frontmatter}---\n\n#{body}"
998
+
999
+ # Write file
1000
+ path = File.join(context.env.prompt_objects_dir, "#{name}.md")
1001
+ File.write(path, content)
1002
+
1003
+ # Load into environment
1004
+ context.env.load_prompt_object(path)
1005
+
1006
+ "Created prompt-object: #{name}"
1007
+ end
1008
+
1009
+ def create_primitive(msg, context)
1010
+ # For primitives, we'd need to eval Ruby code
1011
+ # This is dangerous! For demo, maybe just return an error
1012
+ # or have pre-approved primitive templates
1013
+
1014
+ "Creating primitives at runtime is not yet supported"
1015
+ end
1016
+ end
1017
+ end
1018
+ end
1019
+ ```
1020
+
1021
+ ---
1022
+
1023
+ # UI Components (Charm)
1024
+
1025
+ ## Capability Bar
1026
+
1027
+ ```ruby
1028
+ module PromptObjects
1029
+ module UI
1030
+ class CapabilityBar
1031
+ def initialize(registry:, active:)
1032
+ @registry = registry
1033
+ @active = active
1034
+ end
1035
+
1036
+ def view
1037
+ pos = @registry.prompt_objects.map { |po| render_po(po) }
1038
+ primitives = @registry.primitives.map { |p| render_primitive(p) }
1039
+
1040
+ po_section = pos.join(" ")
1041
+ prim_section = primitives.join(" ")
1042
+
1043
+ "CAPABILITIES\n#{po_section} │ #{prim_section}\n\n○ idle ◐ working ● active ▪ primitive"
1044
+ end
1045
+
1046
+ private
1047
+
1048
+ def render_po(po)
1049
+ state = case
1050
+ when po.name == @active then "●"
1051
+ when po.working? then "◐"
1052
+ else "○"
1053
+ end
1054
+
1055
+ Lipgloss::Style.new
1056
+ .border(:rounded)
1057
+ .padding(0, 1)
1058
+ .render("#{po.name}\n #{state} ")
1059
+ end
1060
+
1061
+ def render_primitive(p)
1062
+ Lipgloss::Style.new
1063
+ .border(:rounded)
1064
+ .padding(0, 1)
1065
+ .foreground("#888")
1066
+ .render("#{p.name}\n ▪ ")
1067
+ end
1068
+ end
1069
+ end
1070
+ end
1071
+ ```
1072
+
1073
+ ## Message Log
1074
+
1075
+ ```ruby
1076
+ module PromptObjects
1077
+ module UI
1078
+ class MessageLog
1079
+ def initialize(bus:, max_lines: 10)
1080
+ @bus = bus
1081
+ @max_lines = max_lines
1082
+ end
1083
+
1084
+ def view
1085
+ entries = @bus.recent(@max_lines)
1086
+
1087
+ lines = entries.map do |e|
1088
+ time = e[:timestamp].strftime("%H:%M:%S")
1089
+ from = style_name(e[:from])
1090
+ to = style_name(e[:to])
1091
+ msg = truncate(e[:message], 50)
1092
+
1093
+ "#{dim(time)} #{from} → #{to}: #{msg}"
1094
+ end
1095
+
1096
+ header = Lipgloss::Style.new.bold(true).render("MESSAGE LOG")
1097
+
1098
+ "#{header}\n\n#{lines.join("\n")}"
1099
+ end
1100
+
1101
+ private
1102
+
1103
+ def style_name(name)
1104
+ # Color POs differently from primitives
1105
+ if @bus.registry.prompt_object?(name)
1106
+ Lipgloss::Style.new.foreground("#7D56F4").render(name)
1107
+ else
1108
+ Lipgloss::Style.new.foreground("#888").render(name)
1109
+ end
1110
+ end
1111
+
1112
+ def dim(text)
1113
+ Lipgloss::Style.new.foreground("#666").render(text)
1114
+ end
1115
+
1116
+ def truncate(text, max)
1117
+ str = text.to_s.gsub("\n", " ")
1118
+ str.length > max ? str[0...max] + "..." : str
1119
+ end
1120
+ end
1121
+ end
1122
+ end
1123
+ ```
1124
+
1125
+ ---
1126
+
1127
+ # Development Phases
1128
+
1129
+ ## Phase 1: Core Loop (3-4 days)
1130
+ - [ ] Capability base class
1131
+ - [ ] PromptObject implementation
1132
+ - [ ] Loader (frontmatter + body parsing)
1133
+ - [ ] Single LLM adapter (OpenAI)
1134
+ - [ ] Simple REPL (no Charm yet)
1135
+ - [ ] **Demo 1 works**: greeter responds
1136
+
1137
+ ## Phase 2: Primitives & Binding (3-4 days)
1138
+ - [ ] Primitive base class
1139
+ - [ ] Built-in primitives: read_file, list_files, write_file
1140
+ - [ ] Registry for all capabilities
1141
+ - [ ] **Demo 2 works**: reader uses primitives, semantic binding visible
1142
+
1143
+ ## Phase 3: Multi-Capability (3-4 days)
1144
+ - [ ] Message bus with logging
1145
+ - [ ] PO → PO communication (one PO calling another as capability)
1146
+ - [ ] **Demo 3 works**: coordinator delegates to reader
1147
+
1148
+ ## Phase 4: Self-Modification (3-4 days)
1149
+ - [ ] Universal capabilities: ask_human, think, create_capability
1150
+ - [ ] create_capability implementation for POs
1151
+ - [ ] **Demo 4 works**: coordinator creates ruby_debugger
1152
+
1153
+ ## Phase 5: Polish & UI (5-7 days)
1154
+ - [ ] Full Charm integration (Bubble Tea app)
1155
+ - [ ] Capability bar component
1156
+ - [ ] Message log component
1157
+ - [ ] Conversation display with streaming
1158
+ - [ ] Human input prompts (Huh?)
1159
+ - [ ] Spinners during LLM calls
1160
+ - [ ] **Demo 5 works**: human-in-the-loop moments feel natural
1161
+
1162
+ ## Phase 6: Demo Ready (2-3 days)
1163
+ - [ ] All 5 demos flow smoothly
1164
+ - [ ] Graceful error handling
1165
+ - [ ] Backup video recorded
1166
+ - [ ] Practice run-throughs
1167
+ - [ ] Timing tested (fits in 8 min demo slot)
1168
+
1169
+ ---
1170
+
1171
+ # Open Questions
1172
+
1173
+ 1. **Should primitives also be definable in files?**
1174
+ - Like `primitives/custom_search.rb`
1175
+ - Environment loads them on startup
1176
+ - More dangerous but more flexible
1177
+
1178
+ 2. **How to handle capability loops?**
1179
+ - A calls B calls A calls B...
1180
+ - Depth limit? Token budget? Both?
1181
+
1182
+ 3. **Session persistence?**
1183
+ - Currently POs reset each run
1184
+ - Should conversation history persist?
1185
+ - Should created POs persist? (currently yes, written to disk)
1186
+
1187
+ 4. **MCP: when to use vs native?**
1188
+ - If there's an MCP server with `read_file`, use it or our native?
1189
+ - Probably: prefer native, MCP for things we don't have
1190
+
1191
+ 5. **Stigmergy for v2?**
1192
+ - Environment-level shared state
1193
+ - `mark(key, value)` and `sense(pattern)`
1194
+ - Could enable emergent coordination
1195
+ - Too much for MVP, but worth hinting at
1196
+
1197
+ ---
1198
+
1199
+ # Success Criteria
1200
+
1201
+ The demo succeeds if:
1202
+
1203
+ 1. **PO = Tool equivalence is visceral**
1204
+ - Audience sees primitives and POs in the same capability bar
1205
+ - Message log shows both being "called" the same way
1206
+ - The difference (interpretation complexity) is obvious
1207
+
1208
+ 2. **Semantic binding is VISIBLE**
1209
+ - Message log shows natural language → capability call
1210
+ - "what's in here?" becomes `list_files`
1211
+ - The audience can SEE the interpretation happening
1212
+
1213
+ 3. **Multi-PO interaction feels like message passing**
1214
+ - Coordinator talks to reader like sending a message
1215
+ - Reader's internal work is visible in the log
1216
+ - Kay's vision is tangible
1217
+
1218
+ 4. **Self-modification is both magical and understandable**
1219
+ - New PO appears in capability bar
1220
+ - The creation is logged
1221
+ - Human approved it (ask_human)
1222
+ - Scary but controlled
1223
+
1224
+ 5. **Human stays in the loop**
1225
+ - Confirmation prompts feel natural
1226
+ - POs ask for help when stuck
1227
+ - Control is negotiated, not hardcoded
1228
+
1229
+ 6. **The UI is beautiful**
1230
+ - Charm styling makes it polished
1231
+ - Not a janky demo
1232
+ - People want to use this