letmeknow-cli 0.2.0 → 0.3.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 (3) hide show
  1. package/README.md +91 -32
  2. package/SKILL.md +83 -39
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # LetMeKnow
2
2
 
3
- LetMeKnow gives an agent a temporary, interactive web surface for a human. The agent sends semantic HTML, receives normalized user actions, and responds with HTML fragments. A constrained HTMX-like runtime handles forms, targets, swaps, pending state, and duplicate submissions in the browser.
3
+ LetMeKnow gives an agent a temporary HTML and CSS workspace for a human. The agent renders a page, receives normalized form and button actions, and responds with HTML fragments. The browser runtime handles validation, form serialization, pending state, and targeted updates.
4
4
 
5
- It is not a localhost proxy, static file server, or arbitrary HTTP tunnel.
5
+ It is not a localhost proxy or a programmable frontend. Agents provide presentation and semantic actions, not JavaScript or HTTP handlers.
6
6
 
7
7
  ## CLI
8
8
 
@@ -20,7 +20,7 @@ LETMEKNOW_URL=http://localhost:8787 npx letmeknow-cli
20
20
 
21
21
  stdin contains one compact JSON command per line. stdout contains one JSON event per line. Diagnostics go to stderr.
22
22
 
23
- Print the agent-facing instructions without opening a connection:
23
+ Print the agent instructions without connecting:
24
24
 
25
25
  ```bash
26
26
  npx letmeknow-cli --skill
@@ -40,77 +40,136 @@ The CLI emits its temporary URL:
40
40
  {"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
41
41
  ```
42
42
 
43
- Render the interface:
43
+ The URL immediately serves a styled shell with a waiting message. Send the initial HTML and optional CSS with `render`; a browser that is already open receives it without polling:
44
44
 
45
45
  ```json
46
- {"type":"render","id":"render-1","body":"<h1>Approve expense?</h1><dl><dt>Amount</dt><dd>$420</dd></dl><form id=\"decision\" hx-post=\"decide\"><label>Comment<textarea name=\"comment\" required></textarea></label><button hx-post=\"approve\" name=\"decision\" value=\"approve\">Approve</button><button hx-post=\"reject\" name=\"decision\" value=\"reject\">Reject</button></form>"}
46
+ {"type":"render","id":"render-1","body":"<h1>Search invoices</h1><form id=\"search\" action=\"search\" method=\"post\" data-lmk-target=\"results\"><label>Customer<input name=\"customer\" required></label><button>Search</button></form><section id=\"results\"><p>Enter a customer.</p></section>","css":"#results { margin-top: 2rem; }"}
47
47
  ```
48
48
 
49
- A successful render is assigned a revision:
49
+ A successful render receives a revision:
50
50
 
51
51
  ```json
52
52
  {"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
53
53
  ```
54
54
 
55
- When the human clicks Approve, the CLI emits one normalized action:
55
+ Submitting the form produces one normalized action:
56
56
 
57
57
  ```json
58
- {"type":"action","id":"event-1","client_id":"8c05cc18-f503-4d1b-aad9-acde3a41c983","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","method":"POST","action_id":"approve","form_id":"decision","target":"#lmk-view","swap":"innerHTML","trigger":{"id":null,"name":"decision","value":"approve"},"values":{"comment":"Looks good","decision":"approve"}}
58
+ {"type":"action","id":"event-1","client_id":"8c05cc18-f503-4d1b-aad9-acde3a41c983","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","action_id":"search","form_id":"search","target_id":"results","trigger":{"id":null,"name":null,"value":null},"values":{"customer":"Acme Ltd"}}
59
59
  ```
60
60
 
61
- Respond with an HTML fragment. The default target is the whole view, so this replaces its contents:
61
+ Respond with HTML for the target's contents:
62
62
 
63
63
  ```json
64
- {"type":"response","id":"response-1","request_id":"event-1","body":"<h1>Approved</h1><p>The expense was approved.</p>"}
64
+ {"type":"response","id":"response-1","request_id":"event-1","body":"<table><tr><th>Invoice</th><th>Amount</th></tr><tr><td>INV-42</td><td>$800</td></tr></table>"}
65
65
  ```
