@paneui/cli 0.0.16 → 0.0.18

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.
@@ -198,7 +198,7 @@ Options:
198
198
  the shell band above the iframe — "who is asking, why".
199
199
  Max 280 chars after trim; a single \\n is allowed for a
200
200
  two-line message; other control chars are rejected. Pass
201
- this whenever the artifact itself doesn't make the
201
+ this whenever the pane itself doesn't make the
202
202
  request self-explanatory.
203
203
  --input-data <v> This instance's seed data — a JSON object (file path or
204
204
  inline JSON), validated by the relay against the template
@@ -34,7 +34,7 @@ export const demoHelp = `pane demo — take the 60-second guided tour
34
34
  Usage:
35
35
  pane demo [options]
36
36
 
37
- Creates a short-lived pane with the built-in tutorial artifact, opens its URL
37
+ Creates a short-lived pane from the built-in tutorial template, opens its URL
38
38
  in your browser (or prints it if none is available), then runs a tiny agent
39
39
  loop right here in your terminal that watches the session and reacts to each
40
40
  thing you do in the pane. Every event you trigger is echoed to this terminal
@@ -27,6 +27,11 @@ Verbs:
27
27
  --data <path|json> [--if-match <version>]
28
28
  delete <pane-id> <collection> <record-key>
29
29
  [--if-match <version>] [--yes]
30
+ delete-collection <pane-id> <collection>
31
+ [--yes]
32
+ Drop a WHOLE collection — all its records + the collection row.
33
+ Owner-only. Collection names are immutable (no rename): to "rename",
34
+ delete the old collection and write under the new name.
30
35
  watch <pane-id>
31
36
  [--collection <name>]... [--since-seq <name>=<n>]...
32
37
 
@@ -43,7 +48,7 @@ export async function runRecords(args) {
43
48
  return;
44
49
  }
45
50
  if (verb === undefined) {
46
- fail("missing verb — pane records <list|get|upsert|update|delete|watch>", "invalid_args");
51
+ fail("missing verb — pane records <list|get|upsert|update|delete|delete-collection|watch>", "invalid_args");
47
52
  }
48
53
  const sub = {
49
54
  positionals: args.positionals.slice(1),
@@ -64,10 +69,12 @@ export async function runRecords(args) {
64
69
  return runUpdate(sub);
65
70
  case "delete":
66
71
  return runDelete(sub);
72
+ case "delete-collection":
73
+ return runDeleteCollection(sub);
67
74
  case "watch":
68
75
  return runWatch(sub);
69
76
  default:
70
- fail(`unknown verb '${verb}' — pane records <list|get|upsert|update|delete|watch>`, "invalid_args");
77
+ fail(`unknown verb '${verb}' — pane records <list|get|upsert|update|delete|delete-collection|watch>`, "invalid_args");
71
78
  }
72
79
  }
73
80
  // ---------------------------------------------------------------------------
@@ -199,6 +206,24 @@ async function runDelete(args) {
199
206
  }
200
207
  }
201
208
  // ---------------------------------------------------------------------------
209
+ // delete-collection — drop a whole collection (all rows + the collection row)
210
+ // ---------------------------------------------------------------------------
211
+ async function runDeleteCollection(args) {
212
+ assertKnownFlags(args, ["url", "api-key"], ["yes", "help"], "pane records delete-collection");
213
+ const [paneId, collection] = args.positionals;
214
+ if (!paneId || !collection) {
215
+ fail("usage: pane records delete-collection <pane-id> <collection>", "invalid_args");
216
+ }
217
+ const client = makeClient(args);
218
+ try {
219
+ await client.deleteRecordCollection(paneId, collection);
220
+ printJson({ deleted: true, collection });
221
+ }
222
+ catch (e) {
223
+ failFromError(e);
224
+ }
225
+ }
226
+ // ---------------------------------------------------------------------------
202
227
  // watch — stream record deltas as JSON-lines
203
228
  // ---------------------------------------------------------------------------
204
229
  async function runWatch(args) {
@@ -29,6 +29,11 @@ Verbs:
29
29
  --data <path|json> [--if-match <version>]
30
30
  delete <template-id|slug> <collection> <record-key>
31
31
  [--if-match <version>] [--yes]
32
+ delete-collection <template-id|slug> <collection>
33
+ [--yes]
34
+ Drop a WHOLE collection — all its records + the collection row.
35
+ Owner-only. Collection names are immutable (no rename): to "rename",
36
+ delete the old collection and write under the new name.
32
37
 
33
38
  Output (stdout): single JSON object per command.
34
39
  Errors on stderr: {"error":{"code","message"}} with non-zero exit.`;
