@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,513 @@
1
+ ---
2
+ name: fluent-test-data
3
+ description: Discover retailer configuration and generate valid test entities dynamically via GraphQL. No hardcoded refs — everything is queried from the live environment. Triggers on "create test order", "test data", "create test entity", "discover products", "discover locations".
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ argument-hint: [entity-type] [--type HD|CC|SFS] [--count N]
7
+ ---
8
+
9
+ # Dynamic Test Data Generator
10
+
11
+ Discover retailer configuration (locations, products, catalogues, networks, inventory, customers) via live GraphQL queries and generate valid entity creation payloads. Everything is queried — nothing is hardcoded.
12
+
13
+ ## Ownership Boundary
14
+
15
+ This skill owns environment discovery and entity payload generation.
16
+
17
+ Canonical MCP extension tool syntax/limits are owned by `/fluent-mcp-tools`.
18
+
19
+ ## When to Use
20
+
21
+ - Before running E2E tests — discover what's available in the target environment
22
+ - Creating test orders, fulfilments, or other entities with valid refs
23
+ - Exploring retailer configuration (what locations exist, what products are in stock)
24
+ - Generating bulk test data for load/stress testing
25
+
26
+ ## Core Principle
27
+
28
+ **Never hardcode entity refs, product SKUs, location refs, or addresses.** Always discover them from the live environment via GraphQL. Different retailers have different catalogues, locations, and products — hardcoded values will fail.
29
+
30
+ ## Phase 1: Environment Discovery
31
+
32
+ Run these queries in sequence. Each subsequent query uses refs discovered in the previous step.
33
+
34
+ ### Step 1 — Retailer Context
35
+
36
+ ```graphql
37
+ {
38
+ me {
39
+ id
40
+ username
41
+ primaryRetailer { id ref tradingName }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Captures: `retailerId`, `retailerRef`. If `primaryRetailer` is null, use `FLUENT_RETAILER_ID` from config.
47
+
48
+ ### Step 1B — Retailer Scope Safety Gate (mandatory)
49
+
50
+ Before selecting any location, catalogue, product, or customer ref, verify scope:
51
+
52
+ 1. **Prefer retailer-scoped query args when schema supports them.**
53
+ - Use `graphql.introspect` to check whether fields accept retailer-scoped filters.
54
+ 2. **If schema does not support retailer filters**, treat unfiltered results as potentially cross-retailer.
55
+ 3. **Validate candidate refs in context of target retailer** before use:
56
+ - Confirm the candidate is used by entities already created for the target retailer (for example, recent orders/fulfilments).
57
+ - If uncertain, pick another candidate and re-check.
58
+
59
+ If this safety gate is skipped, the order may be created but downstream fulfilment creation can fail because refs belong to a different retailer scope.
60
+
61
+ ### Step 2 — Locations (warehouses and stores)
62
+
63
+ ```graphql
64
+ {
65
+ locations(first: 20) {
66
+ edges {
67
+ node {
68
+ id
69
+ ref
70
+ name
71
+ type
72
+ status
73
+ primaryAddress { street city state postcode country }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ Filter results by status = `ACTIVE`. Group by type:
81
+ - `WAREHOUSE` — for HD (home delivery) sourcing
82
+ - `STORE` — for CC (click & collect) or SFS (ship from store)
83
+
84
+ Pick the **first active warehouse** for HD tests. Pick the **first active store** for CC tests.
85
+
86
+ ### Step 3 — Networks
87
+
88
+ ```graphql
89
+ {
90
+ networks(first: 20) {
91
+ edges {
92
+ node {
93
+ id
94
+ ref
95
+ type
96
+ status
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ Look for:
104
+ - HD network (type contains `HD` or ref contains `HD`)
105
+ - CC network (type contains `CC` or ref contains `CC`)
106
+
107
+ ### Step 4 — Product Catalogues
108
+
109
+ ```graphql
110
+ {
111
+ productCatalogues(first: 10) {
112
+ edges {
113
+ node {
114
+ id
115
+ ref
116
+ type
117
+ status
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ Pick the **first active product catalogue** (usually ref like `PC:MASTER:*` or similar).
125
+
126
+ ### Step 5 — Products with inventory
127
+
128
+ ```graphql
129
+ {
130
+ standardProducts(first: 10, catalogue: { ref: "<CATALOGUE_REF>" }) {
131
+ edges {
132
+ node {
133
+ id
134
+ ref
135
+ name
136
+ status
137
+ variants(first: 5) {
138
+ edges {
139
+ node {
140
+ id
141
+ ref
142
+ name
143
+ status
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ If no `standardProducts`, try `variantProducts` directly:
154
+
155
+ ```graphql
156
+ {
157
+ variantProducts(first: 10, catalogue: { ref: "<CATALOGUE_REF>" }) {
158
+ edges {
159
+ node {
160
+ id
161
+ ref
162
+ name
163
+ status
164
+ }
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ Pick the **first active product**. Note its `ref` — this becomes the `productRef` in order items.
171
+
172
+ ### Step 6 — Inventory Positions (verify stock)
173
+
174
+ ```graphql
175
+ {
176
+ inventoryPositions(first: 5, ref: ["<PRODUCT_REF>"], locationRef: "<LOCATION_REF>") {
177
+ edges {
178
+ node {
179
+ id
180
+ ref
181
+ type
182
+ status
183
+ onHand
184
+ quantity {
185
+ ... on Count { value }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ If onHand > 0 at the target location, the product is in stock. If not, try another product or location.
194
+
195
+ ### Step 7 — Virtual Catalogues
196
+
197
+ ```graphql
198
+ {
199
+ virtualCatalogues(first: 5) {
200
+ edges {
201
+ node {
202
+ id
203
+ ref
204
+ type
205
+ status
206
+ }
207
+ }
208
+ }
209
+ }
210
+ ```
211
+
212
+ ### Step 8 — Existing Customers
213
+
214
+ ```graphql
215
+ {
216
+ customers(first: 5) {
217
+ edges {
218
+ node {
219
+ id
220
+ ref
221
+ firstName
222
+ lastName
223
+ primaryEmail
224
+ }
225
+ }
226
+ }
227
+ }
228
+ ```
229
+
230
+ Pick the **first customer**. If none exist, create one (see Entity Creation below).
231
+
232
+ ### Step 9 — Settings (optional — for consignment prefix, etc.)
233
+
234
+ ```graphql
235
+ {
236
+ settings(first: 50, context: "RETAILER") {
237
+ edges {
238
+ node {
239
+ id
240
+ name
241
+ value
242
+ context
243
+ }
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ Look for settings related to consignment prefix, fulfilment routing, etc.
250
+
251
+ ## Phase 2: Build Discovery Summary
252
+
253
+ After running all queries, compile a summary:
254
+
255
+ ```
256
+ === Environment Discovery ===
257
+ Retailer: <ref> (ID: <id>)
258
+
259
+ Locations:
260
+ Warehouses: <ref1> (<name1>), <ref2> (<name2>)
261
+ Stores: <ref3> (<name3>), <ref4> (<name4>)
262
+
263
+ Networks:
264
+ HD: <network_ref>
265
+ CC: <network_ref>
266
+
267
+ Product Catalogue: <catalogue_ref>
268
+
269
+ Products (in stock):
270
+ <product_ref> at <location_ref> (onHand: <N>)
271
+
272
+ Virtual Catalogue: <vc_ref>
273
+
274
+ Customer: <customer_id> (<name>)
275
+
276
+ Settings:
277
+ consignmentPrefix: <value> (or default "A_")
278
+ ```
279
+
280
+ This summary is the input to entity creation. Every ref comes from live queries — nothing invented.
281
+
282
+ ## Phase 3: Entity Creation
283
+
284
+ ### Create Test Order (HD)
285
+
286
+ Use discovered values to build the mutation. The `<TIMESTAMP>` should be generated as `YYYYMMDD_HHmmss` at execution time.
287
+
288
+ ```graphql
289
+ mutation($input: CreateOrderInput!) {
290
+ createOrder(input: $input) {
291
+ id
292
+ ref
293
+ status
294
+ }
295
+ }
296
+ ```
297
+
298
+ Variables — all values from discovery:
299
+
300
+ ```json
301
+ {
302
+ "input": {
303
+ "ref": "E2E_HD_<TIMESTAMP>",
304
+ "type": "MULTI",
305
+ "retailer": { "id": "<discovered.retailerId>" },
306
+ "customer": { "id": "<discovered.customerId>" },
307
+ "items": [
308
+ {
309
+ "ref": "<discovered.productRef>",
310
+ "quantity": 1,
311
+ "productRef": "<discovered.productRef>",
312
+ "productCatalogueRef": "<discovered.catalogueRef>",
313
+ "fulfilmentChoiceRef": "E2E_HD_<TIMESTAMP>-FC-HD"
314
+ }
315
+ ],
316
+ "fulfilmentChoice": {
317
+ "ref": "E2E_HD_<TIMESTAMP>-FC-HD",
318
+ "type": "HD",
319
+ "deliveryType": "STANDARD",
320
+ "deliveryAddress": {
321
+ "ref": "E2E_HD_<TIMESTAMP>-ADDR",
322
+ "name": "<discovered.location.primaryAddress.name or 'Test Delivery'>",
323
+ "street": "<discovered.location.primaryAddress.street or '1 Discovery Lane'>",
324
+ "city": "<discovered.location.primaryAddress.city or 'Sydney'>",
325
+ "postcode": "<discovered.location.primaryAddress.postcode or '2000'>",
326
+ "country": "<discovered.location.primaryAddress.country or 'AU'>"
327
+ },
328
+ "attributes": [
329
+ { "name": "sourcingLocationRef", "type": "STRING", "value": "<discovered.warehouseRef>" },
330
+ { "name": "consignmentPrefix", "type": "STRING", "value": "<discovered.consignmentPrefix or 'A_'>" }
331
+ ]
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### Create Test Order (CC — Click & Collect)
338
+
339
+ Same as HD, except:
340
+ - `fulfilmentChoice.type` = `"CC"`
341
+ - `deliveryType` = `"NONE"` (customer picks up)
342
+ - `pickupLocationRef` attribute instead of delivery address
343
+ - Use a `STORE` location from discovery
344
+
345
+ ```json
346
+ {
347
+ "input": {
348
+ "ref": "E2E_CC_<TIMESTAMP>",
349
+ "type": "MULTI",
350
+ "retailer": { "id": "<discovered.retailerId>" },
351
+ "customer": { "id": "<discovered.customerId>" },
352
+ "items": [
353
+ {
354
+ "ref": "<discovered.productRef>",
355
+ "quantity": 1,
356
+ "productRef": "<discovered.productRef>",
357
+ "productCatalogueRef": "<discovered.catalogueRef>",
358
+ "fulfilmentChoiceRef": "E2E_CC_<TIMESTAMP>-FC-CC"
359
+ }
360
+ ],
361
+ "fulfilmentChoice": {
362
+ "ref": "E2E_CC_<TIMESTAMP>-FC-CC",
363
+ "type": "CC",
364
+ "deliveryType": "NONE",
365
+ "pickupLocationRef": "<discovered.storeRef>",
366
+ "attributes": [
367
+ { "name": "sourcingLocationRef", "type": "STRING", "value": "<discovered.storeRef>" },
368
+ { "name": "consignmentPrefix", "type": "STRING", "value": "<discovered.consignmentPrefix or 'A_'>" }
369
+ ]
370
+ }
371
+ }
372
+ }
373
+ ```
374
+
375
+ ### Create Test Order (ORDER::MULTI — explicit FC routing inputs)
376
+
377
+ Use this when testing `ORDER::MULTI` where downstream FC routing requires fulfilment-choice attributes:
378
+
379
+ ```json
380
+ {
381
+ "input": {
382
+ "ref": "E2E_MULTI_<TIMESTAMP>",
383
+ "type": "MULTI",
384
+ "retailer": { "id": "<discovered.retailerId>" },
385
+ "customer": { "id": "<discovered.customerId>" },
386
+ "items": [
387
+ {
388
+ "ref": "<discovered.productRef>",
389
+ "quantity": 1,
390
+ "productRef": "<discovered.productRef>",
391
+ "productCatalogueRef": "<discovered.catalogueRef>",
392
+ "fulfilmentChoiceRef": "E2E_MULTI_<TIMESTAMP>-FC-HD"
393
+ }
394
+ ],
395
+ "fulfilmentChoice": {
396
+ "ref": "E2E_MULTI_<TIMESTAMP>-FC-HD",
397
+ "type": "HD",
398
+ "deliveryType": "STANDARD",
399
+ "deliveryAddress": {
400
+ "ref": "E2E_MULTI_<TIMESTAMP>-ADDR",
401
+ "name": "Test Delivery",
402
+ "street": "1 Discovery Lane",
403
+ "city": "Sydney",
404
+ "postcode": "2000",
405
+ "country": "AU"
406
+ },
407
+ "attributes": [
408
+ { "name": "sourcingLocationRef", "type": "STRING", "value": "<discovered.warehouseRef>" },
409
+ { "name": "consignmentPrefix", "type": "STRING", "value": "<discovered.consignmentPrefix or 'A_'>" }
410
+ ]
411
+ }
412
+ }
413
+ }
414
+ ```
415
+
416
+ For `ORDER::MULTI`, `sourcingLocationRef` is a critical input for downstream fulfilment creation rules.
417
+
418
+ ### Create Customer (if none found)
419
+
420
+ ```graphql
421
+ mutation($input: CreateCustomerInput!) {
422
+ createCustomer(input: $input) {
423
+ id
424
+ username
425
+ firstName
426
+ lastName
427
+ primaryEmail
428
+ }
429
+ }
430
+ ```
431
+
432
+ Variables:
433
+ ```json
434
+ {
435
+ "input": {
436
+ "username": "e2e-test-<TIMESTAMP>",
437
+ "firstName": "E2E",
438
+ "lastName": "Test",
439
+ "primaryEmail": "e2e-test@example.com",
440
+ "promotionOptIn": false,
441
+ "retailer": { "id": "<discovered.retailerId>" }
442
+ }
443
+ }
444
+ ```
445
+
446
+ ## Phase 4: Bulk Creation
447
+
448
+ For creating multiple test entities:
449
+
450
+ 1. Run discovery once (Phase 1-2)
451
+ 2. For each entity, generate a unique ref with incrementing counter: `E2E_HD_<TIMESTAMP>_001`, `_002`, etc.
452
+ 3. Use `graphql.batchMutate` for up to 50 entities per batch:
453
+
454
+ ```json
455
+ {
456
+ "mutation": "createOrder",
457
+ "inputs": [
458
+ { "ref": "E2E_HD_<TS>_001", ... },
459
+ { "ref": "E2E_HD_<TS>_002", ... }
460
+ ],
461
+ "returnFields": ["id", "ref", "status"]
462
+ }
463
+ ```
464
+
465
+ ## Schema Constraints
466
+
467
+ These constraints are enforced by the GraphQL schema. Violating them produces cryptic errors.
468
+
469
+ ### Customer Creation
470
+ - **NO `ref` field** — `username` is the identifier, not `ref`
471
+ - **`promotionOptIn` (Boolean!) is required** — use `false` for test customers
472
+ - **`firstName` and `lastName` are optional** (despite appearing essential)
473
+ - **`username` must be unique** — append timestamps: `e2e-test-<YYYYMMDD_HHmmss>`
474
+
475
+ ### Order Creation
476
+ - **`retailer.id` must be a String**: `{ "id": "2" }` not `{ "id": 2 }`
477
+ - **`customer.id` must be a String** — use the ID from discovery or customer creation
478
+ - **`productCatalogueRef`** must match an existing catalogue's exact `ref` — discover it, don't guess
479
+ - **`fulfilmentChoice.ref`** must be unique per order — generate from order ref
480
+
481
+ ### Settings Queries (used in discovery)
482
+ - **`context` is a plain String**: `context: "RETAILER"` not `context: { contextType: "RETAILER" }`
483
+ - **Pagination** requires `query($cursor: String)` variable declaration with `variables: { cursor: null }`
484
+
485
+ ### Ref Collision Prevention
486
+ Before creating any entity, check if the ref already exists:
487
+ ```graphql
488
+ { orders(first: 1, ref: ["<proposed_ref>"]) { edges { node { id ref status } } } }
489
+ ```
490
+ If edges is non-empty, append a counter or new timestamp.
491
+
492
+ ## Troubleshooting Discovery
493
+
494
+ | Problem | Cause | Fix |
495
+ |---------|-------|-----|
496
+ | No locations found | Retailer has no locations configured | Check `locations` with no filter, or create via `/fluent-retailer-config` |
497
+ | No products found | Wrong catalogue ref or empty catalogue | Try other catalogues, check `productCatalogues` for all options |
498
+ | Zero inventory | Product exists but no stock at location | Check other locations, or create inventory position via `/fluent-retailer-config` |
499
+ | No customers | Retailer has no customer entities | Create one using the mutation above (remember: no `ref`, need `promotionOptIn`) |
500
+ | Schema error on mutation | Fluent version difference or custom schema | Use `graphql.introspect` to check exact input fields |
501
+ | `productCatalogueRef` rejected | Catalogue might use `ref` format `PC:NAME:VERSION` | Query catalogues and use exact ref format returned |
502
+ | Customer creation fails with "ref" error | `ref` field doesn't exist on `CreateCustomerInput` | Remove `ref`, use `username` as the identifier |
503
+ | Customer creation fails with required field | Missing `promotionOptIn` | Add `"promotionOptIn": false` to input |
504
+ | Order reaches ON_VALIDATION/IN_PROGRESS but no fulfilment created | Discovered refs may be from a different retailer scope | Re-run Step 1B scope checks; replace `productCatalogueRef` and `sourcingLocationRef` with retailer-valid refs |
505
+
506
+ ## Tips
507
+
508
+ - **Run discovery fresh each time** — Locations, products, and inventory can change between sessions
509
+ - **Prefer products with inventory** — Orders for out-of-stock products may fail allocation
510
+ - **Use warehouse addresses for delivery** — When no other address is available, use the sourcing location's address as a fallback
511
+ - **Check workflow type** — Different retailers may use `HD`, `CC`, `SFS`, or custom subtypes. Query `workflow.transitions` to discover what's configured
512
+ - **Schema varies by account** — Always use `graphql.introspect` if a mutation fails; field names may differ
513
+ - **Cross-reference with `/fluent-retailer-config`** for entity creation constraints — it has the full validated Schema Patterns table