66
66
 
67
- The response receives a new render revision in its acknowledgement.
67
+ The runtime assigns a new render revision and places the fragment inside `#results`. If `data-lmk-target` had been omitted, it would replace the contents of the whole workspace.
68
68
 
69
- ## HTML interaction profile
69
+ ## HTML actions
70
70
 
71
- LetMeKnow renders standard semantic HTML with a built-in stylesheet. It recognizes only four HTMX-like attributes:
71
+ ### Forms
72
72
 
73
- - `hx-get="action"`
74
- - `hx-post="action"`
75
- - `hx-target="#element-id"`
76
- - `hx-swap="innerHTML|outerHTML"`
73
+ Interactive forms use standard HTML:
77
74
 
78
- Action names contain letters, digits, `.`, `_`, `:`, or `-`. They are identifiers, not URLs. The default target is `#lmk-view` and the default swap is `innerHTML`.
75
+ ```html
76
+ <form id="decision" action="decide" method="post">
77
+ <label>
78
+ Comment
79
+ <textarea name="comment"></textarea>
80
+ </label>
81
+ <button name="decision" value="approve" formaction="approve">Approve</button>
82
+ <button name="decision" value="reject" formaction="reject">Reject</button>
83
+ </form>
84
+ ```
85
+
86
+ Requirements:
87
+
88
+ - `method="post"`
89
+ - A stable form `id`
90
+ - A relative action identifier such as `search`, `approve`, or `invoice:inspect`
91
+ - Meaningful control `name` values
92
+
93
+ A submit button's standard `formaction` overrides the form's `action`. Native `required`, input types, ranges, and patterns validate locally. `FormData(form, submitter)` is captured before the form is disabled, so selected controls and the clicked submit button are included. Repeated names become string arrays.
94
+
95
+ ### Standalone actions
96
+
97
+ A button can invoke an action without a form:
98
+
99
+ ```html
100
+ <button type="button" data-lmk-action="refresh-status" data-lmk-target="status">
101
+ Refresh
102
+ </button>
103
+ ```
104
+
105
+ Its event has `form_id: null`, empty `values`, and the button's optional `id`, `name`, and `value` in `trigger`.
106
+
107
+ ### Targeted updates
108
+
109
+ `data-lmk-target` accepts one element ID without `#`:
110
+
111
+ ```html
112
+ <button data-lmk-action="load-more" data-lmk-target="results">Load more</button>
113
+ <section id="results"></section>
114
+ ```
115
+
116
+ All updates use `innerHTML`. There are no alternate swap modes or arbitrary selector targets. The default target is the runtime-owned `lmk-view`, which means the whole workspace.
79
117
 
80
- Use ordinary HTML forms and named controls. On submission, LetMeKnow sends the complete form values and identifies the exact submit button through `trigger`. Repeated field names become string arrays. File values are not currently sent.
118
+ Typing, focusing, expanding `<details>`, native validation, scrolling, and other local browser behavior produce no agent events.
81
119
 
82
- The browser handles typing, focus, native validation, and selection locally. These do not produce protocol traffic. Only a completed `hx-get` or `hx-post` interaction reaches the agent.
120
+ ## CSS
83
121
 
84
- One interaction per browser client and form may be pending at a time. The runtime disables that form while waiting, and the service rejects duplicate submissions. Different forms and browser tabs remain independent. Every action includes the browser-tab `client_id` and the `render_id` that produced it.
122
+ `render` accepts page-level CSS in its `css` field. A `response` may include `css` to replace the current page CSS; omitting it preserves the current CSS.
85
123
 