@@ -39,7 +44,7 @@ export async function runTemplateRecords(args) {
39
44
  return;
40
45
  }
41
46
  if (verb === undefined) {
42
- fail("missing verb — pane template-records <list|get|upsert|update|delete>", "invalid_args");
47
+ fail("missing verb — pane template-records <list|get|upsert|update|delete|delete-collection>", "invalid_args");
43
48
  }
44
49
  const sub = {
45
50
  positionals: args.positionals.slice(1),
@@ -60,8 +65,10 @@ export async function runTemplateRecords(args) {
60
65
  return runUpdate(sub);
61
66
  case "delete":
62
67
  return runDelete(sub);
68
+ case "delete-collection":
69
+ return runDeleteCollection(sub);
63
70
  default:
64
- fail(`unknown verb '${verb}' — pane template-records <list|get|upsert|update|delete>`, "invalid_args");
71
+ fail(`unknown verb '${verb}' — pane template-records <list|get|upsert|update|delete|delete-collection>`, "invalid_args");
65
72
  }
66
73
  }
67
74
  async function runList(args) {
@@ -177,6 +184,21 @@ async function runDelete(args) {
177
184
  failFromError(e);
178
185
  }
179
186
  }
187
+ async function runDeleteCollection(args) {
188
+ assertKnownFlags(args, ["url", "api-key"], ["yes", "help"], "pane template-records delete-collection");
189
+ const [templateId, collection] = args.positionals;
190
+ if (!templateId || !collection) {
191
+ fail("usage: pane template-records delete-collection <template-id|slug> <collection>", "invalid_args");
192
+ }
193
+ const client = makeClient(args);
194
+ try {
195
+ await client.deleteTemplateRecordCollection(templateId, collection);
196
+ printJson({ deleted: true, collection });
197
+ }
198
+ catch (e) {
199
+ failFromError(e);
200
+ }
201
+ }
180
202
  function parseIntFlag(args, name, defaultValue, bounds = {}) {
181
203
  const raw = args.flags.get(name);
182
204
  if (raw === undefined)
@@ -24,6 +24,7 @@ const CREATE_FLAGS = [
24
24
  "event-schema",
25
25
  "input-schema",
26
26
  "record-schema",
27
+ "template-record-schema",
27
28
  "icon-emoji",
28
29
  ];
29
30
  const SET_ICON_FLAGS = ["template-id", "emoji", "image"];
@@ -34,6 +35,7 @@ const VERSION_FLAGS = [
34
35
  "event-schema",
35
36
  "input-schema",
36
37
  "record-schema",
38
+ "template-record-schema",
37
39
  ];
38
40
  const UPDATE_FLAGS = ["name", "slug", "description", "tags"];
39
41
  const NO_FLAGS = [];
@@ -76,8 +78,9 @@ Subcommands:
76
78
  [--event-schema <path|json>] [--slug <s>]
77
79
  [--description <d>] [--tags <t1,t2>]
78
80
  [--input-schema <path|json>]
79
- [--record-schema <path|json>] [--template-type <t>]
80
- [--icon-emoji <emoji>]
81
+ [--record-schema <path|json>]
82
+ [--template-record-schema <path|json>]
83
+ [--template-type <t>] [--icon-emoji <emoji>]
81
84
  Creates a named template. Prints { template_id, slug, version }.
82
85
  --icon-emoji sets a single-emoji icon at create time; use 'set-icon'
83
86
  with --image to attach an uploaded image icon afterwards.
@@ -85,7 +88,9 @@ Subcommands:
85
88
  pane template version <id|slug> --template <path|inline>
86
89
  [--event-schema <path|json>]
87
90
  [--input-schema <path|json>]
88
- [--record-schema <path|json>] [--template-type <t>]
91
+ [--record-schema <path|json>]
92
+ [--template-record-schema <path|json>]
93
+ [--template-type <t>]
89
94
  Appends a new immutable version. Prints { template_id, version }.
90
95
 
91
96
  pane template update <id|slug> [--name <n>] [--slug <s>]
@@ -217,6 +222,17 @@ Options:
217
222
  The relay validates the document and rejects anything
218
223
  malformed at create time. See 'pane skill show' (the
219
224
  "Records" section) for the full grammar.
225
+ --template-record-schema <v>
226
+ JSON Schema 2020-12 document declaring this template's
227
+ TEMPLATE-level record collections — a file path, or
228
+ inline JSON. Optional; omit for a template with no
229
+ template-level collections. Same "x-pane-collections"
230
+ grammar as --record-schema, but the collections it
231
+ declares are shared across every pane of the template
232
+ (one collection per template) instead of per-pane.
233
+ Without it, 'pane template-records upsert' is rejected
234
+ with "template declares no template-level record
235
+ collections" — set this first to bootstrap the feature.
220
236
  --icon-emoji <e> Single-emoji icon for the template (create only).
221
237
  --template-type <t> "html-inline" (default) or "html-ref".
222
238
  --url <url> Relay base URL (overrides PANE_URL).
@@ -300,6 +316,32 @@ function resolveRecordSchema(args) {
300
316
  fail(e instanceof Error ? e.message : String(e), "invalid_args");
301
317
  }
302
318
  }
