rikrok 0.5.0

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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/assets/icon.svg +1 -0
  4. package/assets/readme/beat-flow.jpg +0 -0
  5. package/assets/readme/beat-headline.jpg +0 -0
  6. package/assets/readme/beat-next.jpg +0 -0
  7. package/assets/readme/beat-play.jpg +0 -0
  8. package/assets/readme/beat-status.jpg +0 -0
  9. package/assets/readme/beats.jpg +0 -0
  10. package/assets/wordmark.png +0 -0
  11. package/assets/wordmark.svg +1 -0
  12. package/bin/rikrok.mjs +63 -0
  13. package/launchd/com.rikrok.plist.tmpl +25 -0
  14. package/package.json +70 -0
  15. package/remotion/Reel.tsx +513 -0
  16. package/remotion/Root.tsx +71 -0
  17. package/remotion/index.ts +4 -0
  18. package/remotion/public/silence.wav +0 -0
  19. package/remotion/theme.ts +52 -0
  20. package/scripts/gen-icon.mjs +117 -0
  21. package/scripts/ui-check.mjs +76 -0
  22. package/server/feed.mjs +153 -0
  23. package/server/public/app.js +342 -0
  24. package/server/public/icon-180.png +0 -0
  25. package/server/public/icon-512.png +0 -0
  26. package/server/public/icon.svg +1 -0
  27. package/server/public/index.html +112 -0
  28. package/server/public/manifest.webmanifest +14 -0
  29. package/server/public/sw.js +22 -0
  30. package/src/cli/backfill.mjs +13 -0
  31. package/src/cli/config.mjs +29 -0
  32. package/src/cli/demo.mjs +14 -0
  33. package/src/cli/doctor.mjs +152 -0
  34. package/src/cli/feed.mjs +7 -0
  35. package/src/cli/hook.mjs +77 -0
  36. package/src/cli/install.mjs +66 -0
  37. package/src/cli/recap.mjs +66 -0
  38. package/src/cli/setup.mjs +162 -0
  39. package/src/cli/voice.mjs +214 -0
  40. package/src/cli/watch.mjs +5 -0
  41. package/src/hooks/comment.mjs +21 -0
  42. package/src/lib/backfill.mjs +40 -0
  43. package/src/lib/config.mjs +89 -0
  44. package/src/lib/evidence.mjs +21 -0
  45. package/src/lib/gitinfo.mjs +40 -0
  46. package/src/lib/llm.mjs +258 -0
  47. package/src/lib/narrate.mjs +148 -0
  48. package/src/lib/palette.mjs +27 -0
  49. package/src/lib/paths.mjs +29 -0
  50. package/src/lib/pipeline.mjs +180 -0
  51. package/src/lib/render-job.mjs +76 -0
  52. package/src/lib/script-claude.mjs +48 -0
  53. package/src/lib/state.mjs +19 -0
  54. package/src/lib/stt.mjs +26 -0
  55. package/src/lib/watcher.mjs +102 -0
  56. package/src/sources/claude.mjs +168 -0
  57. package/src/sources/index.mjs +26 -0
  58. package/src/voices/clone.mjs +66 -0
  59. package/src/voices/fx.mjs +22 -0
  60. package/src/voices/index.mjs +46 -0
  61. package/src/voices/module.mjs +18 -0
  62. package/src/voices/none.mjs +23 -0
  63. package/src/voices/openai-speech.mjs +34 -0
  64. package/src/voices/say.mjs +32 -0
  65. package/test/fixtures/claude-session.jsonl +23 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Ferrao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ <p align="center"><img src="assets/wordmark.png" alt="Rik Rok" width="480"></p>
