@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.
- package/README.md +60 -16
- package/dist/bin/cli.js +15 -4
- package/dist/bin/commands/install.js +66 -3
- package/dist/bin/utils/mcp.js +8 -10
- package/llms.txt +77 -0
- package/package.json +5 -3
- package/skills/event-tracking/LICENSE.txt +203 -0
- package/skills/event-tracking/SKILL.md +126 -0
- package/skills/event-tracking/references/campaign-mapping.md +29 -0
- package/skills/event-tracking/references/debugging.md +30 -0
- package/skills/event-tracking/references/implementation-patterns.md +45 -0
- package/skills/event-tracking/references/naming-and-schema.md +67 -0
- package/skills/event-tracking/references/trackevent-contract.md +68 -0
- package/skills/event-tracking/scripts/validate-event-plan.sh +115 -0
- package/skills/integration/LICENSE.txt +2 -1
- package/skills/user-management/LICENSE.txt +203 -0
- package/skills/user-management/SKILL.md +114 -0
- package/skills/user-management/references/debugging.md +29 -0
- package/skills/user-management/references/implementation-patterns.md +37 -0
- package/skills/user-management/references/logout-and-switching.md +32 -0
- package/skills/user-management/references/personalization-and-audience.md +34 -0
- package/skills/user-management/references/property-schema.md +65 -0
- package/skills/user-management/references/user-management-contract.md +56 -0
- package/skills/user-management/scripts/validate-user-plan.sh +92 -0
|
@@ -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
|
|
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.
|