319
+ /**
320
+ * Resolve the optional --template-record-schema — file path or inline JSON.
321
+ * Must be an object (a JSON Schema 2020-12 document with an
322
+ * `x-pane-collections` extension), same grammar as --record-schema, but
323
+ * declares TEMPLATE-level (shared across all panes of the template) record
324
+ * collections rather than per-pane ones. Absent → returns `undefined` so the
325
+ * caller omits `template_record_schema` from the request entirely (template
326
+ * has no template-level record collections). The relay runs the full shape +
327
+ * collection validation; this helper only does the obvious "is it an object"
328
+ * pre-check so a typo surfaces locally.
329
+ */
330
+ function resolveTemplateRecordSchema(args) {
331
+ const raw = args.flags.get("template-record-schema");
332
+ if (raw === undefined)
333
+ return undefined;
334
+ try {
335
+ const v = resolveJson(raw, "--template-record-schema");
336
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
337
+ fail("--template-record-schema must be a JSON object", "invalid_args");
338
+ }
339
+ return v;
340
+ }
341
+ catch (e) {
342
+ fail(e instanceof Error ? e.message : String(e), "invalid_args");
343
+ }
344
+ }
303
345
  /** Parse a comma-separated --tags flag into a string array. */
304
346
  function resolveTags(args) {
305
347
  const raw = args.flags.get("tags");
@@ -321,6 +363,7 @@ async function runTemplateCreate(args) {
321
363
  const eventSchema = resolveEventSchema(args);
322
364
  const inputSchema = resolveInputSchema(args);
323
365
  const recordSchema = resolveRecordSchema(args);
366
+ const templateRecordSchema = resolveTemplateRecordSchema(args);
324
367
  const tags = resolveTags(args);
325
368
  const slug = args.flags.get("slug");
326
369
  const description = args.flags.get("description");
@@ -346,6 +389,11 @@ async function runTemplateCreate(args) {
346
389
  // `undefined` would still add the key.
347
390
  if (recordSchema !== undefined)
348
391
  candidate["record_schema"] = recordSchema;
392
+ // template_record_schema is OMITTED entirely when --template-record-schema is
393
+ // absent — a template with no template-level record collections. Setting it
394
+ // to `undefined` would still add the key.
395
+ if (templateRecordSchema !== undefined)
396
+ candidate["template_record_schema"] = templateRecordSchema;
349
397
  const iconEmoji = args.flags.get("icon-emoji");
350
398
  if (iconEmoji !== undefined)
351
399
  candidate["icon_emoji"] = iconEmoji;
@@ -380,6 +428,7 @@ async function runTemplateVersion(args) {
380
428
  const eventSchema = resolveEventSchema(args);
381
429
  const inputSchema = resolveInputSchema(args);
382
430
  const recordSchema = resolveRecordSchema(args);
431
+ const templateRecordSchema = resolveTemplateRecordSchema(args);
383
432
  const candidate = {
384
433
  source,
385
434
  type,
@@ -395,6 +444,12 @@ async function runTemplateVersion(args) {
395
444
  // serialise as null and read as "intentionally cleared" on the wire.
396
445
  if (recordSchema !== undefined)
397
446
  candidate["record_schema"] = recordSchema;
447
+ // template_record_schema is OMITTED entirely when --template-record-schema is
448
+ // absent — same rationale as record_schema above. Including the key as
449
+ // undefined would serialise as null and read as "intentionally cleared" on
450
+ // the wire.
451
+ if (templateRecordSchema !== undefined)
452
+ candidate["template_record_schema"] = templateRecordSchema;
398
453
  const parsed = createArtifactVersionSchema.safeParse(candidate);
399
454
  if (!parsed.success) {
400
455
  const issue = parsed.error.issues[0];
@@ -0,0 +1,194 @@
1
+ // `pane update <pane-id>` — in-place edit of instance-level pane fields (#502)
2
+ // via PATCH /v1/panes/:id. Mirrors the create flags for the editable subset
3
+ // (everything that is per-pane rather than per-template).
4
+ import { updatePaneSchema } from "@paneui/core";
5
+ import { assertKnownFlags } from "../argv.js";
6
+ import { makeClient } from "../config.js";
7
+ import { resolveJson } from "../input.js";
8
+ import { printJson, fail, failFromError } from "../output.js";
9
+ const KNOWN_FLAGS = [
10
+ "ttl",
11
+ "expires-at",
12
+ "input-data",
13
+ "title",
14
+ "preamble",
15
+ "metadata",
16
+ "tags",
17
+ "icon-emoji",
18
+ "icon-attachment-id",
19
+ ];
20
+ // `--clear-icon-emoji` / `--clear-icon-attachment-id` set the corresponding
21
+ // field to `null` on the wire (drops the per-pane override; the pane falls back
22
+ // to the template's icon).
23
+ const KNOWN_BOOLS = ["clear-icon-emoji", "clear-icon-attachment-id"];
24
+ const SCHEMA_PATH_TO_FLAG = {
25
+ ttl: "--ttl",
26
+ expires_at: "--expires-at",
27
+ input_data: "--input-data",
28
+ title: "--title",
29
+ preamble: "--preamble",
30
+ metadata: "--metadata",
31
+ tags: "--tags",
32
+ icon_emoji: "--icon-emoji",
33
+ icon_attachment_id: "--icon-attachment-id",
34
+ };
35
+ function schemaPathToFlag(path) {
36
+ const dotted = path.map(String).join(".");
37
+ for (let i = path.length; i > 0; i--) {
38
+ const prefix = path.slice(0, i).map(String).join(".");
39
+ const flag = SCHEMA_PATH_TO_FLAG[prefix];
40
+ if (flag !== undefined)
41
+ return flag;
42
+ }
43
+ return dotted;
44
+ }
45
+ export const updateHelp = `pane update — edit instance-level fields on a live pane
46
+
47
+ Usage:
48
+ pane update <pane-id> [options]
49
+
50
+ Edits a live pane in place (PATCH /v1/panes/:id). The pane keeps its id, URL,
51
+ event log, and template pin — only the per-instance fields listed below change.
52
+ Pass at least one flag; ttl and --expires-at are mutually exclusive.
53
+
54
+ The relay revalidates --input-data against the pane's current template
55
+ version's input_schema (the pane may have been upgraded since create), and
56
+ runs the same attachment-access checks as 'pane create'.
57
+
58
+ Lifecycle:
59
+ --ttl <seconds> Reset the pane's lifetime to now + <seconds>. Clamped
60
+ against the relay's MAX_TTL_SECONDS cap; exceeding it
61
+ is rejected (not silently truncated).
62
+ --expires-at <iso> Set expires_at to a specific ISO-8601 timestamp.
63
+ Must be in the future and within MAX_TTL_SECONDS
64
+ from now. Mutually exclusive with --ttl.
65
+
66
+ Display:
67
+ --title <text> New tab title. Same length/control-char rules as
68
+ create.
69
+ --preamble <text> New preamble (the context band above the iframe).
70
+ Max 280 chars after trim; one \\n permitted.
71
+ --icon-emoji <e> Set per-pane icon to a single emoji grapheme.
72
+ --icon-attachment-id <id>
73
+ Set per-pane icon to a ready raster-image
74
+ attachment (png/jpeg/webp/gif).
75
+ --clear-icon-emoji Clear the emoji override; fall back to the
76
+ template's icon.
77
+ --clear-icon-attachment-id
78
+ Clear the attachment override; fall back to the
79
+ template's icon.
80
+
81
+ Data:
82
+ --input-data <path|json> Replace the pane's input_data wholesale (JSON file
83
+ path or inline JSON). Revalidated against the pinned
84
+ template version's input_schema.
85
+ --metadata <path|json> Replace the pane's metadata wholesale.
86
+ --tags <t1,t2,...> Replace the per-pane tags (the relay merges them with
87
+ the template's tags, deduped). ≤20 tags, ≤50 chars
88
+ each; 'favorite' / 'favorites' are reserved.
89
+
90
+ Other:
91
+ --url <url> Relay base URL (overrides PANE_URL).
92
+ --api-key <key> Agent API key (overrides PANE_API_KEY).
93
+ -h, --help Show this help.
94
+
95
+ Output (stdout, JSON):
96
+ The full new pane state — same shape as 'pane show <id>' — plus an
97
+ \`updated_fields\` array naming which fields actually changed.`;
98
+ export async function runUpdate(args) {
99
+ assertKnownFlags(args, KNOWN_FLAGS, KNOWN_BOOLS, "pane update");
100
+ const paneId = args.positionals[0];
101
+ if (!paneId)
102
+ fail("missing <pane-id>", "invalid_args");
103
+ const candidate = {};
104
+ const ttlRaw = args.flags.get("ttl");
105
+ const expiresAtRaw = args.flags.get("expires-at");
106
+ if (ttlRaw !== undefined && expiresAtRaw !== undefined) {
107
+ fail("--ttl and --expires-at are mutually exclusive — pass one or the other", "invalid_args");
108
+ }
109
+ if (ttlRaw !== undefined) {
110
+ const ttl = Number(ttlRaw);
111
+ if (!Number.isFinite(ttl))
112
+ fail("--ttl must be a number", "invalid_args");
113
+ candidate["ttl"] = ttl;
114
+ }
115
+ if (expiresAtRaw !== undefined) {
116
+ candidate["expires_at"] = expiresAtRaw;
117
+ }
118
+ const titleRaw = args.flags.get("title");
119
+ if (titleRaw !== undefined)
120
+ candidate["title"] = titleRaw;
121
+ const preambleRaw = args.flags.get("preamble");
122
+ if (preambleRaw !== undefined)
123
+ candidate["preamble"] = preambleRaw;
124
+ const inputDataRaw = args.flags.get("input-data");
125
+ if (inputDataRaw !== undefined) {
126
+ try {
127
+ candidate["input_data"] = resolveJson(inputDataRaw, "--input-data");
128
+ }
129
+ catch (e) {
130
+ fail(e instanceof Error ? e.message : String(e), "invalid_args");
131
+ }
132
+ }
133
+ const metaRaw = args.flags.get("metadata");
134
+ if (metaRaw !== undefined) {
135
+ try {
136
+ candidate["metadata"] = resolveJson(metaRaw, "--metadata");
137
+ }
138
+ catch (e) {
139
+ fail(e instanceof Error ? e.message : String(e), "invalid_args");
140
+ }
141
+ }
142
+ const tagsRaw = args.flags.get("tags");
143
+ if (tagsRaw !== undefined) {
144
+ const tags = tagsRaw
145
+ .split(",")
146
+ .map((t) => t.trim())
147
+ .filter((t) => t !== "");
148
+ // Empty list explicitly = caller passed `--tags ""`; send [] so the relay
149
+ // wipes per-pane tags down to just the template inheritance.
150
+ candidate["tags"] = tags;
151
+ }
152
+ // Icon overrides. The set-flag and the clear-flag for the same field are
153
+ // contradictory — refuse rather than silently picking one.
154
+ const iconEmojiRaw = args.flags.get("icon-emoji");
155
+ const clearIconEmoji = args.bools.has("clear-icon-emoji");
156
+ if (iconEmojiRaw !== undefined && clearIconEmoji) {
157
+ fail("--icon-emoji and --clear-icon-emoji are mutually exclusive", "invalid_args");
158
+ }
159
+ if (iconEmojiRaw !== undefined)
160
+ candidate["icon_emoji"] = iconEmojiRaw;
161
+ if (clearIconEmoji)
162
+ candidate["icon_emoji"] = null;
163
+ const iconAttachmentRaw = args.flags.get("icon-attachment-id");
164
+ const clearIconAttachment = args.bools.has("clear-icon-attachment-id");
165
+ if (iconAttachmentRaw !== undefined && clearIconAttachment) {
166
+ fail("--icon-attachment-id and --clear-icon-attachment-id are mutually exclusive", "invalid_args");
167
+ }
168
+ if (iconAttachmentRaw !== undefined) {
169
+ candidate["icon_attachment_id"] = iconAttachmentRaw;
170
+ }
171
+ if (clearIconAttachment)
172
+ candidate["icon_attachment_id"] = null;
173
+ // Bail before the round trip if nothing was supplied — the relay would
174
+ // reject this with `invalid_request` anyway, but a local message names the
175
+ // CLI flags the user knows.
176
+ if (Object.keys(candidate).length === 0) {
177
+ fail("pass at least one field to update (see --help for the full list)", "invalid_args");
178
+ }
179
+ const parsed = updatePaneSchema.safeParse(candidate);
180
+ if (!parsed.success) {
181
+ const issue = parsed.error.issues[0];
182
+ const where = issue && issue.path.length > 0 ? schemaPathToFlag(issue.path) : "request";
183
+ fail(`invalid update request: ${where}: ${issue ? issue.message : "validation failed"}`, "invalid_args", parsed.error.flatten());
184
+ }
185
+ const req = parsed.data;
186
+ const client = makeClient(args);
187
+ try {
188
+ const res = await client.updatePane(paneId, req);
189
+ printJson(res);
190
+ }
191
+ catch (e) {
192
+ failFromError(e);
193
+ }
194
+ }
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ import { runSend, sendHelp } from "./commands/send.js";
32
32
  import { runWatch, watchHelp } from "./commands/watch.js";
33
33
  import { runDelete, deleteHelp } from "./commands/delete.js";
34
34
  import { runUpgrade, upgradeHelp } from "./commands/upgrade.js";
35
+ import { runUpdate, updateHelp } from "./commands/update.js";
35
36
  import { runParticipant, participantHelp } from "./commands/participant.js";
36
37
  import { runShare, shareHelp } from "./commands/share.js";
37
38
  import { runTemplate, artifactHelp } from "./commands/template.js";
@@ -66,6 +67,10 @@ Pane commands (operate on the core noun — a live UI channel):
66
67
  upgrade <id> Re-pin a live pane to another version of its template —
67
68
  swap design + content in place, same URL (--template-version
68
69
  <n>, --force to override the schema-compat gate).
70
+ update <id> Edit instance-level fields in place (PATCH /v1/panes/:id):
71
+ --ttl / --expires-at, --title, --preamble, --input-data,
72
+ --metadata, --tags, --icon-emoji, --icon-attachment-id
73
+ (and matching --clear-icon-* flags). Same URL.
69
74
  participant Manage participant URLs on an existing pane
70
75
  <list|new|revoke> (list | mint a fresh URL | revoke one URL).
71
76
  share <id> Share a pane by identity: invite humans by email
@@ -80,9 +85,14 @@ Other noun groups:
80
85
  Doubles as an end-to-end smoke test of your install.
81
86
  template Reusable, versioned UI templates
82
87
  (create | version | update | search | list | show | delete).
88
+ records Per-pane mutable collections — todo lists, checklists,
89
+ kanban cards, comments (list | get | upsert | update |
90
+ delete | delete-collection | watch), keyed by a stable
91
+ record_key.
83
92
  template-records Owner-curated content scoped to a Template head
84
- (list | get | upsert | update | delete), visible to
85
- every pane derived from any version of the template.
93
+ (list | get | upsert | update | delete |
94
+ delete-collection), visible to every pane derived from
95
+ any version of the template.
86
96
  key YOUR agent's API key (list | revoke).
87
97
  taste YOUR agent's freeform UI taste notes
88
98
  (get | set | clear) — presentation preferences the agent
@@ -153,6 +163,10 @@ const BOOLEAN_FLAGS = new Set([
153
163
  "list",
154
164
  // `pane upgrade --force`: override the strict schema-compat gate.
155
165
  "force",
166
+ // `pane update --clear-icon-*`: drop the per-pane icon override (the field
167
+ // becomes null and the pane falls back to the template's icon).
168
+ "clear-icon-emoji",
169
+ "clear-icon-attachment-id",
156
170
  ]);
157
171
  async function main() {
158
172
  const rawArgv = process.argv.slice(2);
@@ -189,6 +203,7 @@ async function main() {
189
203
  watch: watchHelp,
190
204
  delete: deleteHelp,
191
205
  upgrade: upgradeHelp,
206
+ update: updateHelp,
192
207
  participant: participantHelp,
193
208
  share: shareHelp,
194
209
  // Other noun groups.
@@ -246,6 +261,9 @@ async function main() {
246
261
  case "upgrade":
247
262
  await runUpgrade(args);
248
263
  break;
264
+ case "update":
265
+ await runUpdate(args);
266
+ break;
249
267
  case "participant":
250
268
  await runParticipant(args);
251
269
  break;
package/dist/version.js CHANGED
@@ -8,4 +8,4 @@
8
8
  // Keep this in lockstep with packages/cli/package.json's `version` field;
9
9
  // they're consulted in different places (here for the runtime header,
10
10
  // package.json for npm publish + dependency resolution).
11
- export const VERSION = "0.0.16";
11
+ export const VERSION = "0.0.18";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paneui/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Command-line client for the Pane relay: create panes, inspect state, send and watch events.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,11 +41,11 @@
41
41
  "test:unit": "vitest run"
42
42
  },
43
43
  "dependencies": {
44
- "@paneui/core": "^0.0.16",
44
+ "@paneui/core": "^0.0.18",
45
45
  "qrcode-terminal": "^0.12.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/node": "^25.9.1",
48
+ "@types/node": "^25.9.2",
49
49
  "@types/qrcode-terminal": "^0.12.2",
50
50
  "typescript": "^6.0.3",
51
51
  "vitest": "^4.1.8"