@clix-so/clix-agent-skills 0.1.3 → 0.1.5

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,126 @@
1
+ ---
2
+ name: clix-event-tracking
3
+ description:
4
+ Implements Clix event tracking (Clix.trackEvent) with consistent naming, safe
5
+ property schemas, and campaign-ready validation. Use when adding, reviewing,
6
+ or debugging event tracking; when configuring event-triggered campaigns; or
7
+ when the user mentions events, tracking, funnels, or properties.
8
+ ---
9
+
10
+ # Tracking Clix Events
11
+
12
+ Use this skill to help developers design and implement **Clix event tracking**
13
+ via `Clix.trackEvent(...)` so events can drive **event-triggered campaigns**,
14
+ **audience filters**, and **personalization**.
15
+
16
+ ## What the official docs guarantee (high-signal)
17
+
18
+ - **When to track**: identify the user action/system event, then attach
19
+ meaningful properties (funnel checkpoints, milestones, button taps).
20
+ - **Property normalization**: booleans/numbers/strings map directly; date-like
21
+ values serialize to ISO 8601; unsupported objects become strings.
22
+ - **Error handling**: event tracking calls can throw—always handle errors so the
23
+ app stays robust.
24
+
25
+ ## MCP-first (source of truth)
26
+
27
+ If Clix MCP tools are available, treat them as the **source of truth**:
28
+
29
+ - `clix-mcp-server:search_docs` for conceptual behavior (campaign triggers,
30
+ personalization)
31
+ - `clix-mcp-server:search_sdk` for exact SDK signatures per platform
32
+
33
+ If MCP tools are not available, use the bundled references:
34
+
35
+ - Event API contract + pitfalls → `references/trackevent-contract.md`
36
+ - Naming + schemas + privacy → `references/naming-and-schema.md`
37
+ - Implementation patterns → `references/implementation-patterns.md`
38
+ - Campaign mapping → `references/campaign-mapping.md`
39
+ - Debugging checklist → `references/debugging.md`
40
+
41
+ ## Workflow (copy + check off)
42
+
43
+ ```
44
+ Event tracking progress:
45
+ - [ ] 1) Confirm platform(s) and goals (analytics vs campaign triggers)
46
+ - [ ] 2) Propose event plan (names, when fired, properties, where in code)
47
+ - [ ] 3) Validate plan (names, keys, types, PII constraints)
48
+ - [ ] 4) Implement trackEvent calls (platform-correct)
49
+ - [ ] 5) Verify: events fire once, serialize cleanly, match campaign configs
50
+ ```
51
+
52
+ ## 1) Confirm the minimum inputs
53
+
54
+ Ask only what’s needed:
55
+
56
+ - **Platform**: iOS / Android / React Native / Flutter
57
+ - **Goal**: analytics only, event-triggered campaigns, or both
58
+ - **Top flows** (1–3): e.g., onboarding, checkout, subscription
59
+ - **PII policy**: what must never be sent (email/phone/name/free-text, etc.)
60
+
61
+ ## 2) Propose an “Event Plan” (before touching code)
62
+
63
+ Return a compact table the user can approve:
64
+
65
+ - **event_name** (stable, `snake_case`)
66
+ - **when** (exact moment the event fires)
67
+ - **properties** (key + type, mark required vs optional)
68
+ - **location** (file/function/UI handler/network response)
69
+ - **purpose** (campaign trigger / analytics / both)
70
+
71
+ If campaigns are involved, remind: **event names and property keys must match
72
+ exactly** in the Clix console.
73
+
74
+ ## 3) Validate the plan (fast feedback loop)
75
+
76
+ Create `event-plan.json` in `.clix/` directory (recommended) or project root:
77
+
78
+ **Recommended location**: `.clix/event-plan.json`
79
+
80
+ - Organized: keeps tooling configs together
81
+ - Hidden: doesn't clutter project root
82
+ - Committable: planning document for team review
83
+
84
+ **Alternative**: `event-plan.json` in project root (simpler, but less organized)
85
+
86
+ **For agents**: Locate `scripts/validate-event-plan.sh` in the installed skill
87
+ directory, then run it:
88
+
89
+ ```bash
90
+ # From project root:
91
+ bash <skill-dir>/scripts/validate-event-plan.sh .clix/event-plan.json
92
+ # Or if in root:
93
+ bash <skill-dir>/scripts/validate-event-plan.sh event-plan.json
94
+ ```
95
+
96
+ The skill directory is typically:
97
+
98
+ - `.cursor/skills/event-tracking/` (Cursor)
99
+ - `.claude/skills/event-tracking/` (Claude Desktop)
100
+ - `.vscode/skills/event-tracking/` (VS Code/Amp)
101
+ - Or check where this skill was installed
102
+
103
+ If validation fails: fix the plan first, then implement.
104
+
105
+ ## 4) Implement tracking (platform-correct)
106
+
107
+ Use MCP to fetch the exact `trackEvent` signature for the platform; then:
108
+
109
+ - **Place calls at stable boundaries** (action confirmed, request succeeded,
110
+ state updated)
111
+ - **Avoid duplicates** (don’t fire on every render; debounce where needed)
112
+ - **Avoid null/complex values** (prefer primitives; serialize dates to ISO)
113
+ - **Do not track PII by default**
114
+
115
+ See `references/implementation-patterns.md` for placement heuristics and code
116
+ patterns.
117
+
118
+ ## 5) Verify
119
+
120
+ Minimum verification:
121
+
122
+ - Event fires **exactly once** per user action (or the intended cadence)
123
+ - Properties are **primitive + stable** (no null surprises)
124
+ - For campaigns: console trigger conditions match **exact names/keys**
125
+
126
+ For troubleshooting steps, see `references/debugging.md`.
@@ -0,0 +1,29 @@
1
+ # Campaign Mapping (Event-triggered)
2
+
3
+ ## Contents
4
+
5
+ - What matters for event-triggered campaigns
6
+ - Mapping checklist
7
+ - Common mistakes
8
+
9
+ ## What matters
10
+
11
+ - Triggers only apply to **new incoming events**
12
+ - Campaign trigger config must match:
13
+ - **event name** exactly
14
+ - **property keys** exactly
15
+ - (often) **property types** as expected
16
+
17
+ ## Mapping checklist
18
+
19
+ - Confirm the app sends `event_name` with the exact spelling used in the console
20
+ - Confirm property keys are stable and `snake_case` (recommended)
21
+ - If filtering on properties in the console, ensure:
22
+ - values are present (not null)
23
+ - value types are consistent (number vs string)
24
+
25
+ ## Common mistakes
26
+
27
+ - Tracking `productId` but filtering on `product_id`
28
+ - Sending numbers as strings (`"14"` vs `14`)
29
+ - Tracking too early (before the action is truly completed)
@@ -0,0 +1,30 @@
1
+ # Debugging Event Tracking
2
+
3
+ ## Contents
4
+
5
+ - Quick triage
6
+ - Common causes
7
+ - Verification checklist
8
+
9
+ ## Quick triage
10
+
11
+ 1. Confirm `Clix.initialize(...)` is called before any tracking.
12
+ 2. Confirm the event is fired exactly once (no render loops).
13
+ 3. Confirm properties are primitive and non-null.
14
+ 4. If using campaigns: confirm console trigger matches event name + property
15
+ keys exactly.
16
+
17
+ ## Common causes
18
+
19
+ - **Not initialized**: tracking called before init completes.
20
+ - **Duplicate events**: track call placed in render/recomposition or repeated
21
+ listeners.
22
+ - **Bad property types**: objects/arrays, inconsistent types, nulls.
23
+ - **Mismatch with campaigns**: name/key mismatch, different casing.
24
+
25
+ ## Verification checklist
26
+
27
+ - Add temporary logs around the tracking call (and remove after verification)
28
+ - Use a stable test flow to reproduce (single tap, single request)
29
+ - Verify in Clix console that events appear (and that campaigns fire when
30
+ expected)
@@ -0,0 +1,45 @@
1
+ # Implementation Patterns
2
+
3
+ ## Contents
4
+
5
+ - Where to place trackEvent calls
6
+ - De-duplication guidance
7
+ - Property hygiene
8
+ - Platform notes
9
+
10
+ ## Where to place trackEvent calls
11
+
12
+ Prefer stable boundaries (low risk of duplicate firing):
13
+
14
+ - **User action confirmed**: button tap handler, after local validation passes
15
+ - **Network success**: after API responds 2xx / mutation completes
16
+ - **State change**: after the app updates state that the user can observe
17
+
18
+ Avoid:
19
+
20
+ - Render functions / recompositions
21
+ - Generic “screen mounted” hooks without guards
22
+
23
+ ## De-duplication guidance
24
+
25
+ Ensure “once per action”:
26
+
27
+ - Debounce rapid taps where relevant
28
+ - Track on success callbacks rather than optimistic UI (unless intentional)
29
+ - For navigation-based events, ensure one event per screen transition
30
+
31
+ ## Property hygiene
32
+
33
+ - Remove `null` values before sending (recommended across platforms)
34
+ - Keep property values small and stable
35
+ - Prefer enums/known strings over arbitrary user text
36
+
37
+ ## Platform notes (what the skill should remember)
38
+
39
+ - **iOS**: properties are coerced to primitives; `Date` → ISO; other objects →
40
+ string.
41
+ - **Android**: unknown objects become ISO (if recognized) else string; avoid
42
+ nulls.
43
+ - **React Native**: `Date` is serialized to ISO; other values pass through.
44
+ - **Flutter**: null properties are filtered out; supports `messageId` in event
45
+ API.
@@ -0,0 +1,67 @@
1
+ # Naming and Schema
2
+
3
+ ## Contents
4
+
5
+ - Naming rules
6
+ - Property rules
7
+ - Privacy / PII guardrails
8
+ - Event Plan template
9
+
10
+ ## Naming rules
11
+
12
+ - **Event names**: `snake_case` (recommended)
13
+ - Good: `signup_completed`, `purchase_completed`, `screen_viewed`
14
+ - Avoid: spaces, mixed casing, unstable suffixes (timestamps, random IDs)
15
+ - **Property keys**: also `snake_case`
16
+ - Good: `screen_name`, `payment_method`
17
+ - Avoid: `camelCase` mixed with `snake_case` in the same project
18
+
19
+ ## Property rules
20
+
21
+ - Prefer JSON primitives:
22
+ - `string`, `number`, `boolean`
23
+ - Datetimes:
24
+ - Prefer ISO 8601 strings or native date types supported by the platform SDK
25
+ - Avoid:
26
+ - `null` (inconsistent across platforms)
27
+ - nested objects/arrays unless you intentionally stringify them
28
+
29
+ ## Privacy / PII guardrails
30
+
31
+ Default stance: **do not track PII** unless the user explicitly approves.
32
+
33
+ ## Event Plan template
34
+
35
+ Create `event-plan.json` in `.clix/` directory (recommended) or project root:
36
+
37
+ **Recommended**: `.clix/event-plan.json` (organized, hidden, committable)
38
+ **Alternative**: `event-plan.json` in project root (simpler)
39
+
40
+ ```json
41
+ {
42
+ "conventions": {
43
+ "event_name_case": "snake_case",
44
+ "property_key_case": "snake_case"
45
+ },
46
+ "pii": {
47
+ "allowed": [],
48
+ "blocked": ["email", "phone", "name", "free_text"]
49
+ },
50
+ "events": [
51
+ {
52
+ "name": "signup_completed",
53
+ "when": "after backend confirms account creation",
54
+ "purpose": ["analytics", "campaigns"],
55
+ "properties": {
56
+ "method": { "type": "string", "required": true },
57
+ "trial_days": { "type": "number", "required": false }
58
+ }
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ **Note**: The `conventions` and `pii` fields are optional metadata for
65
+ documentation purposes. The validation script only validates the `events` array
66
+ structure. These fields help document your team's naming conventions and PII
67
+ policies but are not enforced by the validator.
@@ -0,0 +1,68 @@
1
+ # Clix.trackEvent Contract
2
+
3
+ ## Contents
4
+
5
+ - Contract summary
6
+ - Payload shape (conceptual)
7
+ - Property serialization rules
8
+ - Platform differences (practical)
9
+ - Pitfalls (what breaks campaigns)
10
+
11
+ ## Contract summary
12
+
13
+ - **Event name**: a stable string (recommended `snake_case`)
14
+ - **Properties**: a key/value map; prefer JSON primitives
15
+ - **Initialization**: tracking requires the SDK to be initialized first
16
+ - **Campaign usage**: event-triggered campaigns require **exact matches** for
17
+ event name + property keys
18
+
19
+ ## Payload shape (conceptual)
20
+
21
+ SDKs send events to the Clix API as an array under `events`:
22
+
23
+ - `device_id`
24
+ - `name`
25
+ - `event_property.custom_properties` (your properties)
26
+ - Optional metadata fields used by the SDK (varies by platform):
27
+ - `message_id`
28
+ - `user_journey_id`
29
+ - `user_journey_node_id`
30
+
31
+ ## Property serialization rules
32
+
33
+ Keep properties simple:
34
+
35
+ - **Allowed**: `string`, `number`, `boolean`, ISO datetime strings
36
+ - **Avoid**: nested objects/arrays, large blobs, free-text user content, PII
37
+ - **Avoid nulls**: some SDKs drop nulls; others may stringify or encode null →
38
+ inconsistent analytics
39
+
40
+ Dates:
41
+
42
+ - Prefer passing a native date type (SDK will serialize) or an ISO string you
43
+ control.
44
+
45
+ ## Platform differences (practical)
46
+
47
+ - **iOS**:
48
+ - Drops `nil` properties.
49
+ - Serializes `Date` to ISO.
50
+ - Supports extra metadata params in the internal event layer.
51
+ - **Android**:
52
+ - Converts unknown objects to ISO (if recognized) else string.
53
+ - Null handling can be inconsistent; avoid null values.
54
+ - **React Native**:
55
+ - Serializes `Date` properties to ISO strings.
56
+ - Supports `messageId` + journey IDs at the API layer.
57
+ - **Flutter**:
58
+ - Drops null properties.
59
+ - Supports `messageId` (journey IDs may not be exposed in the Dart event API
60
+ layer).
61
+
62
+ ## Pitfalls (what breaks campaigns)
63
+
64
+ - Event-triggered campaign never fires because:
65
+ - Event name mismatch (`signup_completed` vs `sign_up_completed`)
66
+ - Property key mismatch (`productId` vs `product_id`)
67
+ - Property type mismatch (number sent as string)
68
+ - Event fired too early / too often (duplicates)
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Validate a Clix event tracking plan (event-plan.json).
4
+ #
5
+ # Usage:
6
+ # bash skills/event-tracking/scripts/validate-event-plan.sh path/to/event-plan.json
7
+ #
8
+ # What it validates:
9
+ # - JSON is valid
10
+ # - event names and property keys are snake_case
11
+ # - required fields exist (name/when/purpose/properties)
12
+ #
13
+ set -euo pipefail
14
+
15
+ plan_path="${1:-}"
16
+ if [[ -z "$plan_path" ]]; then
17
+ echo "Usage: bash skills/event-tracking/scripts/validate-event-plan.sh path/to/event-plan.json" >&2
18
+ exit 2
19
+ fi
20
+
21
+ if [[ ! -f "$plan_path" ]]; then
22
+ echo "Error: file not found: $plan_path" >&2
23
+ exit 2
24
+ fi
25
+
26
+ validate_with_python() {
27
+ python3 - "$plan_path" <<'PY'
28
+ import json
29
+ import re
30
+ import sys
31
+
32
+ path = sys.argv[1]
33
+
34
+ snake = re.compile(r"^[a-z][a-z0-9_]*$")
35
+
36
+ with open(path, "r", encoding="utf-8") as f:
37
+ data = json.load(f)
38
+
39
+ errors = []
40
+
41
+ events = data.get("events")
42
+ if not isinstance(events, list) or not events:
43
+ errors.append("events must be a non-empty array")
44
+ else:
45
+ seen_names = set()
46
+ for i, ev in enumerate(events):
47
+ name = ev.get("name")
48
+ if not isinstance(name, str) or not snake.match(name):
49
+ errors.append(f"events[{i}].name must be snake_case string")
50
+ elif name in seen_names:
51
+ errors.append(f"events[{i}].name is duplicated: '{name}'")
52
+ else:
53
+ seen_names.add(name)
54
+
55
+ when = ev.get("when")
56
+ if not isinstance(when, str) or not when.strip():
57
+ errors.append(f"events[{i}].when must be a non-empty string")
58
+
59
+ purpose = ev.get("purpose")
60
+ if not isinstance(purpose, list) or not purpose:
61
+ errors.append(f"events[{i}].purpose must be a non-empty array")
62
+ else:
63
+ allowed = {"analytics", "campaigns"}
64
+ bad = [p for p in purpose if not isinstance(p, str) or p not in allowed]
65
+ if bad:
66
+ errors.append(
67
+ f"events[{i}].purpose entries must be one of: analytics, campaigns"
68
+ )
69
+
70
+ props = ev.get("properties", {})
71
+ if props is None:
72
+ props = {}
73
+ if not isinstance(props, dict):
74
+ errors.append(f"events[{i}].properties must be an object")
75
+ else:
76
+ for key, spec in props.items():
77
+ if not isinstance(key, str) or not snake.match(key):
78
+ errors.append(f"events[{i}].properties key '{key}' must be snake_case")
79
+ if not isinstance(spec, dict):
80
+ errors.append(f"events[{i}].properties['{key}'] must be an object")
81
+ continue
82
+ t = spec.get("type")
83
+ if t is None:
84
+ errors.append(f"events[{i}].properties['{key}'].type is required")
85
+ elif t not in ("string", "number", "boolean", "datetime"):
86
+ errors.append(
87
+ f"events[{i}].properties['{key}'].type must be one of: string, number, boolean, datetime"
88
+ )
89
+ req = spec.get("required")
90
+ if req is not None and not isinstance(req, bool):
91
+ errors.append(f"events[{i}].properties['{key}'].required must be boolean if present")
92
+
93
+ if errors:
94
+ print("❌ event-plan validation failed:")
95
+ for e in errors:
96
+ print(f"- {e}")
97
+ sys.exit(1)
98
+
99
+ print("✅ event-plan validation passed")
100
+ PY
101
+ }
102
+
103
+ if command -v python3 >/dev/null 2>&1; then
104
+ validate_with_python
105
+ exit 0
106
+ fi
107
+
108
+ echo "Warning: python3 not found; only checking JSON validity with node if available." >&2
109
+ if command -v node >/dev/null 2>&1; then
110
+ node -e "JSON.parse(require('fs').readFileSync(process.argv[1], 'utf8')); console.log('✅ JSON is valid');" "$plan_path"
111
+ exit 0
112
+ fi
113
+
114
+ echo "Error: neither python3 nor node found; cannot validate." >&2
115
+ exit 2
@@ -1,3 +1,4 @@
1
+ Copyright (c) 2026 Clix (https://clix.so/)
1
2
 
2
3
  Apache License
3
4
  Version 2.0, January 2004
@@ -187,7 +188,7 @@
187
188
  same "printed page" as the copyright notice for easier
188
189
  identification within third-party archives.
189
190
 
190
- Copyright [yyyy] [name of copyright owner]
191
+ Copyright (c) 2026 Clix (https://clix.so/)
191
192
 
192
193
  Licensed under the Apache License, Version 2.0 (the "License");
193
194
  you may not use this file except in compliance with the License.