86
- Scripts, inline handlers, external styles, frames, external form submissions, and arbitrary browser connections are blocked by the page's Content Security Policy. Other HTMX attributes and extensions have no effect.
124
+ Normal modern CSS is supported, including grid, flexbox, media queries, variables, transitions, and print styles. External stylesheets, `@import`, scripts, inline event handlers, and inline `style` attributes are blocked. CSS and images may reference session assets with relative URLs.
125
+
126
+ ## Assets
127
+
128
+ `put` stores or replaces passive resources only under `/assets/`:
129
+
130
+ ```json
131
+ {"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
132
+ ```
133
+
134
+ Reference them relatively so local path-based sessions also work:
135
+
136
+ ```html
137
+ <img src="assets/logo.png" alt="Company logo">
138
+ ```
139
+
140
+ Assets answer only `GET` and `HEAD`. Each body is limited to 1 MiB. A session accepts at most 100 assets and 10 MiB of decoded asset data. Assets are removed with the session; there is no `delete` command.
87
141
 
88
142
  ## Protocol
89
143
 
90
144
  ### Commands
91
145
 
92
146
  - `open`: create the session; it must be first.
93
- - `render`: replace the HTML shown on a fresh visit or refresh.
94
- - `response`: answer one pending action with an HTML fragment.
95
- - `close`: immediately destroy the session.
96
-
97
- Commands accept an optional string `id`. Successful `render`, `response`, and `close` commands emit a correlated `ack`. `render` and `response` acknowledgements also contain the assigned `render_id`.
147
+ - `render`: set the initial/current HTML and CSS for fresh clients.
148
+ - `put`: store an asset under `/assets/`.
149
+ - `response`: answer one pending action with HTML and optional CSS.
150
+ - `close`: destroy the session immediately.
98
151
 
99
- HTML bodies are UTF-8 strings limited to 1 MiB. Up to 32 interactions may be pending across independent clients and forms. Action request bodies have a 30-second read deadline, and an agent response may take up to five minutes.
152
+ Commands accept an optional string `id`. Successful commands emit correlated `ack` events. `render` and `response` acknowledgements also include the generated `render_id`.
100
153
 
101
154
  ### Events
102
155
 
103
156
  - `session`: the public URL and disconnect grace period.
104
- - `action`: one normalized user interaction.
157
+ - `action`: a normalized form or standalone-button action.
105
158
  - `ack`: command completion.
106
159
  - `error`: invalid command or protocol state.
107
160
  - `closing`: explicit session destruction.
108
161
 
109
- ## Lifecycle
162
+ One interaction per browser client, render, form, and target may be pending at a time. Conflicting submissions are rejected, while independent forms and targets can proceed concurrently. Match responses by action ID rather than event order.
163
+
164
+ HTML, CSS, asset, and action bodies are each limited to 1 MiB. Up to 32 independent actions may be pending. Action bodies have a 30-second read deadline, and an agent response may take up to five minutes.
165
+
166
+ ## Security and lifecycle
167
+
168
+ HTML fragments are sanitized. Scripts, style elements, inline event handlers, HTMX attributes, frames, active metadata, external form actions, and invalid LetMeKnow attributes are removed. A strict Content Security Policy blocks arbitrary browser connections and external resources. Agent CSS is installed separately in a nonce-protected style element.
110
169
 
111
- The active CLI connection owns the session. It receives a private reconnect credential and automatically reconnects after an unexpected disconnect. The credential never appears on protocol stdout.
170
+ The URL is a bearer secret. Share it only with the intended human.
112
171
 
113
- The rendered interface remains visible during the ten-minute disconnect grace period, but interactions return `503`. Browser traffic does not extend the grace period. `close` destroys everything immediately.
172
+ The CLI owns the session and automatically reconnects after an unexpected disconnect. The shell, current render, and assets remain available during the ten-minute disconnect grace period, but actions fail. Browser traffic does not extend the grace period. `close` destroys everything immediately.
114
173
 
115
174
  ## Development
116
175
 
package/SKILL.md CHANGED
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: letmeknow
3
- description: Show a human a temporary semantic HTML interface and handle normalized form actions through the LetMeKnow NDJSON CLI.
3
+ description: Show a human a temporary HTML and CSS workspace and handle normalized form or button actions through the LetMeKnow NDJSON CLI.
4
4
  ---
