@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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +622 -0
  3. package/bin/cli.mjs +1973 -0
  4. package/content/cli/agents/fluent-cli/agent.json +149 -0
  5. package/content/cli/agents/fluent-cli.md +132 -0
  6. package/content/cli/skills/fluent-bootstrap/SKILL.md +181 -0
  7. package/content/cli/skills/fluent-cli-index/SKILL.md +63 -0
  8. package/content/cli/skills/fluent-cli-mcp-cicd/SKILL.md +77 -0
  9. package/content/cli/skills/fluent-cli-reference/SKILL.md +1031 -0
  10. package/content/cli/skills/fluent-cli-retailer/SKILL.md +85 -0
  11. package/content/cli/skills/fluent-cli-settings/SKILL.md +106 -0
  12. package/content/cli/skills/fluent-connect/SKILL.md +886 -0
  13. package/content/cli/skills/fluent-module-deploy/SKILL.md +349 -0
  14. package/content/cli/skills/fluent-profile/SKILL.md +180 -0
  15. package/content/cli/skills/fluent-workflow/SKILL.md +310 -0
  16. package/content/dev/agents/fluent-dev/agent.json +88 -0
  17. package/content/dev/agents/fluent-dev.md +525 -0
  18. package/content/dev/reference-modules/catalog.json +4754 -0
  19. package/content/dev/skills/fluent-build/SKILL.md +192 -0
  20. package/content/dev/skills/fluent-connection-analysis/SKILL.md +386 -0
  21. package/content/dev/skills/fluent-custom-code/SKILL.md +895 -0
  22. package/content/dev/skills/fluent-data-module-scaffold/SKILL.md +714 -0
  23. package/content/dev/skills/fluent-e2e-test/SKILL.md +394 -0
  24. package/content/dev/skills/fluent-event-api/SKILL.md +945 -0
  25. package/content/dev/skills/fluent-feature-explain/SKILL.md +603 -0
  26. package/content/dev/skills/fluent-feature-plan/PLAN_TEMPLATE.md +695 -0
  27. package/content/dev/skills/fluent-feature-plan/SKILL.md +227 -0
  28. package/content/dev/skills/fluent-job-batch/SKILL.md +138 -0
  29. package/content/dev/skills/fluent-mermaid-validate/SKILL.md +86 -0
  30. package/content/dev/skills/fluent-module-scaffold/SKILL.md +1928 -0
  31. package/content/dev/skills/fluent-module-validate/SKILL.md +775 -0
  32. package/content/dev/skills/fluent-pre-deploy-check/SKILL.md +1108 -0
  33. package/content/dev/skills/fluent-retailer-config/SKILL.md +1111 -0
  34. package/content/dev/skills/fluent-rule-scaffold/SKILL.md +385 -0
  35. package/content/dev/skills/fluent-scope-decompose/SKILL.md +1021 -0
  36. package/content/dev/skills/fluent-session-audit-export/SKILL.md +632 -0
  37. package/content/dev/skills/fluent-session-summary/SKILL.md +195 -0
  38. package/content/dev/skills/fluent-settings/SKILL.md +1058 -0
  39. package/content/dev/skills/fluent-source-onboard/SKILL.md +632 -0
  40. package/content/dev/skills/fluent-system-monitoring/SKILL.md +767 -0
  41. package/content/dev/skills/fluent-test-data/SKILL.md +513 -0
  42. package/content/dev/skills/fluent-trace/SKILL.md +1143 -0
  43. package/content/dev/skills/fluent-transition-api/SKILL.md +346 -0
  44. package/content/dev/skills/fluent-version-manage/SKILL.md +744 -0
  45. package/content/dev/skills/fluent-workflow-analyzer/SKILL.md +959 -0
  46. package/content/dev/skills/fluent-workflow-builder/SKILL.md +319 -0
  47. package/content/dev/skills/fluent-workflow-deploy/SKILL.md +267 -0
  48. package/content/mcp-extn/agents/fluent-mcp.md +69 -0
  49. package/content/mcp-extn/skills/fluent-mcp-tools/SKILL.md +461 -0
  50. package/content/mcp-official/agents/fluent-mcp-core.md +91 -0
  51. package/content/mcp-official/skills/fluent-mcp-core/SKILL.md +94 -0
  52. package/content/rfl/agents/fluent-rfl.md +56 -0
  53. package/content/rfl/skills/fluent-rfl-assess/SKILL.md +172 -0
  54. package/docs/CAPABILITY_MAP.md +77 -0
  55. package/docs/CLI_COVERAGE.md +47 -0
  56. package/docs/DEV_WORKFLOW.md +802 -0
  57. package/docs/FLOW_RUN.md +142 -0
  58. package/docs/USE_CASES.md +404 -0
  59. package/metadata.json +156 -0
  60. 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`.