2
+
3
+ # Rik Rok
4
+
5
+ Tired of running six Claude Code or Codex sessions, then going back through every one to work out what it did and put yourself back in the loop?
6
+
7
+ Imagine if your recaps came at you the same way you doomscroll.
8
+
9
+ Welcome to Rik Rok.
10
+
11
+ Rik Rok watches your coding-agent sessions. When one goes idle after real work, it writes a 30 to 45 second news-style recap, narrates it in your own voice, renders a vertical reel, and drops it into a swipe feed on your phone. Every reel ends with the single next step for that project, and when the session changed how something moves, the reel shows the flow. Reply to a reel and the comment can go straight back to the agent.
12
+
13
+ Everything runs on your machine. Sessions are read from disk, scripts come from a local LLM, narration from a 20-second recording of you, rendering from Remotion. No cloud, no accounts, no telemetry. Your voice never leaves the machine.
14
+
15
+ ## Get started
16
+
17
+ Three ways to run it. Pick one; they all end in the same feed.
18
+
19
+ | | Scripts written by | Voice | Needs | Best for |
20
+ |---|---|---|---|---|
21
+ | **1. Claude writes it** (easiest) | Claude Code, headless, on your subscription | your own voice via `rikrok voice serve`, or the Mac voice | Claude Code installed, ffmpeg | anyone already using Claude Code |
22
+ | **2. Local model** | Ollama or LM Studio | same | a local model server | keeping recaps entirely offline |
23
+ | **3. Mac, one server** | oMLX | oMLX (same server) | Apple Silicon, oMLX | one process for scripts, voice and QA |
24
+
25
+ The session already went through Claude, so asking Claude to write the recap adds nothing new; the voice and the render stay on your machine in every option.
26
+
27
+ ### 1. Claude writes it
28
+
29
+ ```bash
30
+ npm install -g rikrok # or: npx rikrok ...
31
+ rikrok setup # answers: yes to Claude writing the recaps, yes to the hook, record your voice
32
+ ```
33
+
34
+ That is the whole setup. From then on, when you leave a Claude Code session that did real work, a reel is built in the background and appears in the feed. By hand, the same thing is:
35
+
36
+ ```bash
37
+ rikrok hook install # SessionEnd hook: recap when you leave a session
38
+ rikrok voice setup # 22 seconds of you, see "Your own voice"
39
+ rikrok voice serve # a local cloning server, or skip and use the Mac voice for now
40
+ rikrok feed # http://127.0.0.1:4870
41
+ rikrok install # keep the feed (and voice server) running at login
42
+ ```
43
+
44
+ `rikrok watch` is the alternative trigger: it recaps sessions that go idle for 15 minutes, hook or no hook. Use both if you tend to leave sessions open.
45
+
46
+ Want to see one first? `rikrok demo` renders a reel from a bundled sample session.
47
+
48
+ ### 2. Local model
49
+
50
+ ```bash
51
+ ollama pull qwen3:8b
52
+ export RIKROK_SCRIPT=local RIKROK_LLM_MODEL=qwen3:8b
53
+ export RIKROK_LLM_EXTRA='{"think":false}' # thinking models: keep the JSON clean
54
+ rikrok backfill --limit 3 && rikrok feed
55
+ ```
56
+
57
+ `rikrok setup` walks through this too (answer no to Claude writing the recaps). LM Studio works the same way on port 1234. No model reachable? Reels still ship with a plain template script built from commits, files and todos.
58
+
59
+ ### 3. Mac, one server: oMLX
60
+
61
+ See "One server on a Mac: oMLX" under Configuration. `rikrok setup` finds it and wires everything.
62
+
63
+ ## When does a session earn a reel?
64
+
65
+ Two triggers, same rules. The hook fires when you leave a session; the watcher catches sessions left open that go quiet. Neither reads a session while you are typing in it. Then the same checks: real work, and only what is new since the last recap.
66
+
67
+ ```mermaid
68
+ flowchart LR
69
+ H["you leave a session<br/>(SessionEnd hook)"] --> C
70
+ A["~/.claude/projects<br/>*.jsonl, read only"] -- "watcher, every 60 s" --> B{"idle?<br/>no writes for 15 min"}
71
+ B -- yes --> C{"real work?<br/>10+ turns, 3+ tool calls"}
72
+ C -- yes --> D{"new since last recap?"}
73
+ D -- yes --> E["build reel<br/>max 4 / hour"]
74
+ B -. "still typing: check next minute" .-> A
75
+ C -. "too small: remember, wait for more" .-> A
76
+ A -. "older than 48 h: backlog, never auto-recapped" .-> F["rikrok backfill"]
77
+ ```
78
+
79
+ | Rule | Value | Why |
80
+ |---|---|---|
81
+ | Idle bar | 15 min | No new lines in the log. A resumed session that goes quiet again gets a second reel covering only the new lines. |
82
+ | Real work | 10 assistant turns, 3 tool calls | Below this the session is remembered but skipped, so a quick question never becomes a reel. |
83
+ | Backlog cutoff | 48 h | Older sessions are history, not news. The first run backfills the 10 most recent instead. |
84
+ | Rate limit | 4 an hour | A reel costs an LLM call, a handful of voice clips and a render. The rest queue for the next scan. |
85
+ | Retries | 5 | LLM or voice server down: back off 2, 4, 8, 16, 32 minutes, then give up for this idle period. |
86
+
87
+ All five are settings (`RIKROK_IDLE_MINUTES`, `RIKROK_MIN_TURNS`, `RIKROK_MIN_TOOLS`, `RIKROK_MAX_PER_HOUR`).
88
+
89
+ ## What goes into the script?
90
+
91
+ The log is condensed to about a page of evidence and handed to the script writer (Claude Code headless, or your local model) with a fixed grammar. If the model fails twice, a template writes the same shape from the raw facts. A reel always ships.
92
+
93
+ ```mermaid
94
+ flowchart LR
95
+ E["evidence (condensed)<br/>last 6 prompts you typed<br/>last 8 assistant notes<br/>15 files edited, 10 commands<br/>git log for the window<br/>todo list, last snapshot<br/>URLs it mentioned<br/>last recap's next step"] --> L["script writer<br/>Claude Code headless, or a local model<br/>strict JSON, 2 tries"]
96
+ L --> S["script (fixed grammar)<br/>headline + narration<br/>done / open counts<br/>2 to 4 plays with evidence<br/>flow: nodes, edges, what changed<br/>status: shipped, open<br/>next step: one action"]
97
+ L -. "invalid twice" .-> T["template fallback"] --> S
98
+ S --> N["narrate, one clip per beat<br/>clip length sets card time"] --> R["render 1080x1920"]
99
+ ```
100
+
101
+ The script is the only creative step. Everything after it is timing: each card stays up as long as its narration runs, never under 2.2 seconds.
102
+
103
+ ## The reel, beat by beat
104
+
105
+ Frames from the demo reel (a sample session). Every reel has this order; only the number of plays varies, two to four, and the flow beat appears only when the session changed how something moves. Click the strip for full size.
106
+
107
+ <a href="assets/readme/beats.jpg"><img src="assets/readme/beats.jpg" alt="The five beats of a reel: headline, play, how it moves, status, next step" width="100%"></a>
108
+
109
+ 1. **Headline.** The outcome up front. Project name in its fixed accent colour, the one-line result, and a score bug: minutes active, done, open. Under it, the repo path and branch. Headline and narration come from the model; path, branch and minutes are facts from the log.
110
+ 2. **Play** (two to four). One thing that happened. A caption plus an evidence panel: files touched, commit lines, commands or a quote, animating in one by one. The model picks the plays; evidence lines must come from the log, never invented.
111
+ 3. **How it moves.** The flow the session built or changed: a user action reaching an API, data written somewhere, a job firing. Nodes in the order things happen, arrows drawn in as the narration reaches them, a dot travelling each arrow, the changed parts lit in the accent. The model returns nodes and edges backed by the session. Off with `RIKROK_FLOW=off`.
112
+ 4. **Status.** Where it stands. Shipped in cyan with ticks, open in red with circles. If the previous recap named a next step, the narration says whether it happened.
113
+ 5. **Next step.** The single next action on an accent card. Also printed under the reel in the feed, so you can act without replaying.
114
+
115
+ ## What you can do on a reel
116
+
117
+ | Action | How | What happens | Talks back to the agent? |
118
+ |---|---|---|---|
119
+ | Watched | double-tap, button, or 80% played | Reel drops below unwatched ones. Saved next to the video. | no |
120
+ | 2x | hold anywhere | Plays at double speed while held. | no |
121
+ | Comment | speech bubble | Saved to the reel, then handed to your hook with the session id, lines covered, newer recaps, and a resume hint. | yes, through `RIKROK_COMMENT_HOOK` |
122
+ | Archive | button | Hidden from the feed. The feed also auto-archives past 3 unwatched per project. | no |
123
+ | Chain | badge | Shows when a newer recap of the same session exists; tap jumps to it. | no |
124
+ | Details | button | Shipped, open, commits, URLs, copy path. | no |
125
+
126
+ ## On your phone
127
+
128
+ The feed binds to `127.0.0.1` by default. To reach it from your phone, put it on a private network you already trust:
129
+
130
+ - Tailscale: `RIKROK_BIND=0.0.0.0 rikrok feed`, then open `http://<your-mac>.<tailnet>.ts.net:4870`. If your tailscaled runs in userspace mode, use `tailscale serve --bg --https=8445 http://127.0.0.1:4870` instead and open the https URL.
131
+ - Same Wi-Fi: `RIKROK_BIND=0.0.0.0 rikrok feed` and open `http://<mac-ip>:4870`.
132
+ - Behind a Cloudflare tunnel with Access in front works well too.
133
+
134
+ Add it to your home screen from the share sheet for the full-screen PWA. There is no login on the feed, so do not expose it to the public internet without something in front.
135
+
136
+ Data lives in `~/.rikrok` (reels, sidecar JSON, state, render bundle, logs). Delete it to start over.
137
+
138
+ ## Configuration
139
+
140
+ Environment variables, or the same keys in `~/.rikrok/config.json`. `rikrok config` prints what is in effect.
141
+
142
+ | Variable | Default | What it does |
143
+ |---|---|---|
144
+ | `RIKROK_HOME` | `~/.rikrok` | Data directory |
145
+ | `RIKROK_CLAUDE_DIR` | `~/.claude/projects` | Where Claude Code keeps sessions |
146
+ | `RIKROK_SCRIPT` | `auto` | Who writes the script: `claude` (headless Claude Code), `local` (server below), `auto` = local if a model is set, else Claude if the CLI exists |
147
+ | `RIKROK_CLAUDE_MODEL` / `_BIN` | `sonnet` / `claude` | Model alias and binary for the Claude path |
148
+ | `RIKROK_LLM_URL` | `http://127.0.0.1:11434` | OpenAI-compatible chat server (Ollama, LM Studio, oMLX, vLLM) |
149
+ | `RIKROK_LLM_MODEL` | unset | Model name. Unset means template scripts only |
150
+ | `RIKROK_LLM_KEY` | unset | Bearer token if your server wants one |
151
+ | `RIKROK_LLM_EXTRA` | `{}` | JSON merged into every chat request, e.g. `{"think":false}` (Ollama) or `{"chat_template_kwargs":{"enable_thinking":false}}` (oMLX, vLLM) |
152
+ | `RIKROK_FLOW` | `on` | `off` drops the "how it moves" beat |
153
+ | `RIKROK_VOICE` | `say:Samantha` on macOS, else `none` | `clone` (your voice), `say:<Voice>`, `openai-speech:<voice>`, `module:<path>`, `none` |
154
+ | `RIKROK_CLONE_REF` / `_TEXT` / `_MODEL` | `~/.rikrok/voice/ref.wav`, `ref.txt`, `Qwen3-TTS-12Hz-1.7B-Base-bf16` | Reference clip, its transcript, and the cloning model on your speech server |
155
+ | `RIKROK_CLONE_API` | `speech` | `voice-clone` for servers with a dedicated `/v1/audio/voice-clone` endpoint (set by `rikrok voice serve`) |
156
+ | `RIKROK_VOICE_PORT` | `4873` | Port for `rikrok voice serve` |
157
+ | `RIKROK_TTS_URL` / `_MODEL` / `_KEY` | LLM URL, `tts-1` | For `openai-speech`: any `/v1/audio/speech` server (Kokoro-FastAPI, oMLX Qwen3-TTS, LM Studio) |
158
+ | `RIKROK_VOICE_FX` | `none` | `light` or `strong` robot treatment |
159
+ | `RIKROK_STT_URL` / `_MODEL` / `_KEY` | unset | Enable transcribe-back QA via a `/v1/audio/transcriptions` server with word timestamps |
160
+ | `RIKROK_PORT` / `RIKROK_BIND` | `4870` / `127.0.0.1` | Feed address |
161
+ | `RIKROK_COMMENT_HOOK` | unset | Shell command run for every comment, JSON on stdin (see below) |
162
+ | `RIKROK_IDLE_MINUTES` / `_MIN_TURNS` / `_MIN_TOOLS` | `15` / `10` / `3` | What counts as a finished, meaningful session |
163
+ | `RIKROK_MAX_PER_HOUR` | `4` | Watcher rate limit |
164
+ | `RIKROK_PROJECT_NAME_RE` | unset | Regex with one capture group applied to the session cwd to name the project, e.g. `workspaces/([^/]+)/` for worktree layouts |
165
+ | `RIKROK_HANDLE` | unset | Handle shown on every reel |
166
+ | `RIKROK_BROWSER` | unset | Path to a Chrome or Chromium binary if you would rather not let Remotion download one |
167
+
168
+ ### Your own voice
169
+
170
+ ```bash
171
+ rikrok voice setup # reads you a two-line script, records 22 seconds, plays it back, done
172
+ rikrok voice setup --file me.wav --text "what I say in it" # or bring a clip you already have
173
+ rikrok voice test # say a line in your voice
174
+ ```
175
+
176
+ There is no training. The clip and its transcript are stored in `~/.rikrok/voice` and sent with every request to a local speech server that does zero-shot cloning. Nothing is uploaded anywhere.
177
+
178
+ You need a speech server that can clone. The easy way:
179
+
180
+ ```bash
181
+ rikrok voice serve # installs and runs a local Qwen3-TTS server on :4873 (MLX on Apple Silicon)
182
+ rikrok voice serve --fast # the 0.6B model: smaller download, quicker, a little less like you
183
+ ```
184
+
185
+ New in 0.4.0 and lightly tested so far; please open an issue with what breaks. It needs [uv](https://docs.astral.sh/uv/) and git, downloads the model from Hugging Face on first start (about 4.5 GB, or 1.5 GB for `--fast`), and points Rik Rok at itself. `rikrok install` keeps it running at login alongside the watcher and the feed. The server is the Apache-2.0 [Qwen3-TTS OpenAI FastAPI project](https://github.com/groxaxo/Qwen3-TTS-Openai-Fastapi); on Linux and Windows it runs on PyTorch (CPU unless you set `TTS_DEVICE=cuda`).
186
+
187
+ Already running something that clones? Any server that takes `ref_audio` and `ref_text` on `/v1/audio/speech` (oMLX with Qwen3-TTS, for example) works with `RIKROK_TTS_URL` and `RIKROK_CLONE_MODEL`; set `RIKROK_CLONE_API=voice-clone` for servers that use the dedicated `/v1/audio/voice-clone` endpoint instead.
188
+
189
+ ### One server on a Mac: oMLX
190
+
191
+ If you are on Apple Silicon and want the least moving parts, run [oMLX](https://github.com/jundot/omlx) (Apache-2.0, menu-bar app). One instance serves the script model, the Qwen3-TTS cloning model and whisper for narration QA on a single port, which is how Rik Rok's author runs it.
192
+
193
+ 1. Install: download the `.dmg` from oMLX's releases, or `brew install jundot/omlx/omlx`. The welcome screen picks a model folder and starts the server.
194
+ 2. In oMLX's model downloader, add a chat model (for example `mlx-community/Qwen3.5-35B-A3B-4bit` if you have the memory, or a 4B to 9B Qwen3.5 otherwise), `mlx-community/Qwen3-TTS-12Hz-1.7B-Base-bf16` for your voice, and a whisper model such as `whisper-large-v3-turbo`.
195
+ 3. `rikrok setup`. It finds oMLX on :8000 or :8800, copies the API key from `~/.omlx/settings.json`, picks the models it finds, and records your voice. Thinking is switched off for the script model automatically.
196
+
197
+ ### Other voices
198
+
199
+ - `say:Samantha` (any voice from `say -v ?`): zero setup on a Mac, the fallback while your voice is not set up.
200
+ - `openai-speech:<voice>`: point `RIKROK_TTS_URL` at a local speech server. Kokoro-FastAPI, oMLX with Qwen3-TTS presets, and LM Studio all speak this API.
201
+ - `module:/path/to/voice.mjs`: your own engine. Export `{ name, available(), synth(text, outWavPath) }`. This is how a private voice clone stays private.
202
+ - `none`: silent reels, captions carry the story.
203
+
204
+ If the configured voice is unavailable, Rik Rok falls back to `say`, then to silence, and says so in the sidecar. The default voices are there so a reel always ships; the point is your own.
205
+
206
+ ### Comment hook
207
+
208
+ Set `RIKROK_COMMENT_HOOK` to any command. For each comment it receives JSON on stdin:
209
+
210
+ ```json
211
+ { "reelId": "…", "source": "claude", "sessionId": "…", "project": "my-app", "cwd": "/Users/me/my-app",
212
+ "headline": "my-app: signup guarded", "coveredLines": { "from": 0, "to": 214 },
213
+ "newerRecaps": [], "resumeHint": "claude --resume <id>", "comment": "also cover checkout", "at": "…" }
214
+ ```
215
+
216
+ A hook that opens the session back up with your note:
217
+
218
+ ```bash
219
+ RIKROK_COMMENT_HOOK='jq -r ".comment" | xargs -I{} claude --resume "$(jq -r .sessionId)" -p "{}"'
220
+ ```
221
+
222
+ Comments are always saved to the reel's sidecar JSON as well.
223
+
224
+ ## Privacy
225
+
226
+ With "Claude writes it", the condensed evidence (about a page: your last prompts, the assistant's notes, file names, commands, git log) goes to Claude through your own Claude Code login, the same place the session itself already went. With a local model, nothing leaves the machine. Either way the voice clip, the narration and the render stay local unless you point `RIKROK_TTS_URL` at a remote server. The reels themselves contain session content (file names, commit lines, what you asked for), so share them the way you would share a terminal recording.
227
+
228
+ ## Requirements
229
+
230
+ - Node 20.19 or newer
231
+ - ffmpeg and ffprobe
232
+ - git
233
+ - A headless Chrome for rendering. Remotion downloads one on first use (a few hundred MB); `rikrok demo` triggers that download so the first real reel is not a surprise. If the download does not work on your machine, set `RIKROK_BROWSER` to any Chrome or Chromium.
234
+ - Remotion's licence applies to you as the user: free for individuals and for companies of up to three people, a paid company licence above that. See remotion.dev/license.
235
+
236
+ Linux: the watcher and feed run fine (`rikrok watch`, `rikrok feed`); `rikrok install` prints systemd user units instead of installing launchd agents. `say` is macOS only, so pick `openai-speech` or `none`.
237
+
238
+ ## Roadmap
239
+
240
+ - Codex CLI sessions (`~/.codex/sessions`) as a second source. The source interface is already in place.
241
+ - Gemini CLI, OpenCode, Cursor.
242
+ - A screenshot of the running app as a play's visual when the session mentioned a live URL.
243
+ - Push notification when a reel lands.
244
+
245
+ ## Development
246
+
247
+ ```bash
248
+ git clone https://github.com/alexferrao/rikrok && cd rikrok && npm install
249
+ npm test # parse, script, flow and props tests on the fixture session
250
+ npm run demo # full pipeline on the fixture: assets/demo.mp4
251
+ npm run studio # Remotion studio for the composition
252
+ node scripts/gen-icon.mjs # regenerate the mark (macOS, needs Baskerville)
253
+ ```
254
+
255
+ MIT.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="mc"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="mr"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><rect width="512" height="512" rx="96" fill="#000"/><g transform="translate(256 256) scale(1.55369) translate(-291.18499594484996 -338.06)"><g style="mix-blend-mode:screen" transform="translate(-7.724 -5.406)"><g fill="#25F4EE"><g clip-path="url(#mr)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#mc)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#25F4EE" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><g style="mix-blend-mode:screen" transform="translate(7.724 5.406)"><g fill="#FE2C55"><g clip-path="url(#mr)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#mc)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#FE2C55" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><g fill="#fff"><g clip-path="url(#mr)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#mc)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#fff" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g></svg>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595 160" width="595" height="160"><defs><clipPath id="wc0c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="wc0r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><defs><clipPath id="wc4c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="wc4r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><defs><clipPath id="wr0c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="wr0r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><defs><clipPath id="wr4c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="wr4r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><defs><clipPath id="ww0c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="ww0r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><defs><clipPath id="ww4c"><rect x="0" y="0" width="800" height="426.12"/></clipPath><clipPath id="ww4r"><path d="M289.8 211 L800 211 L800 490 L0 490 L0 360.45 L243.30444444444444 360.45 Z"/></clipPath></defs><rect width="595" height="160" rx="24" fill="#000"/><g transform="translate(28 28) scale(0.46872) translate(-159.36999188969992 -227.11999999999998)"><g style="mix-blend-mode:screen" transform="translate(-10.667307692307693 -7.467115384615385)"><g transform="translate(0.00 0)"><g fill="#25F4EE"><g clip-path="url(#wc0r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#wc0c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#25F4EE" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(209.63 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#25F4EE" d="M736 1228Q736 1164 690 1118Q644 1072 580 1072Q516 1072 470 1118Q424 1164 424 1228Q424 1293 470 1338.5Q516 1384 580 1384Q644 1384 690 1338.5Q736 1293 736 1228ZM568 242L669 242Q541 -31 311 -31Q218 -31 171 8Q124 47 124 124Q124 169 163 282L283 627Q297 667 297 679Q297 691 291.5 696.5Q286 702 274 702Q223 702 154 572L58 572Q187 851 422 851Q505 851 551 810.5Q597 770 597 699Q597 647 556 534L440 218Q415 151 415 134Q415 123 421.5 117Q428 111 440 111Q466 111 506.5 156Q547 201 568 242Z"/><path transform="translate(303.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#25F4EE" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/><g transform="translate(562.05 0)"><g fill="#25F4EE"><g clip-path="url(#wc4r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#wc4c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#25F4EE" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(771.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#25F4EE" d="M92 345Q92 450 153.5 570.5Q215 691 346.5 771Q478 851 659 851Q884 851 989.5 744Q1095 637 1095 485Q1095 276 933 122.5Q771 -31 519 -31Q336 -31 214 61.5Q92 154 92 345ZM385 244Q385 155 418.5 110.5Q452 66 519 66Q643 66 721.5 253Q800 440 800 569Q800 667 762.5 711Q725 755 676 755Q544 755 464.5 559Q385 363 385 244Z"/><path transform="translate(931.35 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#25F4EE" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/></g><g style="mix-blend-mode:screen" transform="translate(10.667307692307693 7.467115384615385)"><g transform="translate(0.00 0)"><g fill="#FE2C55"><g clip-path="url(#wr0r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#wr0c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#FE2C55" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(209.63 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#FE2C55" d="M736 1228Q736 1164 690 1118Q644 1072 580 1072Q516 1072 470 1118Q424 1164 424 1228Q424 1293 470 1338.5Q516 1384 580 1384Q644 1384 690 1338.5Q736 1293 736 1228ZM568 242L669 242Q541 -31 311 -31Q218 -31 171 8Q124 47 124 124Q124 169 163 282L283 627Q297 667 297 679Q297 691 291.5 696.5Q286 702 274 702Q223 702 154 572L58 572Q187 851 422 851Q505 851 551 810.5Q597 770 597 699Q597 647 556 534L440 218Q415 151 415 134Q415 123 421.5 117Q428 111 440 111Q466 111 506.5 156Q547 201 568 242Z"/><path transform="translate(303.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#FE2C55" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/><g transform="translate(562.05 0)"><g fill="#FE2C55"><g clip-path="url(#wr4r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#wr4c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#FE2C55" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(771.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#FE2C55" d="M92 345Q92 450 153.5 570.5Q215 691 346.5 771Q478 851 659 851Q884 851 989.5 744Q1095 637 1095 485Q1095 276 933 122.5Q771 -31 519 -31Q336 -31 214 61.5Q92 154 92 345ZM385 244Q385 155 418.5 110.5Q452 66 519 66Q643 66 721.5 253Q800 440 800 569Q800 667 762.5 711Q725 755 676 755Q544 755 464.5 559Q385 363 385 244Z"/><path transform="translate(931.35 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#FE2C55" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/></g><g transform="translate(0.00 0)"><g fill="#fff"><g clip-path="url(#ww0r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#ww0c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#fff" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(209.63 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#fff" d="M736 1228Q736 1164 690 1118Q644 1072 580 1072Q516 1072 470 1118Q424 1164 424 1228Q424 1293 470 1338.5Q516 1384 580 1384Q644 1384 690 1338.5Q736 1293 736 1228ZM568 242L669 242Q541 -31 311 -31Q218 -31 171 8Q124 47 124 124Q124 169 163 282L283 627Q297 667 297 679Q297 691 291.5 696.5Q286 702 274 702Q223 702 154 572L58 572Q187 851 422 851Q505 851 551 810.5Q597 770 597 699Q597 647 556 534L440 218Q415 151 415 134Q415 123 421.5 117Q428 111 440 111Q466 111 506.5 156Q547 201 568 242Z"/><path transform="translate(303.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#fff" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/><g transform="translate(562.05 0)"><g fill="#fff"><g clip-path="url(#ww4r)"><path transform="translate(200 450) scale(0.146484375 -0.146484375)" d="M642 612L545 294Q524 225 524 204Q524 112 687 112L653 0L-23 0L11 112L32 112Q89 112 128.5 138Q168 164 202 275L455 1102Q465 1135 465 1153Q465 1245 322 1245L356 1356L1022 1356Q1300 1356 1415 1257.5Q1530 1159 1530 1011Q1530 902 1469.5 820.5Q1409 739 1305.5 692.5Q1202 646 1070 631Q1148 595 1225 363Q1281 196 1332.5 154Q1384 112 1466 112L1432 0L1035 0Q970 94 897 330Q854 469 822 523.5Q790 578 755.5 595Q721 612 642 612ZM675 720Q892 720 988 753Q1084 786 1137 869Q1190 952 1190 1042Q1190 1145 1118.5 1196.5Q1047 1248 904 1248Q889 1248 835 1245Z"/></g><g clip-path="url(#ww4c)"><g transform="translate(224.44444444444446 450) rotate(-2.127) scale(1.6269) translate(-247.20833333333334 -368)"><path transform="translate(200 450) scale(0.3 -0.3)" d="M155 269L251 609Q230 590 203.5 582Q177 574 151 574Q108 574 79 597Q50 620 50 663Q50 691 70 710.5Q90 730 121 730Q150 730 168.5 711.5Q187 693 187 662Q187 640 177 623Q177 617 189 617Q244 617 294 703L312 703L191 269Z" stroke="#fff" stroke-width="8.20" stroke-linejoin="round" stroke-linecap="round" paint-order="stroke"/></g></g></g></g><path transform="translate(771.67 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#fff" d="M92 345Q92 450 153.5 570.5Q215 691 346.5 771Q478 851 659 851Q884 851 989.5 744Q1095 637 1095 485Q1095 276 933 122.5Q771 -31 519 -31Q336 -31 214 61.5Q92 154 92 345ZM385 244Q385 155 418.5 110.5Q452 66 519 66Q643 66 721.5 253Q800 440 800 569Q800 667 762.5 711Q725 755 676 755Q544 755 464.5 559Q385 363 385 244Z"/><path transform="translate(931.35 0) translate(200 450) scale(0.146484375 -0.146484375)" fill="#fff" d="M61 0L412 1149Q422 1181 422 1199Q422 1252 297 1252L329 1356Q579 1356 686 1384L781 1384L516 515Q765 756 867 803.5Q969 851 1050 851Q1121 851 1166 821.5Q1211 792 1211 744Q1211 703 1181.5 672Q1152 641 1098 641Q1051 641 997 675Q964 696 934 696Q830 696 642 515Q723 515 791.5 476.5Q860 438 885 378Q910 318 920 194Q926 113 959 113Q1008 113 1084 255L1176 255Q1070 -14 822 -14Q739 -14 679 29Q619 72 619 155Q619 189 631 259Q642 328 642 354Q642 439 562 439L464 345L358 0Z"/></g></svg>
package/bin/rikrok.mjs ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ // Rik Rok CLI. Your coding-agent sessions, recapped as reels you can doomscroll.
3
+ const [major, minor] = process.versions.node.split(".").map(Number);
4
+ if (major < 20 || (major === 20 && minor < 19)) {
5
+ console.error(`rikrok needs Node 20.19 or newer (you have ${process.versions.node})`);
6
+ process.exit(1);
7
+ }
8
+
9
+ const COMMANDS = {
10
+ setup: "Guided setup: local LLM, your voice, first reel, feed",
11
+ voice: "Your own voice: setup (record a clip) | test | devices | serve (local cloning server)",
12
+ watch: "Run the watcher: recap sessions as they go idle (foreground)",
13
+ hook: "Claude Code SessionEnd hook: install | uninstall (recap when you leave a session)",
14
+ recap: "Recap one transcript now (--transcript path)",
15
+ feed: "Serve the swipe feed",
16
+ backfill: "Recap the most recent qualifying sessions now (--limit N)",
17
+ doctor: "Check node, ffmpeg, sessions, LLM, voice, browser, port",
18
+ demo: "Render a demo reel from the bundled fixture session (--out path)",
19
+ install: "Install launchd agents for watch + feed on macOS (--uninstall to remove)",
20
+ config: "Print the settings in effect",
21
+ };
22
+
23
+ const [cmd, ...rest] = process.argv.slice(2);
24
+ const args = parseArgs(rest);
25
+
26
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
27
+ console.log(`rikrok <command> [options]\n`);
28
+ for (const [k, v] of Object.entries(COMMANDS)) console.log(` ${k.padEnd(10)} ${v}`);
29
+ console.log(`\nSettings are RIKROK_* environment variables or ~/.rikrok/config.json (see README).`);
30
+ process.exit(0);
31
+ }
32
+ if (cmd === "version" || cmd === "--version" || cmd === "-v") {
33
+ const { PKG_VERSION } = await import("../src/lib/paths.mjs");
34
+ console.log(PKG_VERSION);
35
+ process.exit(0);
36
+ }
37
+ if (!COMMANDS[cmd]) {
38
+ console.error(`unknown command "${cmd}" (try: rikrok help)`);
39
+ process.exit(1);
40
+ }
41
+
42
+ const mod = await import(`../src/cli/${cmd}.mjs`);
43
+ try {
44
+ const code = await mod.run(args);
45
+ if (typeof code === "number") process.exit(code);
46
+ } catch (err) {
47
+ console.error(`rikrok ${cmd}: ${err.message}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ function parseArgs(list) {
52
+ const out = { _: [] };
53
+ for (let i = 0; i < list.length; i++) {
54
+ const a = list[i];
55
+ if (a.startsWith("--")) {
56
+ const [k, v] = a.slice(2).split("=");
57
+ if (v !== undefined) out[k] = v;
58
+ else if (list[i + 1] !== undefined && !list[i + 1].startsWith("--")) out[k] = list[++i];
59
+ else out[k] = true;
60
+ } else out._.push(a);
61
+ }
62
+ return out;
63
+ }
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key><string>{{LABEL}}</string>
6
+ <key>ProgramArguments</key>
7
+ <array>
8
+ <string>{{NODE}}</string>
9
+ <string>{{BIN}}</string>
10
+ <string>{{CMD}}</string>
11
+ </array>
12
+ <key>WorkingDirectory</key><string>{{WORKDIR}}</string>
13
+ <key>RunAtLoad</key><true/>
14
+ <key>KeepAlive</key><true/>
15
+ <key>ProcessType</key><string>Background</string>
16
+ <key>Nice</key><integer>10</integer>
17
+ <key>StandardOutPath</key><string>{{LOG}}</string>
18
+ <key>StandardErrorPath</key><string>{{ERRLOG}}</string>
19
+ <key>EnvironmentVariables</key>
20
+ <dict>
21
+ <key>PATH</key><string>{{PATH}}</string>
22
+ {{ENV}}
23
+ </dict>
24
+ </dict>
25
+ </plist>
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "rikrok",
3
+ "version": "0.5.0",
4
+ "description": "Your Claude Code sessions, recapped as short vertical reels you doomscroll on your phone. Local-first.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Alex Ferrao",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/alexferrao/rikrok"
11
+ },
12
+ "homepage": "https://github.com/alexferrao/rikrok#readme",
13
+ "keywords": [
14
+ "claude-code",
15
+ "codex",
16
+ "coding-agent",
17
+ "recap",
18
+ "reels",
19
+ "remotion",
20
+ "local-first"
21
+ ],
22
+ "bin": {
23
+ "rikrok": "bin/rikrok.mjs"
24
+ },
25
+ "engines": {
26
+ "node": ">=20.19"
27
+ },
28
+ "files": [
29
+ "bin",
30
+ "src",
31
+ "server",
32
+ "remotion",
33
+ "launchd",
34
+ "scripts",
35
+ "assets/icon.svg",
36
+ "assets/wordmark.svg",
37
+ "assets/wordmark.png",
38
+ "assets/readme",
39
+ "test/fixtures",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "watch": "node bin/rikrok.mjs watch",
45
+ "feed": "node bin/rikrok.mjs feed",
46
+ "backfill": "node bin/rikrok.mjs backfill",
47
+ "doctor": "node bin/rikrok.mjs doctor",
48
+ "demo": "node bin/rikrok.mjs demo",
49
+ "studio": "remotion studio remotion/index.ts",
50
+ "gen-icon": "node scripts/gen-icon.mjs",
51
+ "test": "node --test test/*.test.mjs"
52
+ },
53
+ "dependencies": {
54
+ "@fontsource-variable/bricolage-grotesque": "^5.2.5",
55
+ "@fontsource/ibm-plex-mono": "^5.2.5",
56
+ "@fontsource/spectral": "^5.2.5",
57
+ "@remotion/bundler": "4.0.399",
58
+ "@remotion/cli": "4.0.399",
59
+ "@remotion/renderer": "4.0.399",
60
+ "express": "^4.21.2",
61
+ "react": "^19.0.0",
62
+ "react-dom": "^19.0.0",
63
+ "remotion": "4.0.399"
64
+ },
65
+ "devDependencies": {
66
+ "@fontsource/noto-music": "^5.3.0",
67
+ "fontkit": "^2.0.4",
68
+ "puppeteer-core": "^25.8.0"
69
+ }
70
+ }