5
5
 
6
6
  # LetMeKnow
7
7
 
8
- Use LetMeKnow when a human needs a temporary rich interface, form, choice, approval, table, or status view. The agent sends HTML and receives only completed semantic actions; typing and other local browser state do not create events.
8
+ Use LetMeKnow when a human needs a temporary rich document, dashboard, report, preview, form, approval, quiz, table, or status view. The agent supplies semantic HTML and CSS. The browser sends only declared actions; typing and other local state do not create events.
9
9
 
10
- LetMeKnow is not a localhost proxy, file server, or arbitrary HTTP tunnel. Do not send JavaScript, CSS, raw HTTP handlers, or application code.
10
+ LetMeKnow is not a localhost proxy, persistent application, or arbitrary JavaScript environment.
11
11
 
12
12
  ## Start
13
13
 
@@ -31,84 +31,128 @@ Wait for the session URL:
31
31
  {"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
32
32
  ```
33
33
 
34
- ## Render semantic HTML
34
+ The URL immediately serves a shell with a waiting message. It is safe for the human to open it before the first render.
35
35
 
36
- Send an HTML fragment with `render`:
36
+ ## Render HTML and CSS
37
+
38
+ Send an HTML fragment and optional page CSS:
37
39
 
38
40
  ```json
39
- {"type":"render","id":"render-1","body":"<h1>Approve expense?</h1><dl><dt>Amount</dt><dd>$420</dd></dl><form id=\"decision\" hx-post=\"decide\"><label>Comment<textarea name=\"comment\" required></textarea></label><button hx-post=\"approve\" name=\"decision\" value=\"approve\">Approve</button><button hx-post=\"reject\" name=\"decision\" value=\"reject\">Reject</button></form>"}
41
+ {"type":"render","id":"render-1","body":"<h1>Search invoices</h1><form id=\"search\" action=\"search\" method=\"post\" data-lmk-target=\"results\"><label>Customer<input name=\"customer\" required></label><button>Search</button></form><section id=\"results\"><p>Enter a customer.</p></section>","css":"#results { margin-top: 2rem; }"}
40
42
  ```
41
43
 
42
- Wait for its acknowledgement before directing the human to the URL:
44
+ A browser already waiting at the URL receives the render. Wait for the acknowledgement:
43
45
 
44
46
  ```json
45
47
  {"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
46
48
  ```
47
49
 
48
- Use ordinary semantic HTML: headings, paragraphs, lists, tables, `dl`, forms, labels, inputs, buttons, progress, and images. A built-in stylesheet makes these presentable. Do not include `<html>`, `<head>`, `<body>`, `<main id="lmk-view">`, scripts, inline event handlers, stylesheets, iframes, or external forms.
50
+ Use semantic HTML such as headings, sections, paragraphs, lists, tables, `dl`, forms, labels, controls, buttons, `details`, progress, and images. Do not include `<html>`, `<head>`, `<body>`, `<main id="lmk-view">`, scripts, style elements, inline event handlers, inline `style`, iframes, or HTMX attributes.
51
+
52
+ The built-in stylesheet makes unstyled HTML usable. The optional `css` field supports normal modern CSS, including grid, flexbox, media queries, variables, transitions, and print styles. External stylesheets, `@import`, and remote resources do not work. Use relative asset URLs.
53
+
54
+ ## Forms
49
55
 
50
- HTML bodies are limited to 1 MiB.
56
+ Interactive forms use standard HTML:
51
57
 
52
- ## Declare interactions
58
+ ```html
59
+ <form id="decision" action="decide" method="post">
60
+ <label>Reason<textarea name="reason" required></textarea></label>
61
+ <button name="decision" value="approve" formaction="approve">Approve</button>
62
+ <button name="decision" value="reject" formaction="reject">Reject</button>
63
+ </form>
64
+ ```
53
65
 
54
- Only these HTMX-like attributes are supported:
66
+ Rules:
55
67
 
56
- - `hx-get="action"`
57
- - `hx-post="action"`
58
- - `hx-target="#element-id"`
59
- - `hx-swap="innerHTML"` or `hx-swap="outerHTML"`
68
+ - Use `method="post"`.
69
+ - Give every interactive form a stable, unique `id`.
70
+ - Give controls meaningful `name` values.
71
+ - Use a relative action identifier containing letters, digits, `.`, `_`, `:`, or `-`.
72
+ - A submit button's standard `formaction` may override the form action.
73
+ - Native `required`, input types, ranges, and patterns validate locally.
60
74
 
61
- Action values are identifiers, not URLs. Use letters, digits, `.`, `_`, `:`, and `-`. Other HTMX attributes and extensions do nothing.
75
+ The runtime captures `FormData(form, submitter)` before disabling the form. Selected radio buttons, checked boxes, ordinary controls, and the clicked submit button are therefore included. Repeated names become string arrays. File values are not currently sent.
62
76
 
63
- The default target is `#lmk-view`, the runtime-owned whole-view container. The default swap is `innerHTML`. Usually omit both and respond with the complete next interface fragment.
77
+ ## Standalone actions
64
78
 
65
- Put `hx-get` or `hx-post` on a form, button, or link. A submit button's attribute overrides its form's attribute, allowing distinct actions:
79
+ Use a button when an action does not need a form:
66
80
 
67
81
  ```html
68
- <form id="decision" hx-post="decide">
69
- <label>Reason<textarea name="reason" required></textarea></label>
70
- <button hx-post="approve" name="decision" value="approve">Approve</button>
71
- <button hx-post="reject" name="decision" value="reject">Reject</button>
82
+ <button type="button" data-lmk-action="refresh-status" data-lmk-target="status">
83
+ Refresh
84
+ </button>
85
+ ```
86
+
87
+ This is appropriate for refresh, retry, cancel, generate, inspect, load-more, and export actions. The event has `form_id: null`, empty `values`, and the button's optional `id`, `name`, and `value` in `trigger`.
88
+
89
+ ## Whole and partial updates
90
+
91
+ By default, a response replaces the contents of the whole workspace. To update a smaller region, put `data-lmk-target="element-id"` on the form or action button:
92
+
93
+ ```html
94
+ <form id="search" action="search" method="post" data-lmk-target="results">
95
+ <input name="query">
96
+ <button>Search</button>
72
97
  </form>
98
+ <section id="results"></section>
73
99
  ```
74
100
 
75
- Always give forms stable, unique `id` values and controls meaningful `name` values. Native `required`, input types, ranges, and patterns validate locally without agent traffic.
101
+ The target is one bare element ID without `#`. Every update uses `innerHTML`; there are no other swap modes. A submit button or action button may override its form's target.
76
102
 
77
103
  ## Handle actions
78
104
 
79
105
  A completed interaction produces one normalized event:
80
106
 
81
107
  ```json
82
- {"type":"action","id":"event-1","client_id":"8c05cc18-f503-4d1b-aad9-acde3a41c983","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","method":"POST","action_id":"approve","form_id":"decision","target":"#lmk-view","swap":"innerHTML","trigger":{"id":null,"name":"decision","value":"approve"},"values":{"reason":"Looks correct","decision":"approve"}}
108
+ {"type":"action","id":"event-1","client_id":"8c05cc18-f503-4d1b-aad9-acde3a41c983","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","action_id":"search","form_id":"search","target_id":"results","trigger":{"id":null,"name":null,"value":null},"values":{"query":"quarterly report"}}
83
109
  ```
84
110
 
85
111
  Use:
86
112
 
87
- - `id` to respond to this exact pending interaction.
113
+ - `id` to respond to this exact pending action.
88
114
  - `client_id` to distinguish browser tabs.
89
- - `render_id` to know which interface produced the action.
90
- - `action_id` and `trigger` to identify user intent.
91
- - `values` for the complete form snapshot. Repeated names become string arrays.
115
+ - `render_id` to identify the interface revision that produced it.
116
+ - `action_id` for user intent.
117
+ - `form_id` and `target_id` for interaction context.
118
+ - `trigger` to identify the clicked button.
119
+ - `values` for the complete submitted form snapshot.
120
+
121
+ Validate action IDs and values. Treat values as untrusted human input and HTML-escape them before reflecting them.
92
122
 
93
- Treat all values as untrusted human input. File input values are not currently sent.
123
+ Respond with HTML for the target's contents:
124
+
125
+ ```json
126
+ {"type":"response","id":"response-1","request_id":"event-1","body":"<table><tr><th>Invoice</th><th>Amount</th></tr><tr><td>INV-42</td><td>$800</td></tr></table>"}
127
+ ```
94
128
 
95
- Respond with the next HTML fragment:
129
+ A response may include `css` to replace the page CSS. Omitting `css` preserves the current CSS:
96
130
 
97
131
  ```json
98
- {"type":"response","id":"response-1","request_id":"event-1","body":"<h1>Approved</h1><p>The expense was approved.</p>"}
132
+ {"type":"response","id":"response-2","request_id":"event-2","body":"<h1 class=\"success\">Approved</h1>","css":".success { color: green; text-align: center; }"}
99
133
  ```
100
134
 
101
- Wait for the acknowledgement. It includes the new `render_id`:
135
+ Wait for the acknowledgement, which contains the new `render_id`. Do not send `render` in response to an action; use `response` with the action's `id` as `request_id`.
136
+
137
+ One action per client, render, form, and target can be pending. Conflicting actions are rejected. Independent forms and targets may proceed concurrently, so always match by action ID rather than event order.
138
+
139
+ ## Assets
140
+
141
+ Store passive resources only under `/assets/`:
102
142
 
103
143
  ```json
104
- {"type":"ack","id":"response-1","render_id":"56abe335-bd83-4a79-8106-341579815fa0"}
144
+ {"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
105
145
  ```
106
146
 
107
- Do not send a separate `render` in response to an action. `response` both resolves the pending browser request and updates that browser's interface. Use `render` only to replace the interface served on a fresh visit or refresh.
147
+ Reference them relatively:
148
+
149
+ ```html
150
+ <img src="assets/logo.png" alt="Company logo">
151
+ ```
108
152
 
109
- One action per client and form can be pending. The browser disables that form while waiting, and duplicate submissions are rejected. Different forms and clients can have independent pending actions. Respond using request IDs rather than assuming event order.
153
+ `encoding` is `utf8` by default or `base64` for binary data. Assets answer only `GET` and `HEAD`. There is no `delete`; replacement uses another `put`, and `close` removes all assets.
110
154
 
111
- ## Close
155
+ ## Close and limits
112
156
 
113
157
  Destroy the session when finished:
114
158
 
@@ -116,8 +160,8 @@ Destroy the session when finished:
116
160
  {"type":"close","id":"close-1"}
117
161
  ```
118
162
 
119
- Wait for `ack` and `closing`. Closing stdin only disconnects the producer and does not replace explicit `close`.
163
+ Wait for `ack` and `closing`. Closing stdin only disconnects the producer.
120
164
 
121
- The CLI automatically reconnects during a ten-minute disconnect grace period. The interface remains readable while disconnected, but interactions fail. Never expose the private reconnect credential. Treat the public session URL as a bearer secret and share it only with the intended human.
165
+ HTML, CSS, asset, and action bodies are each limited to 1 MiB. A session accepts 100 assets, 10 MiB decoded asset data, and 32 pending actions. Action bodies have a 30-second read deadline; the agent has five minutes to respond.
122
166
 
123
- Limits: 1 MiB per HTML or action body, 32 pending interactions, 30 seconds to read an action body, and five minutes for the agent to respond.
167
+ The CLI reconnects during a ten-minute disconnect grace period. The shell, current render, and assets remain readable while disconnected, but actions fail. Treat the URL as a bearer secret. Never expose the private reconnect credential.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letmeknow-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A temporary interactive web surface for agents.",
5
5
  "files": [
6
6
  "bin",