@paneui/cli 0.0.14 → 0.0.16

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.
@@ -14,6 +14,7 @@ const KNOWN_FLAGS = [
14
14
  "version",
15
15
  "event-schema",
16
16
  "input-schema",
17
+ "record-schema",
17
18
  "title",
18
19
  "preamble",
19
20
  "input-data",
@@ -22,6 +23,7 @@ const KNOWN_FLAGS = [
22
23
  "metadata",
23
24
  "callback",
24
25
  "context-key",
26
+ "tags",
25
27
  "icon-emoji",
26
28
  "icon-attachment-id",
27
29
  ];
@@ -59,6 +61,7 @@ const SCHEMA_PATH_TO_FLAG = {
59
61
  "template.source": "--template",
60
62
  "template.event_schema": "--event-schema",
61
63
  "template.input_schema": "--input-schema",
64
+ "template.record_schema": "--record-schema",
62
65
  };
63
66
  function schemaPathToFlag(path) {
64
67
  const dotted = path.map(String).join(".");
@@ -82,7 +85,8 @@ A pane is one use of an template. Supply the template in ONE of two ways:
82
85
  pane create --template-id <id|slug> [--version <n>] [--input-data <v>]
83
86
 
84
87
  Inline form — a one-off template, defined on this call:
85
- pane create --template <path|inline> [--event-schema <path|json>] [options]
88
+ pane create --template <path|inline> [--event-schema <path|json>]
89
+ [--record-schema <path|json>] [options]
86
90
 
87
91
  Exactly one of --template-id / --template must be given.
88
92
 
@@ -106,7 +110,19 @@ Template (choose one):
106
110
  --event-schema <v> Inline-form event schema. A .json file, or inline JSON.
107
111
  Optional with --template. Omit for a view-only template
108
112
  (a report/dashboard the human only views — no page/agent
109
- events). Ignored with --template-id.
113
+ events). Rejected with --template-id (the schema comes
114
+ from the pinned template version there).
115
+
116
+ Pane has TWO data primitives — pick the right one before
117
+ designing the schema. Events (this flag) are an
118
+ append-only journal: forms, approvals, surveys, pickers,
119
+ doc reviews. RECORDS (--record-schema, below) are a
120
+ mutable collection: todo lists, shopping lists,
121
+ checklists, kanban boards, comment threads, inventories.
122
+ If the page shows more than one mutable item and the
123
+ current state matters more than the history of edits,
124
+ you want records. See the "Records" section of
125
+ \`pane skill show\` for the decision table.
110
126
 
111
127
  Shape — an object with an "events" map, keyed by event
112
128
  type. Each entry declares who may emit it and the JSON
@@ -135,6 +151,42 @@ Template (choose one):
135
151
  page via window.pane.downloadBlob. Without it, attachment
136
152
  refs in --input-data are silently unreachable. See
137
153
  docs/SPEC.md and #208.
154
+ --record-schema <v> Inline-form record schema — declares the per-pane
155
+ mutable collections this template exposes (todos,
156
+ comments, line items, etc.). A .json file, or inline
157
+ JSON. Optional with --template; omit for an event-only
158
+ one-off. Rejected with --template-id (the schema comes
159
+ from the pinned template version there).
160
+
161
+ Shape — a JSON Schema 2020-12 document with an
162
+ "x-pane-collections" extension mapping collection names
163
+ to { schema, write, delete }. \`write\` is a non-empty
164
+ subset of {agent, page}; \`delete\` is a non-empty
165
+ subset of {agent, page, author}.
166
+ {
167
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
168
+ "$defs": {
169
+ "Todo": {
170
+ "type": "object",
171
+ "properties": {
172
+ "text": { "type": "string" },
173
+ "done": { "type": "boolean" }
174
+ },
175
+ "required": ["text"]
176
+ }
177
+ },
178
+ "x-pane-collections": {
179
+ "todos": {
180
+ "schema": { "$ref": "#/$defs/Todo" },
181
+ "write": ["page", "agent"],
182
+ "delete": ["author"]
183
+ }
184
+ }
185
+ }
186
+ See 'pane skill show' (the "Records" section) for the
187
+ full grammar. Use \`pane records …\` to mutate rows in
188
+ a live pane, and \`pane template-records …\` for
189
+ template-level (cross-pane) curated rows.
138
190
 
139
191
  Options:
140
192
  --title <text> Tab title shown to the human (max 80 chars, single
@@ -172,6 +224,13 @@ Options:
172
224
  meaningful when the calling agent is claimed by a
173
225
  human; omit otherwise. Allowed chars: A-Za-z0-9_:.-,
174
226
  max 256.
227
+ --tags <t1,t2,...> Per-pane filter tags (comma-separated). Merged with the
228
+ template's tags (deduped) and snapshotted onto the pane;
229
+ they drive the human's Panes-tab tag filter. Use for an
230
+ instance-specific axis on top of the template's tags —
231
+ e.g. a PR-review template tagged \`pr-review\`, with the
232
+ repo per pane: \`--tags cp-backend\`. (\`favorite\`/
233
+ \`favorites\` are reserved; ≤20 tags, ≤50 chars each.)
175
234
  --icon-emoji <e> Per-pane icon override — a single emoji grapheme. NULL/
176
235
  omitted = inherit the template's icon.
177
236
  --icon-attachment-id <id>
@@ -213,6 +272,9 @@ export async function runCreate(args) {
213
272
  if (args.flags.get("input-schema") !== undefined) {
214
273
  fail("--input-schema is incompatible with --template-id — the input schema comes from the pinned template version. Author the schema on the template (`pane template create --input-schema …`) instead.", "invalid_args");
215
274
  }
275
+ if (args.flags.get("record-schema") !== undefined) {
276
+ fail("--record-schema is incompatible with --template-id — the record collections come from the pinned template version. Author the schema on the template (`pane template create --record-schema …`) instead.", "invalid_args");
277
+ }
216
278
  // --name / --slug name the inline form's auto-created template. The
217
279
  // reference form inherits the existing Template.name/slug, so they have
218
280
  // no meaning here — reject rather than silently ignore.
@@ -246,6 +308,7 @@ export async function runCreate(args) {
246
308
  // See #208.
247
309
  const schemaVal = args.flags.get("event-schema");
248
310
  const inputSchemaVal = args.flags.get("input-schema");
311
+ const recordSchemaVal = args.flags.get("record-schema");
249
312
  const templateType = (args.flags.get("template-type") ?? "html-inline");
250
313
  if (templateType !== "html-inline" && templateType !== "html-ref") {
251
314
  fail("--template-type must be 'html-inline' or 'html-ref'", "invalid_args");
@@ -301,6 +364,22 @@ export async function runCreate(args) {
301
364
  fail(e instanceof Error ? e.message : String(e), "invalid_args");
302
365
  }
303
366
  }
367
+ // --record-schema: declares the inline template's per-pane record
368
+ // collections. Absent → no collections (event-only one-off). The relay
369
+ // does the full validation; we only check it parses as an object so a
370
+ // typo surfaces locally rather than as a generic "expected object".
371
+ if (recordSchemaVal !== undefined) {
372
+ try {
373
+ const v = resolveJson(recordSchemaVal, "--record-schema");
374
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
375
+ fail("--record-schema must be a JSON object", "invalid_args");
376
+ }
377
+ inlineArtifact["record_schema"] = v;
378
+ }
379
+ catch (e) {
380
+ fail(e instanceof Error ? e.message : String(e), "invalid_args");
381
+ }
382
+ }
304
383
  candidate["template"] = inlineArtifact;
305
384
  }
306
385
  // --title — passthrough, no client-side requiredness. The relay is the
@@ -370,6 +449,19 @@ export async function runCreate(args) {
370
449
  if (contextKey !== undefined) {
371
450
  candidate["context_key"] = contextKey;
372
451
  }
452
+ // --tags — per-pane filter tags, comma-separated. Merged (deduped) with the
453
+ // template's tags by the relay and snapshotted onto the pane. Useful for an
454
+ // instance-specific axis (e.g. the repo a PR-review pane is for) on top of
455
+ // the template's tags. The relay enforces the limits + reserved names.
456
+ const tagsRaw = args.flags.get("tags");
457
+ if (tagsRaw !== undefined) {
458
+ const tags = tagsRaw
459
+ .split(",")
460
+ .map((t) => t.trim())
461
+ .filter((t) => t !== "");
462
+ if (tags.length > 0)
463
+ candidate["tags"] = tags;
464
+ }
373
465
  // Per-pane icon override. Passthrough — the relay validates the emoji
374
466
  // (single grapheme) and the attachment (ready raster image, accessible to
375
467
  // the agent), so a bad value panes as a schema/route rejection rather than
@@ -23,6 +23,7 @@ const CREATE_FLAGS = [
23
23
  "template-type",
24
24
  "event-schema",
25
25
  "input-schema",
26
+ "record-schema",
26
27
  "icon-emoji",
27
28
  ];
28
29
  const SET_ICON_FLAGS = ["template-id", "emoji", "image"];
@@ -32,6 +33,7 @@ const VERSION_FLAGS = [
32
33
  "template-type",
33
34
  "event-schema",
34
35
  "input-schema",
36
+ "record-schema",
35
37
  ];
36
38
  const UPDATE_FLAGS = ["name", "slug", "description", "tags"];
37
39
  const NO_FLAGS = [];
@@ -73,7 +75,8 @@ Subcommands:
73
75
  pane template create --name <n> --template <path|inline>
74
76
  [--event-schema <path|json>] [--slug <s>]
75
77
  [--description <d>] [--tags <t1,t2>]
76
- [--input-schema <path|json>] [--template-type <t>]
78
+ [--input-schema <path|json>]
79
+ [--record-schema <path|json>] [--template-type <t>]
77
80
  [--icon-emoji <emoji>]
78
81
  Creates a named template. Prints { template_id, slug, version }.
79
82
  --icon-emoji sets a single-emoji icon at create time; use 'set-icon'
@@ -81,7 +84,8 @@ Subcommands:
81
84
 
82
85
  pane template version <id|slug> --template <path|inline>
83
86
  [--event-schema <path|json>]
84
- [--input-schema <path|json>] [--template-type <t>]
87
+ [--input-schema <path|json>]
88
+ [--record-schema <path|json>] [--template-type <t>]
85
89
  Appends a new immutable version. Prints { template_id, version }.
86
90
 
87
91
  pane template update <id|slug> [--name <n>] [--slug <s>]
@@ -149,6 +153,17 @@ Options:
149
153
  report/dashboard the human only views — no page/agent
150
154
  events).
151
155
 
156
+ Pane has TWO data primitives — pick the right one before
157
+ designing the schema. Events (this flag) are an
158
+ append-only journal: forms, approvals, surveys, pickers,
159
+ doc reviews. RECORDS (--record-schema, below) are a
160
+ mutable collection: todo lists, shopping lists,
161
+ checklists, kanban boards, comment threads, inventories.
162
+ If the page shows more than one mutable item and the
163
+ current state matters more than the history of edits,
164
+ you want records. See the "Records" section of
165
+ \`pane skill show\` for the decision table.
166
+
152
167
  Shape — an object with an "events" map, keyed by event
153
168
  type. Each entry declares who may emit it and the JSON
154
169
  Schema for its payload:
@@ -169,6 +184,39 @@ Options:
169
184
  emit against it. See docs/SPEC.md for the full grammar.
170
185
  --input-schema <v> JSON Schema for this template's per-pane input_data —
171
186
  a file path, or inline JSON. Optional.
187
+ --record-schema <v> JSON Schema 2020-12 document declaring this template's
188
+ per-pane record collections — a file path, or inline
189
+ JSON. Optional; omit for an event-only template (no
190
+ mutable collections).
191
+
192
+ Shape — a JSON Schema doc with an "x-pane-collections"
193
+ extension mapping collection names to { schema, write,
194
+ delete } entries. \`write\` is a non-empty subset of
195
+ {agent, page}; \`delete\` is a non-empty subset of
196
+ {agent, page, author}.
197
+ {
198
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
199
+ "$defs": {
200
+ "Todo": {
201
+ "type": "object",
202
+ "properties": {
203
+ "text": { "type": "string" },
204
+ "done": { "type": "boolean" }
205
+ },
206
+ "required": ["text"]
207
+ }
208
+ },
209
+ "x-pane-collections": {
210
+ "todos": {
211
+ "schema": { "$ref": "#/$defs/Todo" },
212
+ "write": ["page", "agent"],
213
+ "delete": ["author"]
214
+ }
215
+ }
216
+ }
217
+ The relay validates the document and rejects anything
218
+ malformed at create time. See 'pane skill show' (the
219
+ "Records" section) for the full grammar.
172
220
  --icon-emoji <e> Single-emoji icon for the template (create only).
173
221
  --template-type <t> "html-inline" (default) or "html-ref".
174
222
  --url <url> Relay base URL (overrides PANE_URL).
@@ -229,6 +277,29 @@ function resolveInputSchema(args) {
229
277
  fail(e instanceof Error ? e.message : String(e), "invalid_args");
230
278
  }
231
279
  }
280
+ /**
281
+ * Resolve the optional --record-schema — file path or inline JSON. Must be an
282
+ * object (a JSON Schema 2020-12 document with an `x-pane-collections`
283
+ * extension). Absent → returns `undefined` so the caller omits `record_schema`
284
+ * from the request entirely (template has no record collections). The relay
285
+ * runs the full shape + collection validation; this helper only does the
286
+ * obvious "is it an object" pre-check so a typo surfaces locally.
287
+ */
288
+ function resolveRecordSchema(args) {
289
+ const raw = args.flags.get("record-schema");
290
+ if (raw === undefined)
291
+ return undefined;
292
+ try {
293
+ const v = resolveJson(raw, "--record-schema");
294
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
295
+ fail("--record-schema must be a JSON object", "invalid_args");
296
+ }
297
+ return v;
298
+ }
299
+ catch (e) {
300
+ fail(e instanceof Error ? e.message : String(e), "invalid_args");
301
+ }
302
+ }
232
303
  /** Parse a comma-separated --tags flag into a string array. */
233
304
  function resolveTags(args) {
234
305
  const raw = args.flags.get("tags");
@@ -249,6 +320,7 @@ async function runTemplateCreate(args) {
249
320
  const source = resolveSource(args, type);
250
321
  const eventSchema = resolveEventSchema(args);
251
322
  const inputSchema = resolveInputSchema(args);
323
+ const recordSchema = resolveRecordSchema(args);
252
324
  const tags = resolveTags(args);
253
325
  const slug = args.flags.get("slug");
254
326
  const description = args.flags.get("description");
@@ -269,6 +341,11 @@ async function runTemplateCreate(args) {
269
341
  candidate["tags"] = tags;
270
342
  if (inputSchema !== undefined)
271
343
  candidate["input_schema"] = inputSchema;
344
+ // record_schema is OMITTED entirely when --record-schema is absent — a
345
+ // template with no record collections (event-only). Setting it to
346
+ // `undefined` would still add the key.
347
+ if (recordSchema !== undefined)
348
+ candidate["record_schema"] = recordSchema;
272
349
  const iconEmoji = args.flags.get("icon-emoji");
273
350
  if (iconEmoji !== undefined)
274
351
  candidate["icon_emoji"] = iconEmoji;
@@ -302,6 +379,7 @@ async function runTemplateVersion(args) {
302
379
  const source = resolveSource(args, type);
303
380
  const eventSchema = resolveEventSchema(args);
304
381
  const inputSchema = resolveInputSchema(args);
382
+ const recordSchema = resolveRecordSchema(args);
305
383
  const candidate = {
306
384
  source,
307
385
  type,
@@ -312,6 +390,11 @@ async function runTemplateVersion(args) {
312
390
  candidate["event_schema"] = eventSchema;
313
391
  if (inputSchema !== undefined)
314
392
  candidate["input_schema"] = inputSchema;
393
+ // record_schema is OMITTED entirely when --record-schema is absent — same
394
+ // rationale as event_schema above. Including the key as undefined would
395
+ // serialise as null and read as "intentionally cleared" on the wire.
396
+ if (recordSchema !== undefined)
397
+ candidate["record_schema"] = recordSchema;
315
398
  const parsed = createArtifactVersionSchema.safeParse(candidate);
316
399
  if (!parsed.success) {
317
400
  const issue = parsed.error.issues[0];
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.14";
11
+ export const VERSION = "0.0.16";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paneui/cli",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
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,7 +41,7 @@
41
41
  "test:unit": "vitest run"
42
42
  },
43
43
  "dependencies": {
44
- "@paneui/core": "^0.0.14",
44
+ "@paneui/core": "^0.0.16",
45
45
  "qrcode-terminal": "^0.12.0"
46
46
  },
47
47
  "devDependencies": {