@clix-so/clix-agent-skills 0.1.6 → 0.1.8

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.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: clix-personalization
3
+ description:
4
+ Helps developers author and debug Clix personalization templates
5
+ (Liquid-style) for message content, deep links/URLs, and audience targeting.
6
+ Use when the user mentions personalization variables, Liquid, templates,
7
+ conditional logic, loops, filters, deep links, message logs, or when the user
8
+ types `clix-personalization`.
9
+ ---
10
+
11
+ # Clix Personalization
12
+
13
+ Use this skill to help developers write **personalized Clix campaigns** using
14
+ template-based variables and light logic in:
15
+
16
+ - **Message content** (title, body, subtitle)
17
+ - **Links** (dynamic URL or deep link params)
18
+ - **Audience targeting** (conditional include/exclude rules)
19
+
20
+ ## What the official docs guarantee (high-signal)
21
+
22
+ - **Data namespaces**:
23
+ - `user.*` (user traits/properties)
24
+ - `event.*` (event payload properties)
25
+ - `trigger.*` (custom properties passed via API-trigger)
26
+ - Device/system vars: `device.id`, `device.platform`, `device.locale`,
27
+ `device.language`, `device.timezone`, plus `user.id`, `event.name`
28
+ - **Missing variables** render as an **empty string**.
29
+ - **Output**: print values with `{{ ... }}`.
30
+ - **Conditionals**: `{% if %}`, `{% else %}`, `{% endif %}` with operators
31
+ `== != > < >= <= and or not`. Invalid conditions evaluate to `false`.
32
+ - **Loops**: `{% for x in y %}` / `{% endfor %}` (use guards for empty lists).
33
+ - **Filters**: `upcase`, `downcase`, `capitalize`, `default`, `join`, `split`,
34
+ `escape`, `strip`, `replace` (chain with `|`).
35
+ - **Errors**: template rendering errors show up in **Message Logs**.
36
+
37
+ ## MCP-first (source of truth)
38
+
39
+ If the Clix MCP tools are available, treat them as the source of truth:
40
+
41
+ - `clix-mcp-server:search_docs` for personalization behavior and supported
42
+ syntax
43
+
44
+ If MCP tools are not available, use the bundled references in `references/`.
45
+
46
+ ## Workflow (copy + check off)
47
+
48
+ ```
49
+ Personalization progress:
50
+ - [ ] 1) Identify where the template runs (message / URL / audience rule)
51
+ - [ ] 2) Identify trigger type (event-triggered vs API-triggered)
52
+ - [ ] 3) Confirm available inputs (user.*, event.*, trigger.*, device.*)
53
+ - [ ] 4) Draft templates with guards/defaults
54
+ - [ ] 5) Validate template structure (balance tags, avoid missing vars)
55
+ - [ ] 6) Verify in Clix (preview/test payloads, check Message Logs)
56
+ ```
57
+
58
+ ## 1) Confirm the minimum inputs
59
+
60
+ Ask only what’s needed:
61
+
62
+ - **Where used**: title/body/subtitle vs URL/deep link vs audience targeting
63
+ rule
64
+ - **Campaign trigger**:
65
+ - Event-triggered → `event.*` is available
66
+ - API-triggered → `trigger.*` is available
67
+ - **Inputs**:
68
+ - Which `user.*` properties are set by the app
69
+ - Which `event.*` / `trigger.*` properties are passed (include example
70
+ payload)
71
+ - **Fallback policy**: what to show when data is missing (default strings,
72
+ guards)
73
+
74
+ ## 2) Produce a “Template Plan” (before tweaking lots of templates)
75
+
76
+ Return a compact table the user can approve:
77
+
78
+ - **field** (title/body/url/audience)
79
+ - **template** (Liquid)
80
+ - **required variables** (and their source: user/event/trigger/device)
81
+ - **fallbacks** (default filter and/or conditional guards)
82
+ - **example payload** + **expected rendered output**
83
+
84
+ ## 3) Authoring guidelines (what to do by default)
85
+
86
+ - Prefer **simple variables + `default`** over complex branching.
87
+ - Add **guards** for optional data and for arrays (`size > 0`) before looping.
88
+ - Prefer **pre-formatted** properties from your app (e.g., `"7.4 miles"`,
89
+ `"38m 40s"`) instead of formatting inside templates.
90
+ - Keep logic minimal; complex branching belongs in the app or segmentation
91
+ setup.
92
+
93
+ ## 4) Validation (fast feedback loop)
94
+
95
+ If you have a template file (recommended for review), run:
96
+
97
+ ```bash
98
+ bash <skill-dir>/scripts/validate-template.sh path/to/template.liquid
99
+ ```
100
+
101
+ This script does lightweight checks (matching `{% if %}`/`{% endif %}`,
102
+ `{% for %}`/`{% endfor %}`, and brace balancing). It can’t guarantee the
103
+ variables exist — you still need a payload + console verification.
104
+
105
+ ## 5) Verification checklist
106
+
107
+ - **Missing data**: does the template still read well with empty strings?
108
+ - **Trigger type**: API triggers use `trigger.*` (not `event.*`).
109
+ - **Message Logs**: check rendering errors and fix syntax issues first.
110
+ - **Upstream data**:
111
+ - If `user.*` is missing → fix user property setting (see
112
+ `clix-user-management`)
113
+ - If `event.*` is missing → fix event tracking payload (see
114
+ `clix-event-tracking`)
115
+
116
+ ## Progressive Disclosure
117
+
118
+ - **Level 1**: This `SKILL.md` (always loaded)
119
+ - **Level 2**: `references/` (load when writing/debugging templates)
120
+ - **Level 3**: `scripts/` (execute directly, do not load into context)
121
+
122
+ ## References
123
+
124
+ - `references/template-syntax.md` - Variables, output, conditionals, loops,
125
+ filters
126
+ - `references/common-patterns.md` - Copy/paste patterns for messages + URLs +
127
+ guards
128
+ - `references/debugging.md` - Troubleshooting missing variables and Message Logs
@@ -0,0 +1,69 @@
1
+ # Common Personalization Patterns (Reference)
2
+
3
+ Copy/paste starting points for Clix templates. Prefer simple templates with
4
+ guards/defaults.
5
+
6
+ ## Welcome / fallback-safe greeting
7
+
8
+ ```liquid
9
+ Hi {{ user.username | default: "there" }},
10
+ ```
11
+
12
+ ## Dynamic message body (event-triggered)
13
+
14
+ ```liquid
15
+ Hi {{ user.username | default: "Runner" }},
16
+ you ran {{ event.distance | default: "?" }} miles in {{ event.elapsedTime | default: "?" }} seconds.
17
+ ```
18
+
19
+ ## Dynamic deep link / URL (event-triggered)
20
+
21
+ ```liquid
22
+ myapp://run/summary?distance={{ event.distance }}&time={{ event.elapsedTime }}
23
+ ```
24
+
25
+ Tip: If a value may contain spaces or special characters, prefer passing a
26
+ pre-encoded string from your app (or keep the template value simple).
27
+
28
+ ## API-triggered promo (trigger.\*)
29
+
30
+ ```liquid
31
+ 🎉 {{ trigger.promotion | default: "A new promotion" }} is here!
32
+ Get {{ trigger.discount | default: "a discount" }} off now!
33
+ ```
34
+
35
+ ## Conditional title by threshold
36
+
37
+ ```liquid
38
+ {% if event.distance >= 10 %}
39
+ Long run completed!
40
+ {% else %}
41
+ Good run today!
42
+ {% endif %}
43
+ ```
44
+
45
+ ## Nested conditions (keep minimal)
46
+
47
+ ```liquid
48
+ {% if user.tier == "pro" %}
49
+ Pro stats updated successfully.
50
+ {% else %}
51
+ {% if event.elapsedTime > 3600 %}
52
+ You ran for more than an hour! Great job!
53
+ {% else %}
54
+ New record saved.
55
+ {% endif %}
56
+ {% endif %}
57
+ ```
58
+
59
+ ## Looping over an array with a guard
60
+
61
+ ```liquid
62
+ {% if user.badges and user.badges.size > 0 %}
63
+ {% for badge in user.badges %}
64
+ - {{ badge }}
65
+ {% endfor %}
66
+ {% else %}
67
+ No badges yet.
68
+ {% endif %}
69
+ ```
@@ -0,0 +1,89 @@
1
+ # Debugging Personalization (Reference)
2
+
3
+ This guide helps you troubleshoot when a personalized message renders
4
+ unexpectedly.
5
+
6
+ ## First principles (what the renderer does)
7
+
8
+ - Unknown / missing variables render as an **empty string**.
9
+ - Invalid conditions evaluate to `false`.
10
+ - Template rendering errors (syntax issues) surface in **Message Logs**.
11
+
12
+ ## Quick checklist
13
+
14
+ - **Are you using the right namespace?**
15
+ - Event-triggered campaign → use `event.*`
16
+ - API-triggered campaign → use `trigger.*`
17
+ - User traits/properties → use `user.*`
18
+ - **Is the variable actually set upstream?**
19
+ - `user.*` comes from your app calling user property APIs.
20
+ - `event.*` comes from the event payload you track.
21
+ - `trigger.*` comes from properties in the API trigger request.
22
+ - **Is the template resilient to missing data?**
23
+ - Add `| default: "..."` for strings.
24
+ - Wrap optional blocks in `{% if ... %}` guards.
25
+ - **Are you looping safely?**
26
+ - Guard with `collection and collection.size > 0` before `{% for %}`.
27
+
28
+ ## Common failure modes
29
+
30
+ ### 1) Blank values
31
+
32
+ Symptoms:
33
+
34
+ - Output is missing where you expected text.
35
+
36
+ Causes:
37
+
38
+ - Variable doesn’t exist for that user/campaign trigger.
39
+
40
+ Fix:
41
+
42
+ - Use `default` for output values:
43
+
44
+ ```liquid
45
+ {{ user.username | default: "Guest" }}
46
+ ```
47
+
48
+ ### 2) Conditional branch never runs
49
+
50
+ Symptoms:
51
+
52
+ - Always hits the `{% else %}` branch.
53
+
54
+ Causes:
55
+
56
+ - `event.*` is missing (or you should be using `trigger.*`).
57
+ - Value is not the type you expect (e.g., string vs number).
58
+
59
+ Fix:
60
+
61
+ - Verify trigger type and payload shape.
62
+ - Keep comparisons simple and ensure the property is sent as a number when you
63
+ compare numerically.
64
+
65
+ ### 3) Syntax error / rendering error
66
+
67
+ Symptoms:
68
+
69
+ - Message fails to render or shows an error in logs.
70
+
71
+ Causes:
72
+
73
+ - Missing `{% endif %}` / `{% endfor %}`
74
+ - Unbalanced `{{` / `}}` or `{%` / `%}`
75
+
76
+ Fix:
77
+
78
+ - Check **Message Logs** first.
79
+ - Validate the template structure locally:
80
+
81
+ ```bash
82
+ bash <skill-dir>/scripts/validate-template.sh path/to/template.liquid
83
+ ```
84
+
85
+ ## When to escalate (data issue vs template issue)
86
+
87
+ - If **defaults/guards** fix it → template issue.
88
+ - If values are always blank across many users → upstream tracking/user-property
89
+ issue (fix app instrumentation).
@@ -0,0 +1,113 @@
1
+ # Personalization Template Syntax (Reference)
2
+
3
+ This reference summarizes the personalization syntax supported by Clix
4
+ templates.
5
+
6
+ ## Data namespaces
7
+
8
+ - `user.*`: user properties (traits) you capture about a user
9
+ - Examples: `user.username`, `user.tier`
10
+ - `event.*`: properties from the triggering event (event-triggered campaigns)
11
+ - Examples: `event.distance`, `event.elapsedTime`
12
+ - `trigger.*`: custom properties passed via API trigger (API-triggered
13
+ campaigns)
14
+ - Examples: `trigger.promotion`, `trigger.discount`
15
+
16
+ Missing or undefined variables render as an **empty string**.
17
+
18
+ ## Device and system variables
19
+
20
+ Clix templates also support device and environment variables:
21
+
22
+ - `device.id`
23
+ - `device.platform` (`IOS` or `ANDROID`)
24
+ - `device.locale`
25
+ - `device.language`
26
+ - `device.timezone`
27
+ - `user.id` (project user id)
28
+ - `event.name` (event name)
29
+
30
+ ## Output variables
31
+
32
+ Print a value:
33
+
34
+ ```liquid
35
+ {{ user.username }}
36
+ ```
37
+
38
+ Nested / bracket access (useful when the key is not a valid identifier):
39
+
40
+ ```liquid
41
+ {{ event["distance"] }}
42
+ ```
43
+
44
+ ## Conditionals
45
+
46
+ Use `{% if %}`, `{% else %}`, `{% endif %}` blocks.
47
+
48
+ Supported operators:
49
+
50
+ - `==`, `!=`, `>`, `<`, `>=`, `<=`
51
+ - `and`, `or`, `not`
52
+
53
+ Example:
54
+
55
+ ```liquid
56
+ {% if event.distance >= 10 %}
57
+ Amazing! You completed a long run today.
58
+ {% else %}
59
+ Nice work! Keep it up!
60
+ {% endif %}
61
+ ```
62
+
63
+ Notes:
64
+
65
+ - Unknown variables render as empty strings.
66
+ - Invalid conditions evaluate to `false`.
67
+
68
+ ## Loops
69
+
70
+ Iterate over arrays with `{% for %}` / `{% endfor %}`:
71
+
72
+ ```liquid
73
+ {% for badge in user.badges %}
74
+ - {{ badge }}
75
+ {% endfor %}
76
+ ```
77
+
78
+ Guard empty collections:
79
+
80
+ ```liquid
81
+ {% if user.badges and user.badges.size > 0 %}
82
+ {% for badge in user.badges %}
83
+ {{ badge }}
84
+ {% endfor %}
85
+ {% else %}
86
+ No badges yet.
87
+ {% endif %}
88
+ ```
89
+
90
+ ## Filters
91
+
92
+ Filters transform values inside `{{ ... }}` blocks and can be chained with `|`.
93
+
94
+ Supported filters:
95
+
96
+ - `upcase` / `downcase`
97
+ - `capitalize`
98
+ - `default`
99
+ - `join`
100
+ - `split`
101
+ - `escape`
102
+ - `strip`
103
+ - `replace`
104
+
105
+ Examples:
106
+
107
+ ```liquid
108
+ Hi {{ user.username | default: "Guest" }}
109
+ ```
110
+
111
+ ```liquid
112
+ {{ user.username | default: "Guest" | upcase }}
113
+ ```
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'EOF'
6
+ Validate a Clix personalization template (Liquid-style) with lightweight checks.
7
+
8
+ Usage:
9
+ validate-template.sh <template-file>
10
+
11
+ Checks:
12
+ - Balanced {{ ... }} and {% ... %} delimiters
13
+ - Properly nested {% if %}/{% endif %} and {% for %}/{% endfor %} blocks
14
+
15
+ Notes:
16
+ - This is a best-effort validator; it cannot guarantee that variables exist.
17
+ - If Clix shows a rendering error, always trust Message Logs as the source of truth.
18
+ EOF
19
+ }
20
+
21
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" || "${1:-}" == "" ]]; then
22
+ usage
23
+ exit 0
24
+ fi
25
+
26
+ file="$1"
27
+
28
+ if [[ ! -f "$file" ]]; then
29
+ echo "Error: file not found: $file" >&2
30
+ exit 2
31
+ fi
32
+
33
+ errors=0
34
+
35
+ echo "Validating: $file"
36
+
37
+ brace_counts="$(
38
+ awk '
39
+ {
40
+ c_open_curly += gsub(/\{\{/, "&");
41
+ c_close_curly += gsub(/\}\}/, "&");
42
+ c_open_tag += gsub(/\{%/, "&");
43
+ c_close_tag += gsub(/%\}/, "&");
44
+ }
45
+ END {
46
+ printf("%d %d %d %d\n", c_open_curly, c_close_curly, c_open_tag, c_close_tag);
47
+ }
48
+ ' "$file"
49
+ )"
50
+
51
+ read -r open_curly close_curly open_tag close_tag <<<"$brace_counts"
52
+
53
+ if [[ "$open_curly" -ne "$close_curly" ]]; then
54
+ echo "Error: unbalanced mustache braces: '{{'=$open_curly vs '}}'=$close_curly" >&2
55
+ errors=$((errors + 1))
56
+ fi
57
+
58
+ if [[ "$open_tag" -ne "$close_tag" ]]; then
59
+ echo "Error: unbalanced tag braces: '{%'=$open_tag vs '%}'=$close_tag" >&2
60
+ errors=$((errors + 1))
61
+ fi
62
+
63
+ awk_exit=0
64
+ awk '
65
+ function trim(s) { sub(/^[ \t\r\n]+/, "", s); sub(/[ \t\r\n]+$/, "", s); return s }
66
+ function push(x) { stack[++sp] = x }
67
+ function top() { return stack[sp] }
68
+ function pop() { if (sp > 0) return stack[sp--]; return "" }
69
+
70
+ BEGIN { sp = 0; errors = 0 }
71
+
72
+ {
73
+ line = $0
74
+ while (match(line, /\{%[ \t]*[^%]*%\}/)) {
75
+ tag = substr(line, RSTART, RLENGTH)
76
+ inner = tag
77
+ gsub(/^\{%[ \t]*/, "", inner)
78
+ gsub(/[ \t]*%\}$/, "", inner)
79
+ inner = trim(inner)
80
+
81
+ # Tag name is first token.
82
+ split(inner, parts, /[ \t]+/)
83
+ name = parts[1]
84
+
85
+ if (name == "if") {
86
+ push("if")
87
+ } else if (name == "for") {
88
+ push("for")
89
+ } else if (name == "endif") {
90
+ if (top() != "if") {
91
+ printf("Error: found endif but top of stack is '%s'\n", top()) > "/dev/stderr"
92
+ errors++
93
+ } else {
94
+ pop()
95
+ }
96
+ } else if (name == "endfor") {
97
+ if (top() != "for") {
98
+ printf("Error: found endfor but top of stack is '%s'\n", top()) > "/dev/stderr"
99
+ errors++
100
+ } else {
101
+ pop()
102
+ }
103
+ } else if (name == "else" || name == "elsif") {
104
+ if (top() != "if") {
105
+ printf("Error: found %s outside of if block\n", name) > "/dev/stderr"
106
+ errors++
107
+ }
108
+ }
109
+
110
+ # Move forward in line after the matched tag.
111
+ line = substr(line, RSTART + RLENGTH)
112
+ }
113
+ }
114
+
115
+ END {
116
+ if (sp > 0) {
117
+ printf("Error: unclosed blocks remaining on stack (count=%d)\n", sp) > "/dev/stderr"
118
+ errors++
119
+ }
120
+ exit(errors > 0 ? 1 : 0)
121
+ }
122
+ ' "$file" || awk_exit=$?
123
+
124
+ if [[ "$awk_exit" -ne 0 ]]; then
125
+ errors=$((errors + 1))
126
+ fi
127
+
128
+ if [[ "$errors" -ne 0 ]]; then
129
+ echo "❌ Validation failed ($errors issue(s))." >&2
130
+ exit 1
131
+ fi
132
+
133
+ echo "✅ Template structure looks OK."
134
+