@clix-so/clix-agent-skills 0.1.3 → 0.1.4
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 +34 -15
- package/dist/bin/cli.js +14 -4
- package/dist/bin/commands/install.js +57 -0
- package/package.json +1 -1
- 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,34 @@
|
|
|
1
|
+
# Personalization + Audience Mapping
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- How user properties are used
|
|
6
|
+
- Personalization (`user.*`)
|
|
7
|
+
- Audience filters
|
|
8
|
+
- Common mistakes
|
|
9
|
+
|
|
10
|
+
## How user properties are used
|
|
11
|
+
|
|
12
|
+
User properties power:
|
|
13
|
+
|
|
14
|
+
- **Message personalization** via `user.*`
|
|
15
|
+
- **Audience targeting** (filters on user/custom attributes)
|
|
16
|
+
|
|
17
|
+
## Personalization (`user.*`)
|
|
18
|
+
|
|
19
|
+
Use `user.*` variables in templates to personalize message content and links.
|
|
20
|
+
Missing values render as empty string.
|
|
21
|
+
|
|
22
|
+
## Audience filters
|
|
23
|
+
|
|
24
|
+
Audience builder can filter on:
|
|
25
|
+
|
|
26
|
+
- user id
|
|
27
|
+
- built-in attributes (like last session)
|
|
28
|
+
- custom attributes (your user properties)
|
|
29
|
+
|
|
30
|
+
## Common mistakes
|
|
31
|
+
|
|
32
|
+
- Storing PII (email/phone/name) as user properties by default.
|
|
33
|
+
- Sending inconsistent types for the same property (e.g., `"25"` vs `25`).
|
|
34
|
+
- Renaming keys without migrating campaign filters.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# User Property Schema + PII Guardrails
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Naming rules
|
|
6
|
+
- Allowed types
|
|
7
|
+
- PII guardrails
|
|
8
|
+
- User Plan template
|
|
9
|
+
|
|
10
|
+
## Naming rules
|
|
11
|
+
|
|
12
|
+
- **Property keys**: `snake_case` recommended.
|
|
13
|
+
- Prefer stable keys that won’t be renamed every sprint.
|
|
14
|
+
|
|
15
|
+
## Allowed types
|
|
16
|
+
|
|
17
|
+
- `string`
|
|
18
|
+
- `number`
|
|
19
|
+
- `boolean`
|
|
20
|
+
|
|
21
|
+
If you must send complex objects: stringify intentionally and document it.
|
|
22
|
+
|
|
23
|
+
## PII guardrails
|
|
24
|
+
|
|
25
|
+
Default stance: **do not store PII in user properties** unless explicitly
|
|
26
|
+
approved.
|
|
27
|
+
|
|
28
|
+
Common PII/sensitive values to avoid:
|
|
29
|
+
|
|
30
|
+
- email, phone number, full name
|
|
31
|
+
- free-text fields (notes, messages)
|
|
32
|
+
- government IDs, payment card details
|
|
33
|
+
|
|
34
|
+
Prefer stable internal IDs:
|
|
35
|
+
|
|
36
|
+
- `user_id` (your system’s id) instead of email/phone
|
|
37
|
+
|
|
38
|
+
## User Plan template
|
|
39
|
+
|
|
40
|
+
Create `.clix/user-plan.json` (recommended) or `user-plan.json` in project root:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"conventions": {
|
|
45
|
+
"property_key_case": "snake_case"
|
|
46
|
+
},
|
|
47
|
+
"pii": {
|
|
48
|
+
"allowed": [],
|
|
49
|
+
"blocked": ["email", "phone", "name", "free_text"]
|
|
50
|
+
},
|
|
51
|
+
"user_id": {
|
|
52
|
+
"source": "auth response",
|
|
53
|
+
"example": "user_12345"
|
|
54
|
+
},
|
|
55
|
+
"logout_policy": "do_not_set_user_id_null",
|
|
56
|
+
"properties": {
|
|
57
|
+
"subscription_tier": { "type": "string", "required": false },
|
|
58
|
+
"premium": { "type": "boolean", "required": false },
|
|
59
|
+
"age": { "type": "number", "required": false }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Note**: The validation script validates `logout_policy`, `user_id`, and
|
|
65
|
+
`properties`. The `conventions` and `pii` fields are optional metadata.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Clix User Management Contract
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Core concepts
|
|
6
|
+
- SDK surface (conceptual)
|
|
7
|
+
- User properties types
|
|
8
|
+
- Platform notes
|
|
9
|
+
- Pitfalls
|
|
10
|
+
|
|
11
|
+
## Core concepts
|
|
12
|
+
|
|
13
|
+
- **Anonymous user**: default when no user ID is set.
|
|
14
|
+
- **Identified user**: after setting user ID; historical activity is linked.
|
|
15
|
+
- **Multi-device**: user identity can be consistent across devices when using
|
|
16
|
+
the same user ID.
|
|
17
|
+
|
|
18
|
+
## SDK surface (conceptual)
|
|
19
|
+
|
|
20
|
+
- `setUserId(userId)`
|
|
21
|
+
- `removeUserId()`
|
|
22
|
+
- `setUserProperty(key, value)`
|
|
23
|
+
- `setUserProperties(map)`
|
|
24
|
+
- `removeUserProperty(key)`
|
|
25
|
+
- `removeUserProperties(keys)`
|
|
26
|
+
|
|
27
|
+
## User properties types
|
|
28
|
+
|
|
29
|
+
Prefer JSON primitives:
|
|
30
|
+
|
|
31
|
+
- **string**
|
|
32
|
+
- **number**
|
|
33
|
+
- **boolean**
|
|
34
|
+
|
|
35
|
+
Avoid:
|
|
36
|
+
|
|
37
|
+
- `null` (inconsistent behavior)
|
|
38
|
+
- nested objects/arrays unless you intentionally stringify them
|
|
39
|
+
- PII unless explicitly approved
|
|
40
|
+
|
|
41
|
+
## Platform notes
|
|
42
|
+
|
|
43
|
+
- **iOS**: async + sync APIs exist; async is recommended.
|
|
44
|
+
- **Android**: suspend functions; internal device service maps user ID to a user
|
|
45
|
+
property on the device backend.
|
|
46
|
+
- **React Native**: Promise-based APIs.
|
|
47
|
+
- **Flutter**: async APIs; user property model infers type and stringifies
|
|
48
|
+
unsupported values.
|
|
49
|
+
|
|
50
|
+
## Pitfalls
|
|
51
|
+
|
|
52
|
+
- Calling `setUserId(null)` on logout (don’t).
|
|
53
|
+
- Setting user ID before login is confirmed (causes wrong attribution).
|
|
54
|
+
- Sending PII as user properties by default.
|
|
55
|
+
- Using unstable identifiers (email/phone) as user ID when an internal ID
|
|
56
|
+
exists.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Validate a Clix user management plan (user-plan.json).
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash skills/user-management/scripts/validate-user-plan.sh path/to/user-plan.json
|
|
7
|
+
#
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
plan_path="${1:-}"
|
|
11
|
+
if [[ -z "$plan_path" ]]; then
|
|
12
|
+
echo "Usage: bash skills/user-management/scripts/validate-user-plan.sh path/to/user-plan.json" >&2
|
|
13
|
+
exit 2
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ ! -f "$plan_path" ]]; then
|
|
17
|
+
echo "Error: file not found: $plan_path" >&2
|
|
18
|
+
exit 2
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
validate_with_python() {
|
|
22
|
+
python3 - "$plan_path" <<'PY'
|
|
23
|
+
import json
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
path = sys.argv[1]
|
|
28
|
+
snake = re.compile(r"^[a-z][a-z0-9_]*$")
|
|
29
|
+
|
|
30
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
31
|
+
data = json.load(f)
|
|
32
|
+
|
|
33
|
+
errors = []
|
|
34
|
+
|
|
35
|
+
logout_policy = data.get("logout_policy")
|
|
36
|
+
if logout_policy not in (None, "do_not_set_user_id_null"):
|
|
37
|
+
errors.append("logout_policy must be 'do_not_set_user_id_null' if present")
|
|
38
|
+
|
|
39
|
+
user_id = data.get("user_id")
|
|
40
|
+
if not isinstance(user_id, dict):
|
|
41
|
+
errors.append("user_id must be an object")
|
|
42
|
+
else:
|
|
43
|
+
src = user_id.get("source")
|
|
44
|
+
ex = user_id.get("example")
|
|
45
|
+
if not isinstance(src, str) or not src.strip():
|
|
46
|
+
errors.append("user_id.source must be a non-empty string")
|
|
47
|
+
if ex is not None and (not isinstance(ex, str) or not ex.strip()):
|
|
48
|
+
errors.append("user_id.example must be a non-empty string if present")
|
|
49
|
+
|
|
50
|
+
props = data.get("properties")
|
|
51
|
+
if not isinstance(props, dict) or not props:
|
|
52
|
+
errors.append("properties must be a non-empty object")
|
|
53
|
+
else:
|
|
54
|
+
for key, spec in props.items():
|
|
55
|
+
if not isinstance(key, str) or not snake.match(key):
|
|
56
|
+
errors.append(f"properties key '{key}' must be snake_case")
|
|
57
|
+
continue
|
|
58
|
+
if not isinstance(spec, dict):
|
|
59
|
+
errors.append(f"properties['{key}'] must be an object")
|
|
60
|
+
continue
|
|
61
|
+
t = spec.get("type")
|
|
62
|
+
if t is None:
|
|
63
|
+
errors.append(f"properties['{key}'].type is required")
|
|
64
|
+
elif t not in ("string", "number", "boolean"):
|
|
65
|
+
errors.append(f"properties['{key}'].type must be one of: string, number, boolean")
|
|
66
|
+
req = spec.get("required")
|
|
67
|
+
if req is not None and not isinstance(req, bool):
|
|
68
|
+
errors.append(f"properties['{key}'].required must be boolean if present")
|
|
69
|
+
|
|
70
|
+
if errors:
|
|
71
|
+
print("❌ user-plan validation failed:")
|
|
72
|
+
for e in errors:
|
|
73
|
+
print(f"- {e}")
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
print("✅ user-plan validation passed")
|
|
77
|
+
PY
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
81
|
+
validate_with_python
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo "Warning: python3 not found; only checking JSON validity with node if available." >&2
|
|
86
|
+
if command -v node >/dev/null 2>&1; then
|
|
87
|
+
node -e "JSON.parse(require('fs').readFileSync(process.argv[1], 'utf8')); console.log('✅ JSON is valid');" "$plan_path"
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo "Error: neither python3 nor node found; cannot validate." >&2
|
|
92
|
+
exit 2
|