@layers/amba 1.0.1 → 4.0.2
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 +45 -22
- package/dist/api-client.d.ts +49 -29
- package/dist/auth.d.ts +8 -0
- package/dist/bundle.d.ts +23 -9
- package/dist/commands/billing.d.ts +34 -0
- package/dist/commands/claim.d.ts +41 -0
- package/dist/commands/functions.d.ts +28 -3
- package/dist/commands/init.d.ts +69 -0
- package/dist/commands/projects.d.ts +8 -0
- package/dist/credentials.d.ts +259 -0
- package/dist/index.js +3657 -425
- package/dist/project-config.d.ts +11 -0
- package/dist/sandbox.d.ts +309 -0
- package/dist/skill-installer.d.ts +95 -0
- package/dist/skills/presets.d.ts +146 -0
- package/dist/skills.d.ts +266 -0
- package/package.json +7 -2
- package/skill-bundle/SKILL.md +324 -0
- package/skill-bundle/references/economy.md +331 -0
- package/skill-bundle/references/engagement.md +400 -0
- package/skill-bundle/references/gamification.md +316 -0
- package/skill-bundle/references/identity.md +395 -0
- package/skill-bundle/references/infrastructure.md +348 -0
- package/skill-bundle/references/social.md +366 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Gamification
|
|
2
|
+
|
|
3
|
+
Five primitives that turn a flat app into something users come back to: XP rules (auto-award points on a tracked event), achievements (badges that unlock on criteria), streaks (consecutive-period qualification), leaderboards (rank by metric), and challenges (time-bounded goals with rewards). All five are define-once / use-many: you create the definitions via MCP, the SDK qualifies / claims / reads against them at runtime.
|
|
4
|
+
|
|
5
|
+
The pattern is always:
|
|
6
|
+
|
|
7
|
+
1. Agent (via MCP): define the rule / achievement / streak / etc.
|
|
8
|
+
2. Client SDK at runtime: track the event (`Amba.events.track(...)`) or qualify (`Amba.streaks.qualify(...)` / `Amba.challenges.claim(...)`).
|
|
9
|
+
3. Server: auto-evaluate, mutate user state, return updated progress.
|
|
10
|
+
|
|
11
|
+
## MCP tools
|
|
12
|
+
|
|
13
|
+
### XP rules
|
|
14
|
+
|
|
15
|
+
| Tool | Purpose | Example args |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `amba_xp_rules_create` / `amba_create_xp_rule` | Auto-award XP on a matching event. | `{ project_id, name: "Workout Completed", event_name: "workout_completed", xp_amount: 50, max_per_day: 5, cooldown_seconds: 60 }` |
|
|
18
|
+
| `amba_xp_rules_list` / `amba_list_xp_rules` | List all XP rules. | `{ project_id }` |
|
|
19
|
+
| `amba_xp_update_rule` | Edit a rule. | `{ project_id, rule_id, xp_amount: 75 }` |
|
|
20
|
+
| `amba_xp_delete_rule` | Delete a rule. | `{ project_id, rule_id }` |
|
|
21
|
+
| `amba_xp_list_users` | List users sorted by XP (for an admin panel or smoke-test). | `{ project_id, limit: 50 }` |
|
|
22
|
+
| `amba_users_get_xp` / `amba_get_user_xp` | Read a specific user's XP total + level. | `{ project_id, user_id }` |
|
|
23
|
+
|
|
24
|
+
### Achievements
|
|
25
|
+
|
|
26
|
+
| Tool | Purpose | Example args |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `amba_achievements_create` / `amba_create_achievement` | Define an achievement that unlocks on criteria. | `{ project_id, key: "first_workout", name: "First Workout", description: "Complete your first workout", xp_reward: 100, criteria: { event: "workout_completed", count: 1 } }` |
|
|
29
|
+
| `amba_achievements_list` / `amba_list_achievements` | List all achievements. | `{ project_id }` |
|
|
30
|
+
| `amba_achievements_get` / `amba_get_achievement` | Read one. | `{ project_id, achievement_id }` |
|
|
31
|
+
| `amba_achievements_update` / `amba_update_achievement` | Edit an achievement. | `{ project_id, achievement_id, xp_reward: 150 }` |
|
|
32
|
+
| `amba_achievements_delete` / `amba_delete_achievement` | Delete. | `{ project_id, achievement_id }` |
|
|
33
|
+
|
|
34
|
+
Common criteria shapes (the schema is permissive — the server validates against the available metrics):
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
// Count-based
|
|
38
|
+
{ "event": "workout_completed", "count": 5 }
|
|
39
|
+
|
|
40
|
+
// Streak-based
|
|
41
|
+
{ "streak_key": "daily_workout", "min_length": 7 }
|
|
42
|
+
|
|
43
|
+
// XP-based
|
|
44
|
+
{ "xp_total": 5000 }
|
|
45
|
+
|
|
46
|
+
// Catalog-item-based
|
|
47
|
+
{ "item_owned": "premium_theme" }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Streaks
|
|
51
|
+
|
|
52
|
+
| Tool | Purpose | Example args |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `amba_streaks_create` / `amba_create_streak` | Define a streak — what event qualifies, what period, freeze rules. | `{ project_id, key: "daily_workout", name: "Daily Workout", qualifying_event: "workout_completed", period: "daily", grace_period_hours: 6, freeze_enabled: true, max_freezes: 3 }` |
|
|
55
|
+
| `amba_streaks_list` | List streaks. | `{ project_id }` |
|
|
56
|
+
| `amba_streaks_update` | Edit a streak definition. | `{ project_id, streak_id, max_freezes: 5 }` |
|
|
57
|
+
| `amba_streaks_delete` | Delete. | `{ project_id, streak_id }` |
|
|
58
|
+
|
|
59
|
+
The `key` is **immutable** after creation (the SDK identifies streaks by key, not UUID — changing it breaks live `Amba.streaks.qualify(...)` calls). Keys: lowercase letters, digits, `_`, `-`, 1-64 chars.
|
|
60
|
+
|
|
61
|
+
### Leaderboards
|
|
62
|
+
|
|
63
|
+
| Tool | Purpose | Example args |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `amba_leaderboards_create` / `amba_create_leaderboard` | Define a leaderboard. | `{ project_id, name: "Weekly XP", metric: "xp", period: "weekly", max_entries: 100 }` |
|
|
66
|
+
| `amba_leaderboards_list` / `amba_list_leaderboards` | List. | `{ project_id }` |
|
|
67
|
+
| `amba_leaderboards_get` / `amba_get_leaderboard` | Read the current top entries. | `{ project_id, leaderboard_id, limit: 50 }` |
|
|
68
|
+
| `amba_leaderboards_get_definition` / `amba_get_leaderboard_definition` | Read the definition without the entries (cheap). | `{ project_id, leaderboard_id }` |
|
|
69
|
+
| `amba_leaderboards_update` / `amba_update_leaderboard` | Edit the period / max_entries. | `{ project_id, leaderboard_id, max_entries: 250 }` |
|
|
70
|
+
| `amba_leaderboards_delete` / `amba_delete_leaderboard` | Delete. | `{ project_id, leaderboard_id }` |
|
|
71
|
+
|
|
72
|
+
Metrics: `xp`, `streak`, `custom`. For `custom`, pass `custom_event` (e.g. `"workout_completed"`) — the leaderboard ranks by the count of that event in the period. Periods: `all_time`, `daily`, `weekly`, `monthly`.
|
|
73
|
+
|
|
74
|
+
### Challenges
|
|
75
|
+
|
|
76
|
+
| Tool | Purpose | Example args |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `amba_challenges_create` / `amba_create_challenge` | Define a time-bounded challenge with a goal + reward. | `{ project_id, name: "Spring Sprint", description: "5 workouts in 7 days", goal_event: "workout_completed", goal_count: 5, starts_at: "2026-06-01T00:00:00Z", ends_at: "2026-06-08T00:00:00Z", reward: { xp: 500, currency: { code: "gems", amount: 50 } } }` |
|
|
79
|
+
| `amba_challenges_list` / `amba_list_challenges` | List. | `{ project_id }` |
|
|
80
|
+
| `amba_challenges_get` / `amba_get_challenge` | Read. | `{ project_id, challenge_id }` |
|
|
81
|
+
| `amba_challenges_update` / `amba_update_challenge` | Edit. | `{ project_id, challenge_id, ends_at: "..." }` |
|
|
82
|
+
| `amba_challenges_delete` / `amba_delete_challenge` | Delete. | `{ project_id, challenge_id }` |
|
|
83
|
+
| `amba_challenges_list_participants` / `amba_list_challenge_participants` | List opt-in participants + progress. | `{ project_id, challenge_id, limit: 100 }` |
|
|
84
|
+
|
|
85
|
+
## SDK init per stack
|
|
86
|
+
|
|
87
|
+
`Amba.configure(...)` runs first — see `identity.md`. The snippets below show only the gamification calls.
|
|
88
|
+
|
|
89
|
+
### Expo
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { Amba } from '@layers/amba-expo';
|
|
93
|
+
|
|
94
|
+
// 1. Track the event that drives XP / achievements / streaks / leaderboards.
|
|
95
|
+
// XP rules + achievements + streaks + leaderboards all match on event_name,
|
|
96
|
+
// so a single track() call can fire all of them.
|
|
97
|
+
async function onWorkoutCompleted(durationMinutes: number) {
|
|
98
|
+
await Amba.events.track('workout_completed', { duration_minutes: durationMinutes });
|
|
99
|
+
|
|
100
|
+
// 2. Qualify the streak. The server keeps the consecutive-period count;
|
|
101
|
+
// the SDK returns the updated state.
|
|
102
|
+
const streak = await Amba.streaks.qualify('daily_workout');
|
|
103
|
+
// streak.current_count, streak.longest_count, streak.status, streak.freezes_remaining
|
|
104
|
+
|
|
105
|
+
// 3. Show the user any newly-unlocked achievements (poll progress).
|
|
106
|
+
const progress = await Amba.achievements.getProgress();
|
|
107
|
+
const newlyUnlocked = progress.filter(p => p.unlocked && p.unlocked_within_seconds < 5);
|
|
108
|
+
|
|
109
|
+
// 4. Show their current XP balance.
|
|
110
|
+
const xp = await Amba.xp.getBalance();
|
|
111
|
+
// xp.total, xp.level, xp.next_level_xp
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 5. Read the leaderboard for a UI screen.
|
|
115
|
+
async function loadLeaderboard(leaderboardKey: string) {
|
|
116
|
+
const entries = await Amba.leaderboards.getEntries(leaderboardKey, 50);
|
|
117
|
+
const myRank = await Amba.leaderboards.getMyRank(leaderboardKey);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 6. Active challenges (e.g. for a "Challenges" tab).
|
|
121
|
+
async function loadChallenges() {
|
|
122
|
+
const active = await Amba.challenges.getActive();
|
|
123
|
+
for (const c of active) {
|
|
124
|
+
const progress = await Amba.challenges.getProgress(c.id);
|
|
125
|
+
if (progress.completed && !progress.claimed) {
|
|
126
|
+
await Amba.challenges.claim(c.id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### React Native (bare)
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { Amba } from '@layers/amba-react-native';
|
|
136
|
+
|
|
137
|
+
await Amba.events.track('workout_completed', { duration_minutes: 30 });
|
|
138
|
+
const streak = await Amba.streaks.qualify('daily_workout');
|
|
139
|
+
const achievements = await Amba.achievements.getProgress();
|
|
140
|
+
const entries = await Amba.leaderboards.getEntries('Weekly XP', 50);
|
|
141
|
+
const active = await Amba.challenges.getActive();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Web (browser / Next.js / Vite)
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { Amba } from '@layers/amba-web';
|
|
148
|
+
|
|
149
|
+
await Amba.events.track('lesson_completed', { course_id: 'algebra-1' });
|
|
150
|
+
const streak = await Amba.streaks.qualify('daily_lesson');
|
|
151
|
+
const xp = await Amba.xp.getBalance();
|
|
152
|
+
const top = await Amba.leaderboards.getEntries('Weekly XP', 100);
|
|
153
|
+
const myRank = await Amba.leaderboards.getMyRank('Weekly XP');
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
With React hooks (`@layers/amba-react`) — there is no dedicated `useXp` / `useStreak` hook in 1.0, so wrap the SDK calls in your own `useEffect`:
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { useEffect, useState } from 'react';
|
|
160
|
+
import { Amba } from '@layers/amba-web';
|
|
161
|
+
|
|
162
|
+
export function useStreak(key: string) {
|
|
163
|
+
const [streak, setStreak] = useState<Streak | null>(null);
|
|
164
|
+
const refresh = async () => {
|
|
165
|
+
const all = await Amba.streaks.getAll();
|
|
166
|
+
setStreak(all.find(s => s.key === key) ?? null);
|
|
167
|
+
};
|
|
168
|
+
useEffect(() => { refresh(); }, [key]);
|
|
169
|
+
return { streak, refresh };
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### iOS (Swift)
|
|
174
|
+
|
|
175
|
+
```swift
|
|
176
|
+
import Amba
|
|
177
|
+
|
|
178
|
+
try await Amba.events.track("workout_completed", properties: ["duration_minutes": 30])
|
|
179
|
+
let streak = try await Amba.streaks.qualify(streakKey: "daily_workout")
|
|
180
|
+
let progress = try await Amba.achievements.getProgress()
|
|
181
|
+
let xp = try await Amba.xp.getBalance()
|
|
182
|
+
let entries = try await Amba.leaderboards.getEntries(key: "Weekly XP", limit: 50)
|
|
183
|
+
let myRank = try await Amba.leaderboards.getMyRank(key: "Weekly XP")
|
|
184
|
+
let active = try await Amba.challenges.getActive()
|
|
185
|
+
for challenge in active {
|
|
186
|
+
let p = try await Amba.challenges.getProgress(id: challenge.id)
|
|
187
|
+
if p.completed && !p.claimed {
|
|
188
|
+
_ = try await Amba.challenges.claim(id: challenge.id)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Android (Kotlin)
|
|
194
|
+
|
|
195
|
+
```kotlin
|
|
196
|
+
Amba.events.track("workout_completed", mapOf("duration_minutes" to 30))
|
|
197
|
+
val streak = Amba.streaks.qualify("daily_workout")
|
|
198
|
+
val progress = Amba.achievements.getProgress()
|
|
199
|
+
val xp = Amba.xp.getBalance()
|
|
200
|
+
val entries = Amba.leaderboards.getEntries("Weekly XP", limit = 50)
|
|
201
|
+
val myRank = Amba.leaderboards.getMyRank("Weekly XP")
|
|
202
|
+
val active = Amba.challenges.getActive()
|
|
203
|
+
for (c in active) {
|
|
204
|
+
val p = Amba.challenges.getProgress(c.id)
|
|
205
|
+
if (p.completed && !p.claimed) Amba.challenges.claim(c.id)
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Flutter
|
|
210
|
+
|
|
211
|
+
```dart
|
|
212
|
+
import 'package:amba/amba.dart';
|
|
213
|
+
|
|
214
|
+
await Amba.events.track('workout_completed', {'duration_minutes': 30});
|
|
215
|
+
final streak = await Amba.streaks.qualify('daily_workout');
|
|
216
|
+
final progress = await Amba.achievements.getProgress();
|
|
217
|
+
final xp = await Amba.xp.getBalance();
|
|
218
|
+
final entries = await Amba.leaderboards.getEntries('Weekly XP', limit: 50);
|
|
219
|
+
final myRank = await Amba.leaderboards.getMyRank('Weekly XP');
|
|
220
|
+
final active = await Amba.challenges.getActive();
|
|
221
|
+
for (final c in active) {
|
|
222
|
+
final p = await Amba.challenges.getProgress(c.id);
|
|
223
|
+
if (p.completed && !p.claimed) {
|
|
224
|
+
await Amba.challenges.claim(c.id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Common follow-ups
|
|
230
|
+
|
|
231
|
+
Batch into one or two questions.
|
|
232
|
+
|
|
233
|
+
1. **What's the qualifying event for XP / achievements / streaks?** Most preset answers below — accept the suggestion or override:
|
|
234
|
+
- fitness: `workout_completed`
|
|
235
|
+
- education: `lesson_completed`
|
|
236
|
+
- game: `level_completed` (or `match_played`)
|
|
237
|
+
- productivity: `task_completed`
|
|
238
|
+
- content_creator: `post_published`
|
|
239
|
+
- ai_chatbot: `prompt_sent`
|
|
240
|
+
|
|
241
|
+
2. **Default XP per qualifying event?** (defaults to `50`)
|
|
242
|
+
|
|
243
|
+
3. **Streak period?**
|
|
244
|
+
- Daily (recommended)
|
|
245
|
+
- Weekly
|
|
246
|
+
- None (skip streaks)
|
|
247
|
+
|
|
248
|
+
4. **Streak freezes?**
|
|
249
|
+
- 3 freezes/month (recommended — covers the occasional missed day without breaking)
|
|
250
|
+
- Hard mode (no freezes)
|
|
251
|
+
- None — don't enable freezes
|
|
252
|
+
|
|
253
|
+
5. **Leaderboard scope:** (multi-select)
|
|
254
|
+
- [x] Weekly (recommended — rotates, never gets stale)
|
|
255
|
+
- [ ] All-time
|
|
256
|
+
- [ ] Daily (high-engagement apps only — daily resets feel relentless)
|
|
257
|
+
- [ ] Monthly
|
|
258
|
+
|
|
259
|
+
6. **Which achievements to seed?** Defaults per preset (offer to create, or skip):
|
|
260
|
+
|
|
261
|
+
**fitness:**
|
|
262
|
+
- `first_workout` — Complete 1 workout (+100 XP)
|
|
263
|
+
- `week_warrior` — 7-day streak (+250 XP)
|
|
264
|
+
- `century_club` — 100 workouts (+1000 XP)
|
|
265
|
+
|
|
266
|
+
**education:**
|
|
267
|
+
- `first_lesson` — Complete 1 lesson (+100 XP)
|
|
268
|
+
- `dedicated_learner` — 7-day streak (+250 XP)
|
|
269
|
+
- `course_complete` — Finish a course (+1000 XP, criteria: `event: course_completed, count: 1`)
|
|
270
|
+
|
|
271
|
+
**game:**
|
|
272
|
+
- `first_win` — Win 1 match (+100 XP)
|
|
273
|
+
- `streak_master` — 5 wins in a row (custom criteria)
|
|
274
|
+
- `level_50` — Reach level 50 (`xp_total: 50000`)
|
|
275
|
+
|
|
276
|
+
**productivity:**
|
|
277
|
+
- `first_task` — Complete 1 task (+100 XP)
|
|
278
|
+
- `inbox_zero` — Clear all tasks (criteria: `event: inbox_zeroed, count: 1`)
|
|
279
|
+
- `monthly_warrior` — 30-day streak (+1000 XP)
|
|
280
|
+
|
|
281
|
+
For other presets, ask the user to confirm 3 achievements they want or skip.
|
|
282
|
+
|
|
283
|
+
7. **Challenges:** seed an example weekly challenge?
|
|
284
|
+
- Yes — "5 workouts this week" (or equivalent for the preset) ending next Sunday
|
|
285
|
+
- No
|
|
286
|
+
|
|
287
|
+
## Re-run behavior
|
|
288
|
+
|
|
289
|
+
1. Read `.amba/wired.json` `surfaces.gamification`:
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"surfaces": {
|
|
294
|
+
"gamification": {
|
|
295
|
+
"xp_rules": ["Workout Completed"],
|
|
296
|
+
"achievements": ["first_workout", "week_warrior", "century_club"],
|
|
297
|
+
"streaks": ["daily_workout"],
|
|
298
|
+
"leaderboards": ["Weekly XP"],
|
|
299
|
+
"challenges": []
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
2. Before creating anything, list-then-diff:
|
|
306
|
+
- `amba_xp_rules_list` — match on `name`. If exists, ask to update (`amba_xp_update_rule`) or skip.
|
|
307
|
+
- `amba_achievements_list` — match on `key`. Achievement `key` is unique per project; collision → skip or update.
|
|
308
|
+
- `amba_streaks_list` — match on `key`. **Never recreate** a streak with an existing key — the SDK calls would silently target a stale definition. Update instead.
|
|
309
|
+
- `amba_leaderboards_list` — match on `name`. Collision → ask.
|
|
310
|
+
- `amba_challenges_list` — match on `name + starts_at`. New challenge with same name + different date is fine; same name + overlapping date → ask.
|
|
311
|
+
|
|
312
|
+
3. In the entry file: detect existing `Amba.events.track('<event_name>')` calls. If they already exist, don't add another. If they're missing for an event tied to a new XP rule, add a sample comment / TODO showing where to call `Amba.events.track`.
|
|
313
|
+
|
|
314
|
+
4. Update `wired.json` — append to each list, never replace.
|
|
315
|
+
|
|
316
|
+
5. If the user asks to "remove gamification": offer a soft path — pause XP rules and unschedule challenges rather than deleting definitions (deletion is irreversible and loses historical user XP / unlocks).
|