@gxp-dev/tools 2.0.76 → 2.0.77

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.
@@ -29,6 +29,10 @@
29
29
  "configurationFile": { "type": "string" },
30
30
  "appInstructionsFile": { "type": "string" },
31
31
  "defaultStylingFile": { "type": "string" },
32
+ "formTemplate": {
33
+ "type": "boolean",
34
+ "description": "Declares this plugin as a form/quiz/survey app. When true, the platform expects `configuration.json` to include a `formTemplate` root key — an array of cards defining the starter question set an admin can customize on install."
35
+ },
32
36
  "appInstructions": { "type": "string" },
33
37
  "defaultStyling": { "type": "string" },
34
38
  "configuration": {
@@ -11,6 +11,11 @@
11
11
  "description": "Top-level array of tab definitions. Each item is a card (usually card_list or fields_list) rendered under the plugin's configuration panel.",
12
12
  "items": { "$ref": "card.schema.json" }
13
13
  },
14
+ "formTemplate": {
15
+ "type": "array",
16
+ "description": "Starter form/quiz/survey question set for plugins that act as a form app. Each item is a card defining a form section (use `fields_list` for question groups or `card_list` to nest further). The admin can copy/customize these when they install the plugin. Only meaningful when `formTemplate` is true in app-manifest.json.",
17
+ "items": { "$ref": "card.schema.json" }
18
+ },
14
19
  "title": { "type": ["string", "null"] },
15
20
  "description": { "type": ["string", "null"] },
16
21
  "version": { "type": ["string", "integer", "null"] }
