@ghentcdh/crouton-cli 0.0.1-alpha.81 → 0.0.1-alpha.89

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.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @ghentcdh/crouton-cli
2
+
3
+ CLI for managing Crouton projects — update resources, datasources, and more.
4
+
5
+ ## Local Development
6
+
7
+ Three ways to test your changes, ordered from fastest to most thorough.
8
+
9
+ ### 1. Run the built CLI directly (quickest)
10
+
11
+ Best for: changes to `update resources` and other CLI commands.
12
+
13
+ ```bash
14
+ # in the crouton repo
15
+ pnpm nx build crouton-cli
16
+ # or watch mode:
17
+ cd packages/crouton-cli && npx tsup --watch
18
+
19
+ # in your test project
20
+ node ~/path/to/crouton/packages/crouton-cli/dist/index.js update resources
21
+ ```
22
+
23
+ Tip — add an alias so you don't retype the path:
24
+
25
+ ```bash
26
+ alias crouton-dev="node ~/path/to/crouton/packages/crouton-cli/dist/index.js"
27
+ crouton-dev update resources
28
+ ```
29
+
30
+ ---
31
+
32
+ ### 2. Run `create-crouton` from your checkout
33
+
34
+ Best for: changes to the scaffold flow (prompts, templates, next-steps output).
35
+
36
+ ```bash
37
+ pnpm nx build create-crouton
38
+ cd ~/project/workshop_crouton
39
+ node ~/path/to/crouton/packages/create-crouton/dist/index.js my-app
40
+ ```
41
+
42
+ Local templates are bundled into `dist`, so you get your edits. The generated `package.json` still references `@ghentcdh/*@<version>`, so `pnpm install` pulls from npm — fine for testing prompts and templates. Pass `--no-install` to skip the install step.
43
+
44
+ ---
45
+
46
+ ### 3. Local npm registry via Verdaccio (most complete)
47
+
48
+ Best for: testing the full `npx @ghentcdh/create-crouton` flow, including the install step, an unpublished `crouton-cli`, and `crouton-prisma`. Run this at least once before cutting a release.
49
+
50
+ ```bash
51
+ # start the registry (http://localhost:4873)
52
+ npx verdaccio
53
+
54
+ # build everything and publish each package to the local registry
55
+ pnpm nx run-many -t build
56
+ (cd packages/crouton-cli/dist && npm publish --registry http://localhost:4873)
57
+ (cd packages/create-crouton && npm publish --registry http://localhost:4873)
58
+ # repeat for crouton-core / crouton-api / crouton-vue / crouton-prisma
59
+
60
+ # scaffold using only the local registry
61
+ cd ~/project/workshop_crouton
62
+ npm_config_registry=http://localhost:4873 npx @ghentcdh/create-crouton@latest my-app
63
+ ```
64
+
65
+ Before republishing the same version, either bump it (e.g. `0.0.1-local.2`) or unpublish first:
66
+
67
+ ```bash
68
+ npm unpublish --force --registry http://localhost:4873 @ghentcdh/crouton-cli
69
+ ```
70
+
71
+ Verdaccio proxies anything it doesn't have through to the public npm registry. You can also let Nx manage the registry setup:
72
+
73
+ ```bash
74
+ pnpm nx g @nx/js:setup-verdaccio
75
+ # then use:
76
+ pnpm nx local-registry
77
+ ```
78
+
79
+ ---
80
+
81
+ ### Which option to use?
82
+
83
+ | Situation | Option |
84
+ |-----------|--------|
85
+ | Fixing a CLI command (`.env`, schema, etc.) | **1** |
86
+ | Changing scaffold prompts or templates | **2** |
87
+ | Pre-release smoke test (e.g. before alpha.83) | **3** |
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: crouton
3
+ description: Add or change crouton resources, relations, field inputs, layouts, operations and data sources (resource.json, datasource.json, crouton.json). Use for any change to these files.
4
+ crouton-version: 0.0.1-alpha.82
5
+ ---
6
+
7
+ > If `crouton --version` differs from `crouton-version` above, the skill may be stale — check the installed version's docs in `node_modules/@ghentcdh/crouton-core/agent-docs/`.
8
+
9
+ # Crouton skill
10
+
11
+ Crouton is a resource-driven admin UI framework. This skill teaches the correct workflow for adding and changing
12
+ resources.
13
+
14
+ ## What crouton is
15
+
16
+ - Resources live in `<resourcesDir>` (see `crouton.json`). Each is a folder with `resource.json`.
17
+ - Data sources live in `<dataSourcesDir>`. Each is a folder with `datasource.json`.
18
+ - The Prisma schema (`schema.prisma`) is the source of truth for models and columns. Never hand-edit column lists that
19
+ `crouton update` owns.
20
+
21
+ ## The workflow loop
22
+
23
+ 1. Change `schema.prisma` (add/modify models or fields).
24
+ 2. Run `crouton update` — regenerates column lists, migrations, and type stubs.
25
+ 3. Edit `resource.json` (UI/behaviour: labels, field inputs, relations, layout).
26
+ 4. Run `crouton validate` — fix any reported issues before proceeding.
27
+ 5. Typecheck/build to confirm no TS errors.
28
+
29
+ ## Key rules
30
+
31
+ - Never hand-edit auto-generated sections inside `resource.json` (marked with comments).
32
+ - Use `manyToOne` for autocomplete lookups; `oneToMany` for embedded tables.
33
+ - `kind: "custom"` only when you have a `repository.ts` (config-only resource). For non-Prisma data, use `adapter` in
34
+ `datasource.json`.
35
+ - After any `resource.json` or `crouton.json` change, run `crouton validate`.
36
+ - Layout nodes use `items` only — there is no `controls` list. Items render in declared order.
37
+ - Table settings (`rowActions`, `tableActions`, `searchFields`, `filterFields`) go on the `type: "table"` node inside
38
+ `layout.table`, never on an outer wrapper node.
39
+ - When `layout.<ctx>` is defined, it is the **full list** for that context — only listed columns render. Unlisted
40
+ columns are hidden regardless of `hiddenIn*` flags.
41
+ - After editing layouts, run `crouton validate` to confirm no migration is needed.
42
+
43
+ ## Project-specific notes
44
+
45
+ If `.claude/skills/crouton/project.md` exists, read it first — it holds project conventions that override these
46
+ defaults.
47
+
48
+ ## References
49
+
50
+ - `references/resource-json.md` — annotated full example + key table
51
+ - `references/relations.md` — relation recipes
52
+ - `references/field-inputs.md` — which input for which column type
53
+ - `references/data-sources.md` — prisma vs custom adapter
54
+ - `references/recipes.md` — task recipes
55
+ - `references/common-mistakes.md` — common mistakes
56
+
57
+ ## Docs for the installed version
58
+
59
+ `node_modules/@ghentcdh/crouton-core/agent-docs/README.md`
@@ -0,0 +1,89 @@
1
+ # Common mistakes
2
+
3
+ ## 1. Using removed root keys
4
+
5
+ Many root keys moved. Run `crouton validate` to detect them — it reports the correct replacement path for each key.
6
+
7
+ | Wrong (old) | Right (new) |
8
+ |-----------------------------------------|------------------------------------------------------------------------------------------------|
9
+ | `"model": "Book"` at root | `"source": { "model": "Book" }` |
10
+ | `"database": "main"` at root | `"source": { "datasource": "main" }` |
11
+ | `"table": "books"` at root | `"source": { "dbTable": "books" }` |
12
+ | `"idType": "number"` at root | `"source": { "idType": "number" }` |
13
+ | `"display": { "mode": "page" }` at root | `"layout": { "view": { "container": { "mode": "page" } } }` |
14
+ | `"modalSize": "lg"` at root | `"layout": { "form": { "container": { "size": "lg" } } }` |
15
+ | `"include": ["author"]` at root | `"operations": { "findAll": { "include": ["author"] }, "findOne": { "include": ["author"] } }` |
16
+ | `"calculatedColumns": [...]` | `"columns": { "myCol": { "computed": { "sql": "..." } } }` |
17
+ | `column.showInLookup: true` | `"lookup": { "labels": ["colId"] }` on resource root |
18
+
19
+ ## 2. Putting relation config in fieldInput instead of column.relation
20
+
21
+ `fieldInput` only configures the UI widget. Relation data model belongs in `column.relation`.
22
+
23
+ ```json
24
+ // WRONG
25
+ "authorId": {
26
+ "fieldInput": {
27
+ "type": "autocomplete",
28
+ "relationType": "manyToOne",
29
+ "resource": "./author/resource.json",
30
+ "foreignKey": "authorId"
31
+ }
32
+ }
33
+
34
+ // RIGHT
35
+ "authorId": {
36
+ "relation": {
37
+ "resource": "./author/resource.json",
38
+ "type": "manyToOne",
39
+ "foreignKey": "authorId",
40
+ "displayKey": "name"
41
+ },
42
+ "fieldInput": { "type": "autocomplete" }
43
+ }
44
+ ```
45
+
46
+ ## 3. Using `format: "relation"` to detect relations
47
+
48
+ `fieldInput.format` is removed. Use `isRelation(col)` from `@ghentcdh/crouton-core` or check `!!col.relation`.
49
+
50
+ ## 4. Setting both `enum` and `fieldInput.options.values`
51
+
52
+ `enum` resolves a shared list from `crouton.enums.json` at load time. Setting both causes a validation error.
53
+ Use `enum` for shared lists, `fieldInput.options.values` for local inline lists — never both.
54
+
55
+ ## 5. Hand-editing auto-generated columns
56
+
57
+ `crouton update` owns columns derived from Prisma. Edits to column names or types are overwritten on the next
58
+ `crouton update`.
59
+ Only edit `fieldInput`, `label`, `hidden`, `required`, `relation` inside each column entry.
60
+
61
+ ## 6. `kind: "custom"` when you need `adapter: "custom"`
62
+
63
+ `kind: "custom"` = this resource has a custom `repository.ts`.
64
+ If your data source is external (not Prisma), set `adapter: "custom"` in `datasource.json` instead.
65
+
66
+ ## 7. Typos in fieldInput type
67
+
68
+ The `type` value is case-sensitive: `autocomplete` not `Autocomplete`; `select` not `Select`.
69
+ Run `crouton validate` — it reports unknown fieldInput types.
70
+
71
+ ## 8. Missing `crouton validate` after edits
72
+
73
+ Schema violations, unknown resources in relations, and broken references are only caught by the validator.
74
+ Always run `crouton validate` after any change to `resource.json` or `crouton.json`.
75
+
76
+ ## 9. Wrong relation direction
77
+
78
+ `manyToOne`: FK is on **this** resource. `oneToMany`: FK is on the **target** resource.
79
+ Swapping them causes the relation panel to query the wrong table.
80
+
81
+ ## 10. `colspan`/`rowspan`/`hideLabel` on table items
82
+
83
+ These form-layout options have no effect on table items and are rejected with a validation error.
84
+ Remove them from `layout.table` items — use `width`, `align`, `truncate` for table-specific display.
85
+
86
+ ## 11. Table/column settings on the wrapper instead of the table node
87
+
88
+ In a wrapper table layout, `rowActions`, `tableActions`, `searchFields`, `filterFields`, `pageSize` etc.
89
+ belong on the **`type: "table"` node**, not on the outer wrapper.
@@ -0,0 +1,47 @@
1
+ # Data sources — decision table
2
+
3
+ ## Prisma-backed (default)
4
+
5
+ Use when: data lives in the relational DB and a Prisma model exists.
6
+
7
+ `datasource.json`:
8
+ ```json
9
+ {
10
+ "name": "default",
11
+ "prismaSchema": "prisma/schema.prisma",
12
+ "prismaConfig": "prisma/prisma.config.ts",
13
+ "urlEnv": "DATABASE_URL",
14
+ "zodOutput": "generated/default/types/src",
15
+ "clientOutput": "generated/default/client",
16
+ "default": true
17
+ }
18
+ ```
19
+
20
+ - `crouton update` introspects this datasource and manages column lists.
21
+ - Resources reference it implicitly when `kind` is `"default"`.
22
+
23
+ ## Custom adapter
24
+
25
+ Use when: data comes from an external API, file, or non-Prisma store.
26
+
27
+ `datasource.json`:
28
+ ```json
29
+ {
30
+ "name": "external-api",
31
+ "adapter": "custom"
32
+ }
33
+ ```
34
+
35
+ - `crouton update` skips Prisma introspection for this datasource.
36
+ - Resources using it must supply a `repository.ts` that implements the crouton repository interface.
37
+ - Set `kind: "custom"` in `resource.json` and point `datasource` to this name.
38
+
39
+ ## kind vs adapter
40
+
41
+ | Scenario | resource.json `kind` | datasource.json `adapter` |
42
+ |----------|----------------------|---------------------------|
43
+ | Prisma-backed resource | `"default"` (or omit) | omit (Prisma) |
44
+ | Config-only resource with custom repo | `"custom"` | omit (Prisma still used for other resources) |
45
+ | Fully external data source | `"custom"` | `"custom"` |
46
+
47
+ **Common mistake**: using `kind: "custom"` when you only need a custom adapter. If your data is in the DB but you want custom read logic, keep `kind: "default"` and override the repository at the NestJS module level.
@@ -0,0 +1,52 @@
1
+ # Field inputs — column type → input type
2
+
3
+ | Column type (Prisma) | Recommended fieldInput type | Notes |
4
+ |---------------------|-----------------------------|-------|
5
+ | `String` | `string` | Default for text |
6
+ | `String` (long text) | `textarea` | Use when content is multi-line prose |
7
+ | `String` (rich text) | `markdown` | Renders a Markdown editor |
8
+ | `Int` / `Float` / `Decimal` | `number` | |
9
+ | `Boolean` | `toggle` | Renders a toggle switch |
10
+ | `DateTime` | `date` | Date picker; use `date-range` for range filters |
11
+ | `String[]` / `Int[]` | `array` | Multi-value field |
12
+ | Enum | `select` | Pass `options` from the enum values |
13
+ | FK column (relation) | `autocomplete` | Requires `resource` + `labelField` |
14
+ | PK / internal | `hidden` | Suppresses the field from forms |
15
+ | Computed / read-only | `string` + `readonly: true` | Shows value, disallows editing |
16
+
17
+ ## autocomplete options
18
+
19
+ ```json
20
+ {
21
+ "type": "autocomplete",
22
+ "resource": "author",
23
+ "labelField": "name"
24
+ }
25
+ ```
26
+
27
+ `resource` — the target resource name (folder name under `resourcesDir`).
28
+ `labelField` — the column from the target resource to show as the label.
29
+
30
+ ## select options
31
+
32
+ ```json
33
+ {
34
+ "type": "select",
35
+ "options": [
36
+ { "value": "draft", "label": "Draft" },
37
+ { "value": "published", "label": "Published" }
38
+ ]
39
+ }
40
+ ```
41
+
42
+ Options can also be sourced from `crouton.enums.json` via `enumKey`.
43
+
44
+ ## custom field input
45
+
46
+ Register a custom Vue component, then reference it:
47
+
48
+ ```json
49
+ { "type": "custom", "component": "MyCustomInput" }
50
+ ```
51
+
52
+ The component must be registered globally or via `useCrouton` before the form mounts.
@@ -0,0 +1,222 @@
1
+ # Recipes — common tasks
2
+
3
+ ## Add a resource for an existing Prisma model
4
+
5
+ 1. Confirm the model exists in `schema.prisma`.
6
+ 2. Run `crouton update` and select the model when prompted.
7
+ 3. Crouton writes `<resourcesDir>/<name>/resource.json`.
8
+ 4. Edit `resource.json`: set labels, choose fieldInput types, add relations.
9
+ 5. Run `crouton validate`.
10
+
11
+ Minimal resource:
12
+
13
+ ```json
14
+ {
15
+ "schemaVersion": 1,
16
+ "name": "book",
17
+ "title": "Books",
18
+ "source": { "model": "Book" },
19
+ "columns": {
20
+ "id": { "idField": true, "hiddenInForm": true },
21
+ "title": { "label": "Title" }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ## Add a manyToOne relation (autocomplete)
27
+
28
+ In `resource.json` of the resource holding the FK:
29
+
30
+ ```json
31
+ {
32
+ "columns": {
33
+ "authorId": {
34
+ "label": "Author",
35
+ "relation": {
36
+ "resource": "./author/resource.json",
37
+ "type": "manyToOne",
38
+ "foreignKey": "authorId",
39
+ "displayKey": "name"
40
+ },
41
+ "fieldInput": { "type": "autocomplete" }
42
+ }
43
+ },
44
+ "operations": {
45
+ "findAll": { "include": ["author"] },
46
+ "findOne": { "include": ["author"] }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Add a oneToMany relation (embedded panel)
52
+
53
+ In `resource.json` of the parent resource:
54
+
55
+ ```json
56
+ {
57
+ "columns": {
58
+ "reviews": {
59
+ "label": "Reviews",
60
+ "hiddenInTable": true,
61
+ "hiddenInForm": true,
62
+ "relation": {
63
+ "resource": "./review/resource.json",
64
+ "type": "oneToMany",
65
+ "foreignKey": "bookId",
66
+ "sort": "createdAt",
67
+ "sortDir": "desc"
68
+ },
69
+ "fieldInput": { "type": "relation" }
70
+ }
71
+ },
72
+ "operations": {
73
+ "findOne": { "include": ["reviews"] }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ## Add an enum select
79
+
80
+ 1. Add the enum to `crouton.enums.json` (or let `crouton update` create it).
81
+ 2. In `resource.json` use `enum` to reference it:
82
+
83
+ ```json
84
+ {
85
+ "columns": {
86
+ "status": {
87
+ "enum": "BookStatus",
88
+ "fieldInput": { "type": "select" }
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ Or use inline `values` for a local list (not shared):
95
+
96
+ ```json
97
+ {
98
+ "columns": {
99
+ "genre": {
100
+ "fieldInput": {
101
+ "type": "select",
102
+ "options": { "values": [
103
+ { "value": "fiction", "label": "Fiction" },
104
+ { "value": "non-fiction", "label": "Non-fiction" }
105
+ ]}
106
+ }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ Never set both `enum` and `fieldInput.options.values` — validation error.
113
+
114
+ ## Right-align numbers, newest-first, 50 per page
115
+
116
+ ```json
117
+ {
118
+ "layout": {
119
+ "table": {
120
+ "pageSize": 50,
121
+ "pageSizes": [20, 50, 100],
122
+ "items": [
123
+ { "id": "title", "sortable": true },
124
+ { "id": "price", "align": "right", "width": "xs", "sortable": true },
125
+ { "id": "createdAt", "sortable": true, "defaultSort": "desc" }
126
+ ]
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ `defaultSort: "desc"` sets newest-first as the initial sort direction.
133
+
134
+ ## Truncate long text in table
135
+
136
+ ```json
137
+ { "id": "description", "truncate": 3 }
138
+ ```
139
+
140
+ `truncate: 3` = max 3 lines before clamp. `truncate: true` = single-line ellipsis.
141
+
142
+ ## Pin a column sticky left
143
+
144
+ ```json
145
+ { "id": "name", "pin": "left", "width": "md" }
146
+ ```
147
+
148
+ ## Open view as page, form as large modal
149
+
150
+ ```json
151
+ {
152
+ "layout": {
153
+ "form": { "container": { "mode": "modal", "size": "xl" } },
154
+ "view": { "container": { "mode": "page" } }
155
+ }
156
+ }
157
+ ```
158
+
159
+ ## Load a relation only on detail, not in list
160
+
161
+ ```json
162
+ {
163
+ "operations": {
164
+ "findAll": { "include": [] },
165
+ "findOne": { "include": ["author", "chapters"] }
166
+ }
167
+ }
168
+ ```
169
+
170
+ ## Add a computed column (SQL subquery)
171
+
172
+ ```json
173
+ {
174
+ "columns": {
175
+ "chapterCount": {
176
+ "label": "Chapters",
177
+ "type": "number",
178
+ "computed": {
179
+ "sql": "SELECT COUNT(*) FROM chapter WHERE chapter.book_id = main.id"
180
+ }
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ Use `main` as the alias for the parent table row.
187
+
188
+ ## Set dropdown labels for this resource
189
+
190
+ Control how this resource's records appear in other resources' autocompletes:
191
+
192
+ ```json
193
+ {
194
+ "lookup": {
195
+ "labels": ["title", "authorId"],
196
+ "separator": " · "
197
+ }
198
+ }
199
+ ```
200
+
201
+ Replaces `showInLookup: true` on individual columns.
202
+
203
+ ## Custom field input + renderer registration
204
+
205
+ 1. Create a Vue component `MyInput.vue`.
206
+ 2. Register it via `useCrouton`:
207
+ ```ts
208
+ crouton.registerRenderer('MyInput', MyInput);
209
+ ```
210
+ 3. Reference in `resource.json`:
211
+ ```json
212
+ { "fieldInput": { "type": "custom", "component": "MyInput" } }
213
+ ```
214
+
215
+ ## Config-only resource (kind: custom)
216
+
217
+ Use when the resource has no Prisma model or uses fully custom data logic.
218
+
219
+ 1. Create `<resourcesDir>/<name>/resource.json` with `"kind": "custom"`.
220
+ 2. Create `<resourcesDir>/<name>/repository.ts` implementing the crouton repository interface.
221
+ 3. Register the repository in the NestJS module.
222
+ 4. Run `crouton validate`.