@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,385 @@
1
+ ---
2
+ name: fluent-rule-scaffold
3
+ description: Scaffold new Fluent Commerce Rubix rules. Generates Java class, test class, and wires into module.json. Triggers on "create rule", "new rule", "scaffold rule", "add rule class".
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
6
+ argument-hint: <rule-name> [--entity-type ORDER|FULFILMENT|...] [--package common|fulfilment|order|...]
7
+ ---
8
+
9
+ # Rule Scaffolder
10
+
11
+ Generate new Fluent Commerce Rubix rule classes with proper structure, annotations, test skeletons, and module wiring.
12
+
13
+ ## Planning Gate
14
+
15
+ **Before scaffolding any rule, write a plan using the template from `PLAN_TEMPLATE.md` in the `fluent-feature-plan` skill.** Every table row must carry a Source column (NEW/EXISTING/MODIFIED/REUSED/OOTB). For NEW rules, provide full pseudo logic.
16
+
17
+ **If this rule is part of a larger feature** (multiple rules + workflow changes + settings), use `/fluent-feature-plan` first to produce the comprehensive plan. Then reference the approved plan during scaffolding.
18
+
19
+ **Rule-scaffold specific emphasis — ensure these are covered:**
20
+
21
+ 1. **Business Context (§1)** — what business need drives this rule; why custom, not OOTB
22
+ 2. **State machine (§3.1)** — show the workflow status where this rule fires, with transitions
23
+ 3. **Sequence diagram (§3.2)** — data flow: READ, VALIDATE, QUERY, BUILD, MUTATE, LOG. Use Mermaid `sequenceDiagram`. Validate syntax per `/fluent-mermaid-validate`
24
+ 4. **Rulesets (§6)** — which rulesets use this rule, trigger event, from/to status
25
+ 5. **Rules Inventory (§7)** — this rule classified as NEW with source label
26
+ 6. **Detailed Design (§8)** — parameters table, pseudo logic, GraphQL operations
27
+ 7. **Settings (§9)** — settings the rule reads, with context, value type, and shape
28
+ 8. **GraphQL Operations (§12)** — queries and mutations, marked as NEW or REUSED pattern
29
+
30
+ **Write the plan to:** `accounts/<PROFILE>/plans/<YYYY-MM-DD>-rule-scaffold-<slug>.md`. Set `Status: PENDING`.
31
+
32
+ Present the full plan content to the user and wait for approval before generating any files. On approval, update the file to `Status: APPROVED`. If the user says "just do it", proceed directly (still write the file for audit trail).
33
+
34
+ ## When to Use
35
+
36
+ - Creating a new custom Rubix rule
37
+ - Adding a rule that follows the project's BaseRule pattern
38
+ - Generating boilerplate with proper annotations and logging
39
+ - Wiring a new rule into `module.json`
40
+
41
+ ## Pre-Check: Use OOTB or Build Custom?
42
+
43
+ Before scaffolding, determine if an out-of-the-box (OOTB) rule already covers the requirement.
44
+
45
+ ### Decision Tree
46
+
47
+ ```
48
+ User asks: "I need a rule that does X"
49
+
50
+ ├── 1. Check module inventory
51
+ │ Read: accounts/<PROFILE>/analysis/module-validate/module-inventory.json
52
+ │ If missing → run /fluent-module-validate inventory first
53
+
54
+ ├── 2. Search registered rules for matching capability
55
+ │ Use MCP tool: plugin.list with name filter
56
+ │ Look for rules whose description matches the ask
57
+ │ Check both FLUENTRETAIL.* (platform) and <ACCOUNT>.* (deployed)
58
+
59
+ ├── 3. Evaluate match
60
+ │ ├── EXACT MATCH → Recommend OOTB rule
61
+ │ │ Tell user: "Use <RuleName> with parameters: ..."
62
+ │ │ Show accepted entity types and parameters
63
+ │ │ No scaffolding needed
64
+ │ │
65
+ │ ├── PARTIAL MATCH → Evaluate gap
66
+ │ │ Small gap (missing one parameter) → Consider extending OOTB
67
+ │ │ Large gap (fundamentally different) → Build custom
68
+ │ │
69
+ │ └── NO MATCH → Build custom rule
70
+ │ Proceed with scaffolding below
71
+ ```
72
+
73
+ ### When OOTB Rules Suffice
74
+
75
+ | Need | OOTB Rule | Why not custom? |
76
+ |------|-----------|----------------|
77
+ | Change entity status | `SendEvent` / `ChangeStateGQL` | Standard lifecycle — no custom logic needed |
78
+ | Send event to another entity | `SendEventToEntity` | Platform handles entity resolution |
79
+ | Forward event conditionally | `ForwardIf*` family (dozens exist) | Many condition checks already built |
80
+ | Update attributes from event | `SetAttributes` / `UpsertAttributes` | Standard attribute operations |
81
+ | Create a fulfilment | `CreateFulfilmentRule` | Standard fulfilment creation |
82
+
83
+ ### When Custom Rules Are Needed
84
+
85
+ | Need | Why custom? | Real example |
86
+ |------|------------|-------------|
87
+ | Setting-driven dynamic behavior | OOTB rules have fixed parameters, can't read settings for runtime config | `SendWebhookWithDynamicAttributes`, `UpdateEntityFromSetting` |
88
+ | Complex data transformation | Multi-step JSON path extraction + field mapping | `UpsertAttributeFromPath`, `UpdateFulfilmentItemsFromEvent` |
89
+ | Cross-entity composition | Query one entity to create/update another atomically | `CreateFulfilmentFromSourcingLocation` |
90
+ | Domain-specific validation | Business logic not covered by generic conditions | `CreateMissingVariantProducts` |
91
+
92
+ ### Rule Discovery via MCP
93
+
94
+ ```
95
+ # Search all registered rules by keyword
96
+ plugin.list name="Fulfilment" → all fulfilment-related rules
97
+ plugin.list name="SendEvent" → all event-forwarding rules
98
+ plugin.list name="Attribute" → all attribute manipulation rules
99
+ plugin.list name="Webhook" → webhook-related rules
100
+ ```
101
+
102
+ Each result includes `description`, `parameters[]`, and `accepts[]` (entity types) — enough to determine fit.
103
+
104
+ ## Rubix Rule Framework
105
+
106
+ All custom rules in this project follow this architecture:
107
+
108
+ ```
109
+ BaseRule (abstract)
110
+ └── extends Rule (Rubix SDK)
111
+ └── wraps Context in ContextWrapper
112
+ └── provides automatic logging via CommonUtils.generateLog()
113
+ └── subclasses implement: run(ContextWrapper context)
114
+ ```
115
+
116
+ ### Required Annotations
117
+
118
+ ```java
119
+ @RuleInfo(
120
+ name = "MyRuleName",
121
+ description = "What this rule does in one sentence"
122
+ )
123
+ @ParamString(name = "paramName", description = "What this parameter controls")
124
+ @Slf4j // Lombok logging
125
+ public class MyRuleName extends BaseRule {
126
+ @Override
127
+ public void run(ContextWrapper context) {
128
+ // Rule implementation
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Key APIs
134
+
135
+ | API | Usage |
136
+ |-----|-------|
137
+ | `context.getProp("name")` | Read ruleset prop value |
138
+ | `context.getEvent()` | Get the triggering event |
139
+ | `context.getEvent().getAccountId()` | Account ID for logging |
140
+ | `context.getEvent().getEntityId()` | Entity ID |
141
+ | `context.getEvent().getEntityRef()` | Entity reference |
142
+ | `context.getEvent().getEntityType()` | Entity type |
143
+ | `context.getEvent().getAttributes()` | Event attributes map |
144
+ | `context.addLog("message")` | Add to execution log |
145
+ | `context.action().mutation()` | Queue a GraphQL mutation (executes AFTER rule completes) |
146
+ | `context.action().eventAction()` | Queue an event to fire |
147
+ | `DynamicUtils.query(context, paths, queryName, params)` | Execute dynamic GraphQL query |
148
+ | `DynamicUtils.mutate(context, values)` | Execute dynamic mutation |
149
+ | `DynamicUtils.getJsonPath(context, jsonPath)` | Resolve value from event or entity |
150
+ | `SettingUtils.getSettingByRef(context, ref)` | Read a Fluent Setting |
151
+
152
+ ## Rule Class Template
153
+
154
+ ```java
155
+ package com.fluentcommerce.rule.<PACKAGE>;
156
+
157
+ import com.fluentcommerce.common.BaseRule;
158
+ import com.fluentcommerce.common.ContextWrapper;
159
+ import com.fluentretail.rubix.rule.meta.ParamString;
160
+ import com.fluentretail.rubix.rule.meta.RuleInfo;
161
+ import lombok.extern.slf4j.Slf4j;
162
+
163
+ /**
164
+ * <DESCRIPTION>
165
+ */
166
+ @RuleInfo(
167
+ name = "<RULE_NAME>",
168
+ description = "<DESCRIPTION>"
169
+ )
170
+ @ParamString(name = "<PARAM1>", description = "<PARAM1_DESC>")
171
+ @Slf4j
172
+ public class <RULE_NAME> extends BaseRule {
173
+
174
+ private static final String CLASS_NAME = <RULE_NAME>.class.getSimpleName();
175
+
176
+ @Override
177
+ public void run(ContextWrapper context) {
178
+ String accountId = context.getEvent().getAccountId();
179
+
180
+ log.info("[{}] [{}] - Processing event: {}", accountId, CLASS_NAME,
181
+ context.getEvent().getName());
182
+ context.addLog("Processing " + CLASS_NAME);
183
+
184
+ // Read props
185
+ String param1 = context.getProp("<PARAM1>");
186
+ if (param1 == null || param1.trim().isEmpty()) {
187
+ log.error("[{}] [{}] - Required prop '<PARAM1>' is missing", accountId, CLASS_NAME);
188
+ context.addLog("Error: <PARAM1> is required");
189
+ return;
190
+ }
191
+
192
+ try {
193
+ // === Rule logic here ===
194
+
195
+ context.addLog(CLASS_NAME + " completed successfully");
196
+ } catch (Exception e) {
197
+ log.error("[{}] [{}] - Error: {}", accountId, CLASS_NAME, e.getMessage(), e);
198
+ context.addLog("Error in " + CLASS_NAME + ": " + e.getMessage());
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## Test Class Template
205
+
206
+ ```java
207
+ package com.fluentcommerce.rule.<PACKAGE>;
208
+
209
+ import com.fluentretail.rubix.event.Event;
210
+ import com.fluentretail.rubix.rule.meta.RuleInfo;
211
+ import com.fluentretail.rubix.v2.context.Context;
212
+ import com.fluentretail.rubix.v2.action.Action;
213
+ import com.fluentretail.rubix.v2.action.MutationAction;
214
+ import com.fluentretail.rubix.v2.action.EventAction;
215
+ import org.junit.jupiter.api.BeforeEach;
216
+ import org.junit.jupiter.api.Test;
217
+ import org.junit.jupiter.api.DisplayName;
218
+ import org.mockito.Mock;
219
+ import org.mockito.MockitoAnnotations;
220
+
221
+ import java.util.HashMap;
222
+ import java.util.Map;
223
+
224
+ import static org.junit.jupiter.api.Assertions.*;
225
+ import static org.mockito.Mockito.*;
226
+
227
+ class <RULE_NAME>Test {
228
+
229
+ private <RULE_NAME> rule;
230
+
231
+ @Mock private Context context;
232
+ @Mock private Event event;
233
+ @Mock private Action action;
234
+ @Mock private MutationAction mutationAction;
235
+ @Mock private EventAction eventAction;
236
+
237
+ @BeforeEach
238
+ void setUp() {
239
+ MockitoAnnotations.openMocks(this);
240
+ rule = new <RULE_NAME>();
241
+
242
+ when(context.getEvent()).thenReturn(event);
243
+ when(context.action()).thenReturn(action);
244
+ when(action.mutation()).thenReturn(mutationAction);
245
+ when(action.eventAction()).thenReturn(eventAction);
246
+ when(event.getAccountId()).thenReturn("TEST_ACCOUNT");
247
+ when(event.getName()).thenReturn("TestEvent");
248
+ }
249
+
250
+ @Test
251
+ @DisplayName("Rule has correct @RuleInfo annotation")
252
+ void hasRuleInfo() {
253
+ RuleInfo info = <RULE_NAME>.class.getAnnotation(RuleInfo.class);
254
+ assertNotNull(info);
255
+ assertEquals("<RULE_NAME>", info.name());
256
+ }
257
+
258
+ @Test
259
+ @DisplayName("Handles missing required prop gracefully")
260
+ void missingRequiredProp() {
261
+ Map<String, String> props = new HashMap<>();
262
+ when(context.getProp("<PARAM1>")).thenReturn(null);
263
+
264
+ // Should not throw — rule logs error and returns
265
+ assertDoesNotThrow(() -> rule.run(context));
266
+ }
267
+
268
+ @Test
269
+ @DisplayName("Processes event successfully with valid props")
270
+ void processesSuccessfully() {
271
+ when(context.getProp("<PARAM1>")).thenReturn("testValue");
272
+
273
+ rule.run(context);
274
+
275
+ // Verify expected behavior
276
+ // verify(mutationAction).xxx(...);
277
+ }
278
+ }
279
+ ```
280
+
281
+ ## Pre-Check: Load Source Analysis
282
+
283
+ Before scaffolding, check if `/fluent-custom-code` analysis artifacts exist:
284
+
285
+ ```
286
+ accounts/<PROFILE>/analysis/custom-code/source-map.json
287
+ accounts/<PROFILE>/analysis/custom-code/constraints.json
288
+ ```
289
+
290
+ If found, read them to discover:
291
+ - `modules[].patterns.basePackage` — correct package prefix
292
+ - `modules[].patterns.subPackages` — available sub-packages
293
+ - `modules[].patterns.baseClass` — base class to extend
294
+ - `modules[].buildRoot` — where to run Maven
295
+ - `modules[].rules[]` — existing rules (avoid name collisions)
296
+ - `constraints[].extensionConstraints` — rules to follow
297
+
298
+ If not found, suggest running `/fluent-custom-code <PROFILE>` first, or fall back to the defaults below. Treat artifact-gate failures as "not found" (for example missing required files, missing hashes, or blocking `missingSources` in `constraints.json`).
299
+
300
+ ## Scaffolding Steps
301
+
302
+ ### 1. Determine rule location
303
+
304
+ Rules live under the plugin source tree:
305
+
306
+ ```
307
+ plugins/rules/<module-alias>/src/main/java/com/fluentcommerce/rule/
308
+ ├── common/ # Entity-agnostic rules
309
+ ├── fulfilment/ # Fulfilment-specific rules
310
+ ├── variantproduct/ # Product-specific rules
311
+ ├── order/ # Order-specific rules (create if needed)
312
+ └── returnorder/ # Return order rules (create if needed)
313
+ ```
314
+
315
+ ### 2. Create the rule class
316
+
317
+ Write the Java file at the appropriate package path using the template above. Replace all `<PLACEHOLDERS>`.
318
+
319
+ ### 3. Create the test class
320
+
321
+ Write the test at the mirror path under `src/test/java/`. Test class name = `<RuleName>Test.java`.
322
+
323
+ ### 4. Wire into module.json
324
+
325
+ Add the rule name to the `rules` array in the module's `resources/module.json`:
326
+
327
+ ```json
328
+ {
329
+ "rules": [
330
+ "ExistingRule1",
331
+ "ExistingRule2",
332
+ "NewRuleName"
333
+ ]
334
+ }
335
+ ```
336
+
337
+ ### 5. Verify compilation
338
+
339
+ ```bash
340
+ cd plugins/ && mvn clean install -pl rules/<module-alias> -am
341
+ ```
342
+
343
+ If Maven fails, check:
344
+ - Node.js version (must be 18 for Apollo codegen): `nvm use 18` on Unix; on Windows with nvm-windows (nvm4w), use the full version number: `nvm use 18.x.x`
345
+ - Import paths are correct
346
+ - `BaseRule` and `ContextWrapper` are in the `common` package
347
+
348
+ ## Rules That Use Dynamic GraphQL
349
+
350
+ For rules that need to query or mutate entities dynamically:
351
+
352
+ ```java
353
+ import com.fluentcommerce.util.dynamic.DynamicUtils;
354
+
355
+ // Query entity fields via JSON paths
356
+ List<String> paths = Arrays.asList("ref", "status", "fulfilments.ref", "fulfilments.status");
357
+ JsonNode result = DynamicUtils.query(context, paths, "orderQuery", null);
358
+
359
+ // Mutate entity
360
+ Map<String, Object> values = new HashMap<>();
361
+ values.put("id", entityId);
362
+ values.put("status", "NEW_STATUS");
363
+ values.put("attributes", attributeList);
364
+ DynamicUtils.mutate(context, values);
365
+ ```
366
+
367
+ **Path syntax:** Omit `edges` and `node` — DynamicEntityQuery adds them automatically. Use `items.product.name` not `items.edges.node.product.name`.
368
+
369
+ ## Rules That Read Settings
370
+
371
+ ```java
372
+ import com.fluentcommerce.util.SettingUtils;
373
+ import com.fluentretail.rubix.foundation.graphql.type.Setting;
374
+
375
+ Setting setting = SettingUtils.getSettingByRef(context, "my.setting.ref");
376
+ JsonNode config = JsonUtils.stringToPojo(setting.getLobValue(), JsonNode.class);
377
+ ```
378
+
379
+ ## Gotchas
380
+
381
+ - **`@ParamString` names are CASE-SENSITIVE** — `"jsonpath"` and `"jsonPath"` are different props
382
+ - **`context.action().mutation()` only QUEUES** — the mutation executes AFTER the rule returns
383
+ - **`DynamicMutation.buildMutationDocument` appends `!`** — never include `!` in type names
384
+ - **`updateFulfilmentItem` does NOT exist** — use `updateFulfilment` with nested `items` array
385
+ - **BaseRule wraps Context** — implement `run(ContextWrapper context)`, not `run(Context context)`