Skip to content
SlashClone

Retention cohorts

@usagemetrics/retention-cohorts

Build weekly or monthly retention cohorts for users or accounts from an events CSV. Writes the cohort table, a size-weighted average curve, where the curve flattens, and the best and worst cohorts, as markdown, CSV, and JSON.

SkillDatav1.0.0 (latest)Tested
Activity
New: be the first to try it
  1. 1{
  2. 2 "name": "retention-cohorts",
  3. 3 "owner": "usagemetrics",
  4. 4 "version": "1.0.0",
  5. 5 "type": "skill",
  6. 6 "description": "Build weekly or monthly retention cohorts for users or accounts from an events CSV. Writes the cohort table, a size-weighted average curve, where the curve flattens, and the best and worst cohorts, as markdown, CSV, and JSON.",
  7. 7 "category": "data",
  8. 8 "license": "MIT",
  9. 9 "compat": ["claude-code"],
  10. 10 "permissions": {
  11. 11 "shell": ["node scripts/cohorts.mjs"],
  12. 12 "files": { "read": ["./data/**"], "write": ["./out/retention*"] }
  13. 13 },
  14. 14 "tests": {
  15. 15 "cases": [
  16. 16 {
  17. 17 "name": "monthly user cohorts from the example events",
  18. 18 "inputs": {},
  19. 19 "checks": [
  20. 20 { "type": "command_exits_zero", "command": "node scripts/cohorts.mjs examples/events.csv --period month --entity user --out out/retention" },
  21. 21 { "type": "file_exists", "path": "out/retention.csv" },
  22. 22 { "type": "file_contains", "path": "out/retention.md", "text": "| 2025-11 | 11 | 100% | 27% |" },
  23. 23 {
  24. 24 "type": "output_matches_schema",
  25. 25 "path": "out/retention.json",
  26. 26 "schema": {
  27. 27 "type": "object",
  28. 28 "required": ["cohorts", "average_curve", "best_period1", "worst_period1"],
  29. 29 "properties": {
  30. 30 "last_complete_period": { "const": "2026-09" },
  31. 31 "cohorts": { "type": "array", "minItems": 11, "maxItems": 11 },
  32. 32 "best_period1": { "type": "object", "properties": { "cohort": { "const": "2026-06" } } },
  33. 33 "worst_period1": { "type": "object", "properties": { "cohort": { "const": "2025-11" } } }
  34. 34 }
  35. 35 }
  36. 36 }
  37. 37 ]
  38. 38 },
  39. 39 {
  40. 40 "name": "only counts the chosen active events and leaves out the current month",
  41. 41 "inputs": {
  42. 42 "data/events.csv": "timestamp,account_id,user_id,event\n2026-01-05,a1,u1,ran_query\n2026-01-09,a1,u2,ran_query\n2026-02-03,a1,u1,login\n2026-02-10,a1,u2,ran_query\n2026-02-11,a2,u3,ran_query\n2026-03-02,a2,u3,ran_query\n"
  43. 43 },
  44. 44 "checks": [
  45. 45 { "type": "command_exits_zero", "command": "node scripts/cohorts.mjs data/events.csv --active-events ran_query --out out/retention" },
  46. 46 {
  47. 47 "type": "output_matches_schema",
  48. 48 "path": "out/retention.json",
  49. 49 "schema": {
  50. 50 "type": "object",
  51. 51 "properties": {
  52. 52 "last_complete_period": { "const": "2026-02" },
  53. 53 "cohorts": {
  54. 54 "type": "array",
  55. 55 "minItems": 2,
  56. 56 "maxItems": 2,
  57. 57 "items": { "type": "object", "required": ["cohort", "size", "rates"] }
  58. 58 },
  59. 59 "average_curve": { "type": "array", "minItems": 2, "maxItems": 2, "items": { "type": "object" } }
  60. 60 }
  61. 61 }
  62. 62 },
  63. 63 { "type": "file_contains", "path": "out/retention.md", "text": "| 2026-01 | 2 | 100% | 50% |" }
  64. 64 ]
  65. 65 }
  66. 66 ]
  67. 67 }