@fluentcommerce/ai-skills 0.1.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.
- package/LICENSE +21 -0
- package/README.md +622 -0
- package/bin/cli.mjs +1973 -0
- package/content/cli/agents/fluent-cli/agent.json +149 -0
- package/content/cli/agents/fluent-cli.md +132 -0
- package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
- package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
- package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
- package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
- package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
- package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
- package/content/cli/skills/fluent-connect/SKILL.md +886 -0
- package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
- package/content/cli/skills/fluent-profile/SKILL.md +180 -0
- package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
- package/content/dev/agents/fluent-dev/agent.json +88 -0
- package/content/dev/agents/fluent-dev.md +525 -0
- package/content/dev/reference-modules/catalog.json +4754 -0
- package/content/dev/skills/fluent-build/SKILL.md +192 -0
- package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
- package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
- package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
- package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
- package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
- package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
- package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
- package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
- package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
- package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
- package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
- package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
- package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
- package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
- package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
- package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
- package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
- package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
- package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
- package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
- package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
- package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
- package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
- package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
- package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
- package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
- package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
- package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
- package/content/mcp-extn/agents/fluent-mcp.md +69 -0
- package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
- package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
- package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
- package/content/rfl/agents/fluent-rfl.md +56 -0
- package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
- package/docs/CAPABILITY_MAP.md +77 -0
- package/docs/CLI_COVERAGE.md +47 -0
- package/docs/DEV_WORKFLOW.md +802 -0
- package/docs/FLOW_RUN.md +142 -0
- package/docs/USE_CASES.md +404 -0
- package/metadata.json +156 -0
- package/package.json +51 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fluent-data-module-scaffold
|
|
3
|
+
description: Scaffold a new Fluent Commerce data module (no Java plugins). Generates module structure with assets directories, module.json, config template, and build script. Triggers on "create data module", "scaffold data module", "new data module", "data module".
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
|
|
6
|
+
argument-hint: <module-name> [--profile <profile>]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Data Module Scaffolder
|
|
10
|
+
|
|
11
|
+
Generate a complete Fluent Commerce data module skeleton with no Java plugins, no Maven, and no compilation step. Data modules package infrastructure data (locations, networks, carriers), product catalogues, settings, workflows, and other JSON/CSV assets into a deployable module ZIP.
|
|
12
|
+
|
|
13
|
+
## Planning Gate
|
|
14
|
+
|
|
15
|
+
**Before scaffolding, write a plan and get approval.** This is mandatory for all non-trivial module creation.
|
|
16
|
+
|
|
17
|
+
**Plan file path:** `accounts/<PROFILE>/plans/<YYYY-MM-DD>-data-module-scaffold-<slug>.md`
|
|
18
|
+
|
|
19
|
+
The plan should cover:
|
|
20
|
+
1. **Purpose** -- what data this module will contain and why it is a separate module
|
|
21
|
+
2. **Asset inventory** -- which asset directories will be populated and with what content
|
|
22
|
+
3. **Config variables** -- what `[[variable]]` tokens will be used and their expected values
|
|
23
|
+
4. **Dependencies** -- other modules that must be installed first or after
|
|
24
|
+
5. **Target retailer(s)** -- which retailer(s) this data is scoped to
|
|
25
|
+
|
|
26
|
+
Write the plan file, present it to the user, and wait for explicit approval ("yes", "go ahead", "approved", "do it") before generating any files. On approval, update the plan status to `APPROVED`.
|
|
27
|
+
|
|
28
|
+
**Skip the gate** when: the user says "just do it" or "skip planning", or a pre-approved plan already exists in `accounts/<PROFILE>/plans/`.
|
|
29
|
+
|
|
30
|
+
## When to Use
|
|
31
|
+
|
|
32
|
+
- Infrastructure data: locations, networks, carriers, storage areas
|
|
33
|
+
- Product catalogues, categories, and product data
|
|
34
|
+
- Inventory catalogue and virtual catalogue definitions
|
|
35
|
+
- Settings bundles (retailer-scoped or account-scoped)
|
|
36
|
+
- Workflow-only modules (deploying workflow JSON without custom rules)
|
|
37
|
+
- Sample or test data for development and QA environments
|
|
38
|
+
- Any module that contains only JSON/CSV assets and no Java plugins
|
|
39
|
+
|
|
40
|
+
## When NOT to Use
|
|
41
|
+
|
|
42
|
+
- If you need custom Java orchestration rules, use `/fluent-module-scaffold` instead
|
|
43
|
+
- If you need to add rules to an existing module, use `/fluent-rule-scaffold`
|
|
44
|
+
- If you are building an extension module with Maven/OSGi plugins, use `/fluent-module-scaffold`
|
|
45
|
+
|
|
46
|
+
## Ownership Boundary
|
|
47
|
+
|
|
48
|
+
This skill owns:
|
|
49
|
+
- Creating the data module directory structure
|
|
50
|
+
- Generating `module.json` (no rules section)
|
|
51
|
+
- Generating `module.config.json` template
|
|
52
|
+
- Creating empty asset directories
|
|
53
|
+
- Generating build scripts (bash `.sh` and PowerShell `.ps1`) for ZIP packaging
|
|
54
|
+
- Providing asset JSON schema examples
|
|
55
|
+
|
|
56
|
+
This skill does **not** own:
|
|
57
|
+
- Java compilation or Maven builds --> `/fluent-module-scaffold`
|
|
58
|
+
- Adding rules to any module --> `/fluent-rule-scaffold`
|
|
59
|
+
- Building extension modules with plugins --> `/fluent-build`
|
|
60
|
+
- Deploying modules --> `/fluent-module-deploy`
|
|
61
|
+
- Validating module structure --> `/fluent-module-validate`
|
|
62
|
+
|
|
63
|
+
## Inputs
|
|
64
|
+
|
|
65
|
+
| Parameter | Required | Default | Description |
|
|
66
|
+
|-----------|----------|---------|-------------|
|
|
67
|
+
| `module-name` | Yes | -- | Module identifier (e.g., `hm-infra-data`). Lowercase alphanumeric with hyphens only. Used for directory name and `module.json` name field. |
|
|
68
|
+
| `--description` | No | Auto-generated | Module description for `module.json` |
|
|
69
|
+
| `--account-prefix` | No | Auto-detect from profile | Account/company prefix for the module name in `module.json` (e.g., `hm`, `acme`) |
|
|
70
|
+
| `--profile` | No | Active profile | Fluent CLI profile for path resolution and prefix detection |
|
|
71
|
+
| `--initial-version` | No | `1.0.0` | Initial version in `module.json` |
|
|
72
|
+
|
|
73
|
+
## Pre-Check: Module Already Exists?
|
|
74
|
+
|
|
75
|
+
Before creating anything, check if the target directory already exists:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
accounts/<PROFILE>/SOURCE/<module-name>/
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
If it exists, **abort** with message:
|
|
82
|
+
```
|
|
83
|
+
Module directory already exists: accounts/<PROFILE>/SOURCE/<module-name>/
|
|
84
|
+
Choose a different module name or delete the existing directory first.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Also check for a Maven-based module with the same name:
|
|
88
|
+
```
|
|
89
|
+
accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If found, warn that an extension module with this name already exists.
|
|
93
|
+
|
|
94
|
+
## Account Prefix Detection
|
|
95
|
+
|
|
96
|
+
The account prefix appears in the `module.json` `name` field as `<prefix>/<module-name>`. Detection order:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
1. If --account-prefix provided, use it directly
|
|
100
|
+
|
|
101
|
+
2. Else check existing modules under accounts/<PROFILE>/SOURCE/
|
|
102
|
+
Read any module.json and extract the prefix from the "name" field
|
|
103
|
+
e.g., "name": "hm/infra-data" --> prefix is "hm"
|
|
104
|
+
|
|
105
|
+
3. Else derive from PROFILE name (lowercase)
|
|
106
|
+
HMDEV --> hmdev
|
|
107
|
+
SAGIRISH --> sagirish
|
|
108
|
+
|
|
109
|
+
4. Fallback: "my-company"
|
|
110
|
+
Warn user: "Could not detect account prefix. Update module.json name field manually."
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Generated Directory Structure
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
accounts/<PROFILE>/SOURCE/<module-name>/
|
|
117
|
+
+-- resources/
|
|
118
|
+
| +-- module.json # Module manifest (no rules)
|
|
119
|
+
| +-- module.config.json # Variable template
|
|
120
|
+
| +-- assets/
|
|
121
|
+
| +-- locations/ # Location JSON files
|
|
122
|
+
| +-- networks/ # Network JSON files
|
|
123
|
+
| +-- carriers/ # Carrier JSON files
|
|
124
|
+
| +-- categories/ # Category JSON files
|
|
125
|
+
| +-- products/ # Product JSON files
|
|
126
|
+
| +-- inventory-catalogues/ # Inventory catalogue definitions
|
|
127
|
+
| +-- product-catalogues/ # Product catalogue definitions
|
|
128
|
+
| +-- virtual-catalogues/ # Virtual catalogue definitions
|
|
129
|
+
| +-- settings/ # Settings JSON files
|
|
130
|
+
| +-- workflows/ # Workflow JSON files
|
|
131
|
+
| +-- workflow-fragments/ # Workflow fragment groups
|
|
132
|
+
| | +-- <workflow-name>/ # One subdirectory per workflow
|
|
133
|
+
| +-- controls/ # Control group JSON files
|
|
134
|
+
| +-- storage-areas/ # Storage area definitions
|
|
135
|
+
| +-- users/ # User JSON files
|
|
136
|
+
+-- scripts/
|
|
137
|
+
| +-- build-module.sh # Bash build script (ZIP packaging)
|
|
138
|
+
| +-- build-module.ps1 # PowerShell build script (ZIP packaging)
|
|
139
|
+
+-- dist/ # Output directory for built ZIPs
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Key difference from extension modules:** No `plugins/` directory, no `pom.xml`, no `global/` directory, no `.gitignore` for Maven artifacts. The entire module is just `resources/` + `scripts/`.
|
|
143
|
+
|
|
144
|
+
## File Templates
|
|
145
|
+
|
|
146
|
+
### Module Manifest (`resources/module.json`)
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"_schema": "1.0.0",
|
|
151
|
+
"name": "${ACCOUNT_PREFIX}/${MODULE_NAME}",
|
|
152
|
+
"version": "${INITIAL_VERSION}",
|
|
153
|
+
"title": "${MODULE_TITLE}",
|
|
154
|
+
"description": "${MODULE_DESCRIPTION}",
|
|
155
|
+
"authors": [
|
|
156
|
+
{
|
|
157
|
+
"name": "Implementation Team"
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"dependencies": [
|
|
161
|
+
{
|
|
162
|
+
"name": "fluent-commerce/core",
|
|
163
|
+
"type": "module",
|
|
164
|
+
"version": "2.x.x"
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"contracts": []
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Important:** Data modules have NO `modules[].provides` section and NO `rules` array. There are no Java plugins to register.
|
|
172
|
+
|
|
173
|
+
### Config Template (`resources/module.config.json`)
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"default:carrier.ref": "",
|
|
178
|
+
"default:network.ref": "",
|
|
179
|
+
"default:catalogue.ref": "",
|
|
180
|
+
"workflow:order:hd:network.ref": "",
|
|
181
|
+
"workflow:order:cc:network.ref": "",
|
|
182
|
+
"setting:api.timeout": "30000"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Populate this template with keys relevant to the module's asset files. Keys that are not needed can be removed. An empty `{}` is valid if no variables are used.
|
|
187
|
+
|
|
188
|
+
### Config Prefix System
|
|
189
|
+
|
|
190
|
+
Config keys in `module.config.json` use a prefix system to control variable scope:
|
|
191
|
+
|
|
192
|
+
| Prefix | Scope | Example |
|
|
193
|
+
|--------|-------|---------|
|
|
194
|
+
| `default:` | Applied to all assets | `default:carrier.ref` |
|
|
195
|
+
| `workflow:<type>:<subtype>:` | Applied only to matching workflow assets | `workflow:order:hd:network.ref` |
|
|
196
|
+
| `setting:` | Applied only to settings assets | `setting:api.timeout` |
|
|
197
|
+
| *(none)* | Global scope | `carrier.ref` |
|
|
198
|
+
|
|
199
|
+
**Specificity rule:** More specific prefixes (more colons) take priority over less specific ones. For example, `workflow:order:hd:network.ref` overrides `default:network.ref` for the ORDER::HD workflow.
|
|
200
|
+
|
|
201
|
+
**Auto-injected variables** (available without config): `account.id`, `retailer.id`, `retailer.ref`, `retailer.name`.
|
|
202
|
+
|
|
203
|
+
### Variable Syntax
|
|
204
|
+
|
|
205
|
+
Use `[[variable]]` tokens inside asset JSON files. The Fluent CLI resolves these at install time using the config file values.
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"ref": "NET_HD_1",
|
|
210
|
+
"name": "HD Network",
|
|
211
|
+
"type": "FulfilmentNetwork",
|
|
212
|
+
"defaultCarrier": "[[carrier.ref]]"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Unresolved tokens are left as-is in the installed data. Always verify that all tokens have corresponding config entries before deploying to production.
|
|
217
|
+
|
|
218
|
+
## Asset JSON Schema Examples
|
|
219
|
+
|
|
220
|
+
### Location (`resources/assets/locations/locations.json`)
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
[
|
|
224
|
+
{
|
|
225
|
+
"ref": "LOC_WH_1",
|
|
226
|
+
"name": "Warehouse 1",
|
|
227
|
+
"type": "WAREHOUSE",
|
|
228
|
+
"defaultCarrier": "[[carrier.ref]]",
|
|
229
|
+
"supportPhoneNumber": "+1-555-0100",
|
|
230
|
+
"attributes": [
|
|
231
|
+
{
|
|
232
|
+
"name": "storageCapacity",
|
|
233
|
+
"type": "STRING",
|
|
234
|
+
"value": "5000"
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"ref": "LOC_STORE_1",
|
|
240
|
+
"name": "Store 1",
|
|
241
|
+
"type": "STORE",
|
|
242
|
+
"defaultCarrier": "[[carrier.ref]]"
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Network (`resources/assets/networks/networks.json`)
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
[
|
|
251
|
+
{
|
|
252
|
+
"ref": "NET_HD_1",
|
|
253
|
+
"name": "HD Network",
|
|
254
|
+
"type": "FulfilmentNetwork",
|
|
255
|
+
"locationRefs": [
|
|
256
|
+
"LOC_WH_1"
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"ref": "NET_CC_1",
|
|
261
|
+
"name": "CC Network",
|
|
262
|
+
"type": "FulfilmentNetwork",
|
|
263
|
+
"locationRefs": [
|
|
264
|
+
"LOC_STORE_1"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Carrier (`resources/assets/carriers/carriers.json`)
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
[
|
|
274
|
+
{
|
|
275
|
+
"ref": "CARRIER_STD",
|
|
276
|
+
"name": "Standard Carrier"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"ref": "CARRIER_EXPRESS",
|
|
280
|
+
"name": "Express Carrier"
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Setting (`resources/assets/settings/settings.json`)
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
[
|
|
289
|
+
{
|
|
290
|
+
"name": "WEBHOOK_ORDER_URL",
|
|
291
|
+
"value": "https://example.com/webhook/order",
|
|
292
|
+
"context": "RETAILER"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"name": "ORDER_NOTES_ENABLED",
|
|
296
|
+
"value": "true",
|
|
297
|
+
"context": "RETAILER"
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"name": "FULFILMENT_BATCH_SIZE",
|
|
301
|
+
"value": "50",
|
|
302
|
+
"context": "ACCOUNT"
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Product Catalogue (`resources/assets/product-catalogues/product-catalogues.json`)
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
[
|
|
311
|
+
{
|
|
312
|
+
"ref": "PC:MASTER:[[retailer.id]]",
|
|
313
|
+
"name": "Master Product Catalogue",
|
|
314
|
+
"type": "DEFAULT",
|
|
315
|
+
"description": "Primary product catalogue"
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Inventory Catalogue (`resources/assets/inventory-catalogues/inventory-catalogues.json`)
|
|
321
|
+
|
|
322
|
+
```json
|
|
323
|
+
[
|
|
324
|
+
{
|
|
325
|
+
"ref": "DEFAULT:[[retailer.id]]",
|
|
326
|
+
"name": "Default Inventory Catalogue",
|
|
327
|
+
"type": "DEFAULT",
|
|
328
|
+
"description": "Primary inventory catalogue",
|
|
329
|
+
"retailerRefs": [
|
|
330
|
+
"[[retailer.ref]]"
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
]
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Virtual Catalogue (`resources/assets/virtual-catalogues/virtual-catalogues.json`)
|
|
337
|
+
|
|
338
|
+
```json
|
|
339
|
+
[
|
|
340
|
+
{
|
|
341
|
+
"ref": "VC:DEFAULT:[[retailer.id]]",
|
|
342
|
+
"name": "Default Virtual Catalogue",
|
|
343
|
+
"type": "DEFAULT",
|
|
344
|
+
"inventoryCatalogueRef": "DEFAULT:[[retailer.id]]",
|
|
345
|
+
"productCatalogueRef": "PC:MASTER:[[retailer.id]]",
|
|
346
|
+
"networkRef": "[[network.ref]]"
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Build Script Templates
|
|
352
|
+
|
|
353
|
+
Data module build scripts are simpler than extension module scripts -- no Maven, no Java compilation. They read the version from `module.json` and ZIP the `resources/` directory.
|
|
354
|
+
|
|
355
|
+
### Bash Build Script (`scripts/build-module.sh`)
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
#!/bin/bash
|
|
359
|
+
set -euo pipefail
|
|
360
|
+
|
|
361
|
+
VERBOSE=false
|
|
362
|
+
|
|
363
|
+
parse_options() {
|
|
364
|
+
while getopts "x" opt; do
|
|
365
|
+
case $opt in
|
|
366
|
+
x) VERBOSE=true ;;
|
|
367
|
+
*)
|
|
368
|
+
echo "Usage: $0 [-x]"
|
|
369
|
+
echo " -x Enable verbose mode"
|
|
370
|
+
exit 1
|
|
371
|
+
;;
|
|
372
|
+
esac
|
|
373
|
+
done
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
log() {
|
|
377
|
+
if [ "$VERBOSE" = true ]; then
|
|
378
|
+
echo "$@"
|
|
379
|
+
fi
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
require_command() {
|
|
383
|
+
local command_name=$1
|
|
384
|
+
if ! command -v "$command_name" >/dev/null 2>&1; then
|
|
385
|
+
echo "Error: required command '$command_name' was not found in PATH"
|
|
386
|
+
exit 1
|
|
387
|
+
fi
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
clean_folder() {
|
|
391
|
+
local dist_dir=$1
|
|
392
|
+
if [ -d "$dist_dir" ]; then
|
|
393
|
+
log "Directory $dist_dir exists, clearing..."
|
|
394
|
+
rm -rf "${dist_dir:?}/"
|
|
395
|
+
fi
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
main() {
|
|
399
|
+
parse_options "$@"
|
|
400
|
+
require_command zip
|
|
401
|
+
require_command sed
|
|
402
|
+
|
|
403
|
+
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
|
404
|
+
MODULE_BASE_DIR="$SCRIPT_DIR/.."
|
|
405
|
+
DIST_DIR="$MODULE_BASE_DIR/dist"
|
|
406
|
+
RESOURCES_DIR="$MODULE_BASE_DIR/resources"
|
|
407
|
+
|
|
408
|
+
log "Script Directory: $SCRIPT_DIR"
|
|
409
|
+
log "Module Directory: $MODULE_BASE_DIR"
|
|
410
|
+
|
|
411
|
+
if [ ! -f "$RESOURCES_DIR/module.json" ]; then
|
|
412
|
+
echo "Error: resources/module.json not found"
|
|
413
|
+
exit 1
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
module_name=$(sed -n 's/.*"name": *"\([^"]*\)".*/\1/p' "$RESOURCES_DIR/module.json" | head -n 1)
|
|
417
|
+
sanitized_name=$(echo "$module_name" | sed 's|/|-|g')
|
|
418
|
+
module_version=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$RESOURCES_DIR/module.json" | head -n 1)
|
|
419
|
+
combined_name="$sanitized_name-$module_version"
|
|
420
|
+
|
|
421
|
+
echo "Building data module: $module_name v$module_version"
|
|
422
|
+
|
|
423
|
+
clean_folder "$DIST_DIR"
|
|
424
|
+
|
|
425
|
+
local module_dist_dir="$DIST_DIR/$combined_name"
|
|
426
|
+
local module_dist_assets_dir="$module_dist_dir/assets"
|
|
427
|
+
mkdir -p "$module_dist_dir"
|
|
428
|
+
|
|
429
|
+
# Copy module.json
|
|
430
|
+
cp "$RESOURCES_DIR/module.json" "$module_dist_dir/"
|
|
431
|
+
|
|
432
|
+
# Copy module.config.json if present
|
|
433
|
+
if [ -f "$RESOURCES_DIR/module.config.json" ]; then
|
|
434
|
+
cp "$RESOURCES_DIR/module.config.json" "$module_dist_dir/"
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# Copy all asset directories
|
|
438
|
+
if [ -d "$RESOURCES_DIR/assets" ]; then
|
|
439
|
+
mkdir -p "$module_dist_assets_dir"
|
|
440
|
+
for entry in "$RESOURCES_DIR/assets"/*; do
|
|
441
|
+
[ -e "$entry" ] || continue
|
|
442
|
+
cp -r "$entry" "$module_dist_assets_dir/"
|
|
443
|
+
done
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
# Create ZIP
|
|
447
|
+
local zip_file="$combined_name.zip"
|
|
448
|
+
(cd "$module_dist_dir" && zip -r "$zip_file" . -q)
|
|
449
|
+
mv "$module_dist_dir/$zip_file" "$DIST_DIR/"
|
|
450
|
+
|
|
451
|
+
echo "Built: dist/$zip_file"
|
|
452
|
+
echo "Install with: fluent module install ./ -p <PROFILE> -r <RETAILER>"
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
main "$@"
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### PowerShell Build Script (`scripts/build-module.ps1`)
|
|
459
|
+
|
|
460
|
+
```powershell
|
|
461
|
+
#!/usr/bin/env pwsh
|
|
462
|
+
|
|
463
|
+
<#
|
|
464
|
+
.SYNOPSIS
|
|
465
|
+
Build a Fluent Commerce data module (no Java, ZIP packaging only)
|
|
466
|
+
|
|
467
|
+
.DESCRIPTION
|
|
468
|
+
Reads version from module.json, copies resources into a staging directory,
|
|
469
|
+
and creates a distributable ZIP file in dist/.
|
|
470
|
+
|
|
471
|
+
.PARAMETER VerboseLogging
|
|
472
|
+
Enable verbose logging
|
|
473
|
+
|
|
474
|
+
.EXAMPLE
|
|
475
|
+
.\build-module.ps1
|
|
476
|
+
|
|
477
|
+
.EXAMPLE
|
|
478
|
+
.\build-module.ps1 -VerboseLogging
|
|
479
|
+
#>
|
|
480
|
+
|
|
481
|
+
[CmdletBinding()]
|
|
482
|
+
param(
|
|
483
|
+
[Alias('x')]
|
|
484
|
+
[switch]$VerboseLogging
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
$Script:VerboseMode = $VerboseLogging
|
|
488
|
+
|
|
489
|
+
function Write-Log {
|
|
490
|
+
param([string]$Message)
|
|
491
|
+
if ($Script:VerboseMode) {
|
|
492
|
+
Write-Host $Message
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function Remove-FolderIfExists {
|
|
497
|
+
param([string]$Path)
|
|
498
|
+
if (Test-Path $Path) {
|
|
499
|
+
Write-Log "Directory $Path exists, clearing..."
|
|
500
|
+
Remove-Item -Path $Path -Recurse -Force
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function Get-JsonValue {
|
|
505
|
+
param(
|
|
506
|
+
[string]$FilePath,
|
|
507
|
+
[string]$PropertyName
|
|
508
|
+
)
|
|
509
|
+
if (-not (Test-Path $FilePath)) { return $null }
|
|
510
|
+
try {
|
|
511
|
+
$json = Get-Content $FilePath -Raw | ConvertFrom-Json
|
|
512
|
+
return $json.$PropertyName
|
|
513
|
+
} catch {
|
|
514
|
+
Write-Log "Failed to parse JSON from $FilePath"
|
|
515
|
+
return $null
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function Main {
|
|
520
|
+
if ($PSScriptRoot) {
|
|
521
|
+
$scriptDir = $PSScriptRoot
|
|
522
|
+
} elseif ($MyInvocation.MyCommand.Path) {
|
|
523
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
524
|
+
} else {
|
|
525
|
+
$scriptDir = (Get-Location).Path
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
$moduleBaseDir = Split-Path -Parent $scriptDir
|
|
529
|
+
$distDir = Join-Path $moduleBaseDir "dist"
|
|
530
|
+
$resourcesDir = Join-Path $moduleBaseDir "resources"
|
|
531
|
+
|
|
532
|
+
$moduleJsonPath = Join-Path $resourcesDir "module.json"
|
|
533
|
+
if (-not (Test-Path $moduleJsonPath)) {
|
|
534
|
+
Write-Error "resources/module.json not found"
|
|
535
|
+
exit 1
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
$moduleName = Get-JsonValue $moduleJsonPath "name"
|
|
539
|
+
$moduleVersion = Get-JsonValue $moduleJsonPath "version"
|
|
540
|
+
if (-not $moduleName -or -not $moduleVersion) {
|
|
541
|
+
Write-Error "Could not extract module name or version from module.json"
|
|
542
|
+
exit 1
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
$sanitizedName = $moduleName -replace "/", "-"
|
|
546
|
+
$combinedName = "$sanitizedName-$moduleVersion"
|
|
547
|
+
|
|
548
|
+
Write-Host "Building data module: $moduleName v$moduleVersion"
|
|
549
|
+
|
|
550
|
+
Remove-FolderIfExists $distDir
|
|
551
|
+
|
|
552
|
+
$moduleDistDir = Join-Path $distDir $combinedName
|
|
553
|
+
$moduleDistAssetsDir = Join-Path $moduleDistDir "assets"
|
|
554
|
+
New-Item -ItemType Directory -Path $moduleDistDir -Force | Out-Null
|
|
555
|
+
|
|
556
|
+
# Copy module.json
|
|
557
|
+
Copy-Item $moduleJsonPath $moduleDistDir
|
|
558
|
+
|
|
559
|
+
# Copy module.config.json if present
|
|
560
|
+
$configPath = Join-Path $resourcesDir "module.config.json"
|
|
561
|
+
if (Test-Path $configPath) {
|
|
562
|
+
Copy-Item $configPath $moduleDistDir
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
# Copy all asset directories
|
|
566
|
+
$assetsDir = Join-Path $resourcesDir "assets"
|
|
567
|
+
if (Test-Path $assetsDir) {
|
|
568
|
+
New-Item -ItemType Directory -Path $moduleDistAssetsDir -Force | Out-Null
|
|
569
|
+
Get-ChildItem -Path $assetsDir | ForEach-Object {
|
|
570
|
+
Copy-Item -Path $_.FullName -Destination $moduleDistAssetsDir -Recurse -Force
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
# Create ZIP
|
|
575
|
+
$zipPath = Join-Path $distDir "$combinedName.zip"
|
|
576
|
+
Compress-Archive -Path "$moduleDistDir\*" -DestinationPath $zipPath -Force
|
|
577
|
+
|
|
578
|
+
Write-Host "Built: dist/$combinedName.zip"
|
|
579
|
+
Write-Host "Install with: fluent module install ./ -p <PROFILE> -r <RETAILER>"
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
Main
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Deployment
|
|
586
|
+
|
|
587
|
+
After building, install the module using the Fluent CLI:
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
# Basic install (no config variables)
|
|
591
|
+
fluent module install ./<module-name>/ -p <PROFILE> -r <RETAILER>
|
|
592
|
+
|
|
593
|
+
# Install with config file (resolves [[variable]] tokens)
|
|
594
|
+
fluent module install ./<module-name>/ -p <PROFILE> -r <RETAILER> --config module.config.<RETAILER>.<module>.json
|
|
595
|
+
|
|
596
|
+
# Generate a retailer-specific config from the template
|
|
597
|
+
fluent module config <module-name> -p <PROFILE> -r <RETAILER>
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Install order matters.** Infrastructure data (locations, networks, carriers) must be installed before modules that reference them (orders, fulfilments). A typical sequence:
|
|
601
|
+
|
|
602
|
+
```
|
|
603
|
+
1. Core module (fluent-commerce/core) -- usually pre-installed
|
|
604
|
+
2. Infrastructure data module -- locations, networks, carriers
|
|
605
|
+
3. Order/fulfilment workflow modules -- reference network and location refs
|
|
606
|
+
4. Product and inventory data modules -- catalogues and stock
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Scaffold Execution Protocol
|
|
610
|
+
|
|
611
|
+
Follow these steps in order when scaffolding a new data module:
|
|
612
|
+
|
|
613
|
+
```
|
|
614
|
+
1. VALIDATE inputs
|
|
615
|
+
a. module-name: must match /^[a-z][a-z0-9-]*$/ (lowercase, hyphens, no leading hyphen)
|
|
616
|
+
b. module-name length: 1-50 characters
|
|
617
|
+
c. Reject names starting with digits or containing spaces/special characters
|
|
618
|
+
|
|
619
|
+
2. PRE-CHECK: Module already exists?
|
|
620
|
+
a. Check accounts/<PROFILE>/SOURCE/<module-name>/ exists
|
|
621
|
+
b. Check accounts/<PROFILE>/SOURCE/fluentcommerce-fc-module-<module-name>/ exists
|
|
622
|
+
c. If either exists --> ABORT with guidance
|
|
623
|
+
|
|
624
|
+
3. DETECT account prefix
|
|
625
|
+
a. If --account-prefix provided --> use it
|
|
626
|
+
b. Else scan existing modules under accounts/<PROFILE>/SOURCE/*/resources/module.json
|
|
627
|
+
Extract prefix from "name" field (part before the slash)
|
|
628
|
+
c. Else derive from PROFILE name (lowercase)
|
|
629
|
+
d. Fallback: "my-company" with warning
|
|
630
|
+
|
|
631
|
+
4. COMPUTE derived values
|
|
632
|
+
a. MODULE_TITLE = title-case of module-name with hyphens as spaces
|
|
633
|
+
(e.g., "hm-infra-data" --> "Hm Infra Data")
|
|
634
|
+
b. MODULE_DESCRIPTION = description from --description or auto-generated
|
|
635
|
+
(e.g., "Data module for Hm Infra Data")
|
|
636
|
+
|
|
637
|
+
5. CREATE directory structure
|
|
638
|
+
a. Create the root: accounts/<PROFILE>/SOURCE/<module-name>/
|
|
639
|
+
b. Create resources/ and resources/assets/ with all subdirectories
|
|
640
|
+
c. Create scripts/ and dist/
|
|
641
|
+
|
|
642
|
+
6. GENERATE files (use Write tool, not bash echo)
|
|
643
|
+
a. resources/module.json -- with substituted values
|
|
644
|
+
b. resources/module.config.json -- empty template {}
|
|
645
|
+
c. scripts/build-module.sh -- bash ZIP script
|
|
646
|
+
d. scripts/build-module.ps1 -- PowerShell ZIP script
|
|
647
|
+
|
|
648
|
+
7. MAKE build script executable
|
|
649
|
+
bash: chmod +x scripts/build-module.sh
|
|
650
|
+
|
|
651
|
+
8. REPORT generated files
|
|
652
|
+
List all files and directories created.
|
|
653
|
+
Print next steps:
|
|
654
|
+
- "Data module scaffolded at: accounts/<PROFILE>/SOURCE/<module-name>/"
|
|
655
|
+
- "Next: Add asset JSON files under resources/assets/<type>/"
|
|
656
|
+
- "Build: cd accounts/<PROFILE>/SOURCE/<module-name> && bash scripts/build-module.sh"
|
|
657
|
+
- "Deploy: fluent module install ./<module-name>/ -p <PROFILE> -r <RETAILER>"
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
## Integration with Other Skills
|
|
661
|
+
|
|
662
|
+
| Skill | Relationship |
|
|
663
|
+
|-------|-------------|
|
|
664
|
+
| `/fluent-module-scaffold` | For extension modules with Java plugins. If you realize you need custom rules, scaffold an extension module instead. |
|
|
665
|
+
| `/fluent-module-validate` | Validates the data module structure. Should pass with 0 FAILs on a freshly scaffolded module. |
|
|
666
|
+
| `/fluent-module-deploy` | Deploys the built ZIP to a target retailer. |
|
|
667
|
+
| `/fluent-settings` | For managing settings via MCP tools. Data module settings are an alternative approach using the module install pipeline. |
|
|
668
|
+
| `/fluent-retailer-config` | For creating locations, networks, and carriers via GraphQL mutations. Data modules are the alternative file-based approach. |
|
|
669
|
+
| `/fluent-workflow-builder` | Workflows can be packaged in a data module under `assets/workflows/` for deployment via `fluent module install`. |
|
|
670
|
+
| `/fluent-build` | Extension module build skill. Not needed for data modules -- use the included `scripts/build-module.sh` instead. |
|
|
671
|
+
|
|
672
|
+
## Edge Cases
|
|
673
|
+
|
|
674
|
+
### Empty module (no assets yet)
|
|
675
|
+
|
|
676
|
+
A scaffolded module with empty asset directories is valid. The build script will produce a ZIP containing only `module.json` and empty `assets/` directories. This is useful as a starting point -- add JSON files incrementally.
|
|
677
|
+
|
|
678
|
+
### Module name conflicts
|
|
679
|
+
|
|
680
|
+
Check both data module and extension module naming patterns:
|
|
681
|
+
```bash
|
|
682
|
+
ls accounts/<PROFILE>/SOURCE/ | grep -i "<module-name>"
|
|
683
|
+
```
|
|
684
|
+
If found, abort. Do NOT overwrite an existing module.
|
|
685
|
+
|
|
686
|
+
### Windows path handling
|
|
687
|
+
|
|
688
|
+
- Build scripts have both `.sh` (Git Bash, WSL, macOS, Linux) and `.ps1` (PowerShell) variants
|
|
689
|
+
- Asset directory names use hyphens, not colons (e.g., `inventory-catalogues`, not `inventory::catalogues`)
|
|
690
|
+
- Workflow fragment subdirectories should use Windows-safe names (e.g., `ORDER-HD` instead of `ORDER::HD`)
|
|
691
|
+
|
|
692
|
+
### Large asset files
|
|
693
|
+
|
|
694
|
+
For large datasets (thousands of products, inventory positions):
|
|
695
|
+
- Split into multiple JSON files within the same asset directory
|
|
696
|
+
- The Fluent CLI processes all files in each asset directory
|
|
697
|
+
- Consider using CSV format for inventory data (`inventory.csv` instead of `inventory.json`)
|
|
698
|
+
|
|
699
|
+
### Config variable not resolved
|
|
700
|
+
|
|
701
|
+
If a `[[variable]]` token has no matching key in the config file:
|
|
702
|
+
- The token is left as-is in the installed data (e.g., the literal string `[[carrier.ref]]`)
|
|
703
|
+
- This will likely cause errors in the Fluent environment
|
|
704
|
+
- Always run `fluent module config <module> -p <PROFILE> -r <RETAILER>` to generate and fill the config before production installs
|
|
705
|
+
|
|
706
|
+
## Gotchas
|
|
707
|
+
|
|
708
|
+
- **`module.config.json` is required** even if empty. The Fluent CLI expects this file to exist. Use `{}` for modules with no variables.
|
|
709
|
+
- **No `rules` array in `module.json`** for data modules. Including an empty `"rules": []` is harmless but unnecessary. Including rule names that do not exist will cause install warnings.
|
|
710
|
+
- **Asset directory names are convention-driven.** The Fluent CLI maps directory names to entity types. Using a non-standard directory name (e.g., `locs/` instead of `locations/`) will cause the assets to be skipped silently.
|
|
711
|
+
- **Install order matters.** Locations must exist before networks can reference them. Networks must exist before workflows can reference them. Install infrastructure data before order/fulfilment modules.
|
|
712
|
+
- **Workflow fragments** go in `workflow-fragments/<group>/`, not `workflows/`. Fragments are partial workflow definitions that merge into existing workflows. Full workflow JSONs go in `workflows/`.
|
|
713
|
+
- **The `_schema` field** in `module.json` must be `"1.0.0"`. This is a Fluent CLI requirement and not a user-defined version.
|
|
714
|
+
- **Config key auto-injection:** `account.id`, `retailer.id`, `retailer.ref`, and `retailer.name` are always available as `[[account.id]]`, `[[retailer.id]]`, etc. without needing config entries. Do not duplicate these in `module.config.json`.
|