@@ -1009,6 +1009,7 @@ function buildInteractiveInitialPrompt(projectName, description, provider) {
1009
1009
  "- What real-world data it reads/writes — identify the concrete platform operationIds via the MCP, never invent them",
1010
1010
  "- Which real-time events matter (use `api_find_events_for_operation` for each planned operationId)",
1011
1011
  "- Every piece of admin-editable content: strings, assets, colors/thresholds/settings, feature toggles",
1012
+ '- Whether this is a **form app** (quiz, survey, questionnaire, signup flow). If yes, set `formTemplate: true` in `app-manifest.json` and populate `configuration.json`\'s `formTemplate` root array with starter question cards (`config_add_card` with `parent_path: "/formTemplate"` — auto-initializes). See the instructions file for the full pattern. Keep end-user questions in `formTemplate` and admin config in `additionalTabs` — do not mix the two.',
1012
1013
  "",
1013
1014
  "Then propose a plan — screens/components, data flow, admin configuration form, and the exact keys you'll add to `app-manifest.json` — and get my confirmation before implementing.",
1014
1015
  "",
@@ -163,7 +163,12 @@ function moveItem(doc, fromPointer, targetArrayPointer, position = "end") {
163
163
  * cards with their JSON pointer, type, title, and a summary of children.
164
164
  *
165
165
  * Cards are discovered under:
166
- * additionalTabs[] (root array of cards)
166
+ * additionalTabs[] (root array of cards — admin form)
167
+ * formTemplate[] (root array of cards — starter
168
+ * form/quiz/survey questions; only
169
+ * present on form apps where
170
+ * app-manifest.json has
171
+ * `formTemplate: true`)
167
172
  * <card>.cards[] (card_list)
168
173
  * <card>.tabsList[].cards[] (tabs_list)
169
174
  */
@@ -201,6 +206,9 @@ function listCards(doc) {
201
206
  if (Array.isArray(doc?.additionalTabs)) {
202
207
  doc.additionalTabs.forEach((c, i) => walk(c, `/additionalTabs/${i}`))
203
208
  }
209
+ if (Array.isArray(doc?.formTemplate)) {
210
+ doc.formTemplate.forEach((c, i) => walk(c, `/formTemplate/${i}`))
211
+ }
204
212
  return out
205
213
  }
206
214
 
@@ -224,7 +224,7 @@ const CONFIG_TOOLS = [
224
224
  {
225
225
  name: "config_add_card",
226
226
  description:
227
- "Add a card under a parent container (additionalTabs, a card_list's cards[], or a tabs_list tab).",
227
+ "Add a card under a parent container (additionalTabs, formTemplate, a card_list's cards[], or a tabs_list tab). The `/additionalTabs` and `/formTemplate` root arrays are auto-initialized if missing, so the first add works without seeding.",
228
228
  inputSchema: {
229
229
  type: "object",
230
230
  properties: {
@@ -232,7 +232,7 @@ const CONFIG_TOOLS = [
232
232
  parent_path: {
233
233
  type: "string",
234
234
  description:
235
- "JSON pointer to the parent array of cards (e.g. '/additionalTabs' or '/additionalTabs/0/cards').",
235
+ "JSON pointer to the parent array of cards. Top-level options: '/additionalTabs' (admin configuration form) or '/formTemplate' (starter form/quiz questions for form apps). Nested options: '/additionalTabs/0/cards', '/formTemplate/0/cards', etc.",
236
236
  },
237
237
  card: {
238
238
  type: "object",
@@ -448,6 +448,18 @@ async function handleConfigToolCall(name, args = {}) {
448
448
  case "config_add_card": {
449
449
  const abs = resolveProjectPath(args.path)
450
450
  const doc = readJson(abs)
451
+
452
+ // Auto-initialize known root-level card arrays if they're missing
453
+ // so the first `config_add_card` against /formTemplate (or
454
+ // /additionalTabs) works without a separate seeding step.
455
+ const KNOWN_ROOT_ARRAYS = ["/formTemplate", "/additionalTabs"]
456
+ if (KNOWN_ROOT_ARRAYS.includes(args.parent_path)) {
457
+ const rootKey = args.parent_path.slice(1)
458
+ if (!Array.isArray(doc[rootKey])) {
459
+ doc[rootKey] = []
460
+ }
461
+ }
462
+
451
463
  const parent = getByPointer(doc, args.parent_path)
452
464
  if (!Array.isArray(parent)) {
453
465
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gxp-dev/tools",
3
- "version": "2.0.76",
3
+ "version": "2.0.77",
4
4
  "description": "Dev tools to create platform plugins",
5
5
  "type": "commonjs",
6
6
  "publishConfig": {
@@ -522,6 +522,74 @@ Each field's `name` must exactly match the manifest key it controls — that's t
522
522
 
523
523
  Every mutation is linter-guarded against `bin/lib/lint/schemas/`. If a write is refused, read the validation error and fix the input — do not reach for `force: true`.
524
524
 
525
+ ## Form / Quiz / Survey Apps — `formTemplate`
526
+
527
+ Some plugins _are_ a form: a quiz, a survey, a signup flow. For those, ship a starter question set an admin can customize on install. Two keys, kept consistent:
528
+
529
+ 1. **`app-manifest.json` → `"formTemplate": true`** — flags the plugin as a form app so the platform opts into form-specific UI (question editor, response viewer).
530
+ 2. **`configuration.json` → `"formTemplate": [ ...cards ]`** — array of cards defining the starter form sections, same shape as `additionalTabs`. Typically `fields_list` cards whose `fieldsList` items are the end-user questions.
531
+
532
+ ### Minimal example
533
+
534
+ ```json
535
+ // app-manifest.json
536
+ {
537
+ "name": "welcome-quiz",
538
+ "version": "0.1.0",
539
+ "formTemplate": true,
540
+ "settings": {},
541
+ "strings": { "default": { "title": "Welcome Quiz" } },
542
+ "assets": {},
543
+ "dependencies": [],
544
+ "permissions": []
545
+ }
546
+ ```
547
+
548
+ ```json
549
+ // configuration.json
550
+ {
551
+ "additionalTabs": [
552
+ /* admin-facing plugin config (unchanged) */
553
+ ],
554
+ "formTemplate": [
555
+ {
556
+ "type": "fields_list",
557
+ "title": "About You",
558
+ "fieldsList": [
559
+ { "type": "text", "name": "full_name", "label": "Full name" },
560
+ {
561
+ "type": "radio",
562
+ "name": "experience",
563
+ "label": "Experience level",
564
+ "options": [
565
+ { "label": "Beginner", "value": "beginner" },
566
+ { "label": "Advanced", "value": "advanced" }
567
+ ]
568
+ }
569
+ ]
570
+ }
571
+ ]
572
+ }
573
+ ```
574
+
575
+ ### Building with the MCP
576
+
577
+ Pointer-based adds work against `/formTemplate` the same way they do against `/additionalTabs`:
578
+
579
+ - `config_add_card` with `parent_path: "/formTemplate"` — the array auto-initializes on first add, no seed step.
580
+ - `config_add_card` with `parent_path: "/formTemplate/0/cards"` — nest under a `card_list` for grouped sections.
581
+ - `config_add_field` with `card_path: "/formTemplate/0"` — add a question to the first section.
582
+ - `config_list_cards` — returns cards from both `/additionalTabs` and `/formTemplate` with their JSON pointers.
583
+
584
+ Use any valid field type (`text`, `textarea`, `number`, `radio`, `checkbox`, `select`, `asyncSelect`, `selectAsset`, etc.). The `name` of each field becomes the response key the platform stores.
585
+
586
+ ### Mental model
587
+
588
+ - `additionalTabs` → **admin** configuration (every plugin). Strings, assets, color pickers, dependency binding, feature toggles.
589
+ - `formTemplate` → **end-user** questions (only form apps). Only add here if `app-manifest.json` has `formTemplate: true`.
590
+
591
+ Don't mix. Quiz questions never belong in `additionalTabs`; admin config never belongs in `formTemplate`. Both roots validate against the same card/field schema, so `gxdev lint --all` catches malformed structures in either place.
592
+
525
593
  ## Component Template
526
594
 
527
595
  When creating new components, use this pattern:
@@ -321,6 +321,78 @@ Group related fields into `fields_list` cards (`config_add_card` then `config_ad
321
321
 
322
322
  If a mutation is refused, read the validation error and fix the input — do not reach for `force: true`.
323
323
 
324
+ ## Form / Quiz / Survey Apps — `formTemplate`
325
+
326
+ Some plugins _are_ a form — a quiz, a survey, a signup questionnaire, a check-in flow. For those, the configuration file ships a second root-level card array, `formTemplate`, that holds the starter questions an admin customizes on install.
327
+
328
+ **Two keys tie this together. You must set both consistently.**
329
+
330
+ 1. **`app-manifest.json` → `"formTemplate": true`** — declares the plugin as a form app. Platforms use this to opt the install into form-specific UI (question editor, response viewer, etc.).
331
+ 2. **`configuration.json` → `"formTemplate": [ ...cards ]`** — the starter question set, structured identically to `additionalTabs`: an array of cards, typically `fields_list` sections, each with a `fieldsList` of question fields.
332
+
333
+ ### Minimal example
334
+
335
+ ```json
336
+ // app-manifest.json
337
+ {
338
+ "name": "welcome-quiz",
339
+ "version": "0.1.0",
340
+ "formTemplate": true,
341
+ "settings": {},
342
+ "strings": { "default": { "title": "Welcome Quiz" } },
343
+ "assets": {},
344
+ "dependencies": [],
345
+ "permissions": []
346
+ }
347
+ ```
348
+
349
+ ```json
350
+ // configuration.json
351
+ {
352
+ "additionalTabs": [
353
+ /* admin-facing plugin config goes here as always */
354
+ ],
355
+ "formTemplate": [
356
+ {
357
+ "type": "fields_list",
358
+ "title": "About You",
359
+ "fieldsList": [
360
+ { "type": "text", "name": "full_name", "label": "Full name" },
361
+ {
362
+ "type": "radio",
363
+ "name": "experience",
364
+ "label": "Experience level",
365
+ "options": [
366
+ { "label": "Beginner", "value": "beginner" },
367
+ { "label": "Advanced", "value": "advanced" }
368
+ ]
369
+ }
370
+ ]
371
+ }
372
+ ]
373
+ }
374
+ ```
375
+
376
+ ### Building `formTemplate` with the MCP
377
+
378
+ Use the same `config_*` tools you'd use for `additionalTabs`, pointed at the `/formTemplate` root:
379
+
380
+ - `config_add_card` with `parent_path: "/formTemplate"` — the array is auto-initialized on first add, no seed step needed.
381
+ - `config_add_card` with `parent_path: "/formTemplate/0/cards"` — nest sub-cards if you need a `card_list` grouping.
382
+ - `config_add_field` with `card_path: "/formTemplate/0"` — add questions to a section.
383
+ - `config_list_cards` — lists cards from both `/additionalTabs` and `/formTemplate` with their JSON pointers.
384
+
385
+ Use the same field types as any other form (`text`, `textarea`, `number`, `radio`, `checkbox`, `select`, `asyncSelect`, `selectAsset`, etc.). Question names live under `fieldsList[].name` — these become the response keys the platform stores.
386
+
387
+ ### When to use `formTemplate` vs `additionalTabs`
388
+
389
+ - `additionalTabs` — **admin** configuration form (every plugin has this). The person installing the plugin fills this in once.
390
+ - `formTemplate` — **end-user** form questions (only form apps). Admins may tweak these before publishing; end users (attendees, staff) answer them at runtime.
391
+
392
+ Don't confuse them. Strings, assets, dependencies, colors → `additionalTabs`. Quiz/survey questions that end users will answer → `formTemplate`.
393
+
394
+ Finish with `gxdev lint --all`. The linter validates both roots against the same card/field schema, so malformed questions fail in the same way malformed admin fields do.
395
+
324
396
  ## Component Kit
325
397
 
326
398
  Import UI components from `@gramercytech/gx-componentkit`:
@@ -231,6 +231,21 @@ Every admin-editable piece of content goes through a directive — that's the br
231
231
 
232
232
  Tools: `config_list_card_types`, `config_list_field_types`, `config_get_field_schema`, `config_add_card`, `config_add_field`, `config_move_field`, `config_remove_field`, `config_validate`, `config_extract_strings`. Every mutation is linter-guarded against schemas in `bin/lib/lint/schemas/`.
233
233
 
234
+ ## Form / Quiz / Survey Apps — `formTemplate`
235
+
236
+ If the plugin _is_ a form (quiz, survey, questionnaire), ship a starter question set the admin can customize on install. Two keys, both required, must be kept consistent:
237
+
238
+ 1. `app-manifest.json` → `"formTemplate": true` — marks the plugin as a form app.
239
+ 2. `configuration.json` → `"formTemplate": [ ...cards ]` — array of cards (same shape as `additionalTabs`), typically `fields_list` sections whose `fieldsList` items are the questions.
240
+
241
+ Build it with the same MCP tools, pointed at `/formTemplate`:
242
+
243
+ - `config_add_card` with `parent_path: "/formTemplate"` (the array is auto-initialized on first add).
244
+ - `config_add_field` with `card_path: "/formTemplate/0"` to add questions to a section.
245
+ - `config_list_cards` surfaces both `/additionalTabs` and `/formTemplate` cards.
246
+
247
+ Mental model: `additionalTabs` = **admin** config (every plugin). `formTemplate` = **end-user** questions (only form apps). Admin strings, assets, colors, dependency bindings → `additionalTabs`. Questions an attendee/user will answer → `formTemplate`.
248
+
234
249
  ## Component Kit
235
250
 
236
251
  Use `@gramercytech/gx-componentkit` for UI: