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.
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/assets/icon.svg +1 -0
- package/assets/readme/beat-flow.jpg +0 -0
- package/assets/readme/beat-headline.jpg +0 -0
- package/assets/readme/beat-next.jpg +0 -0
- package/assets/readme/beat-play.jpg +0 -0
- package/assets/readme/beat-status.jpg +0 -0
- package/assets/readme/beats.jpg +0 -0
- package/assets/wordmark.png +0 -0
- package/assets/wordmark.svg +1 -0
- package/bin/rikrok.mjs +63 -0
- package/launchd/com.rikrok.plist.tmpl +25 -0
- package/package.json +70 -0
- package/remotion/Reel.tsx +513 -0
- package/remotion/Root.tsx +71 -0
- package/remotion/index.ts +4 -0
- package/remotion/public/silence.wav +0 -0
- package/remotion/theme.ts +52 -0
- package/scripts/gen-icon.mjs +117 -0
- package/scripts/ui-check.mjs +76 -0
- package/server/feed.mjs +153 -0
- package/server/public/app.js +342 -0
- package/server/public/icon-180.png +0 -0
- package/server/public/icon-512.png +0 -0
- package/server/public/icon.svg +1 -0
- package/server/public/index.html +112 -0
- package/server/public/manifest.webmanifest +14 -0
- package/server/public/sw.js +22 -0
- package/src/cli/backfill.mjs +13 -0
- package/src/cli/config.mjs +29 -0
- package/src/cli/demo.mjs +14 -0
- package/src/cli/doctor.mjs +152 -0
- package/src/cli/feed.mjs +7 -0
- package/src/cli/hook.mjs +77 -0
- package/src/cli/install.mjs +66 -0
- package/src/cli/recap.mjs +66 -0
- package/src/cli/setup.mjs +162 -0
- package/src/cli/voice.mjs +214 -0
- package/src/cli/watch.mjs +5 -0
- package/src/hooks/comment.mjs +21 -0
- package/src/lib/backfill.mjs +40 -0
- package/src/lib/config.mjs +89 -0
- package/src/lib/evidence.mjs +21 -0
- package/src/lib/gitinfo.mjs +40 -0
- package/src/lib/llm.mjs +258 -0
- package/src/lib/narrate.mjs +148 -0
- package/src/lib/palette.mjs +27 -0
- package/src/lib/paths.mjs +29 -0
- package/src/lib/pipeline.mjs +180 -0
- package/src/lib/render-job.mjs +76 -0
- package/src/lib/script-claude.mjs +48 -0
- package/src/lib/state.mjs +19 -0
- package/src/lib/stt.mjs +26 -0
- package/src/lib/watcher.mjs +102 -0
- package/src/sources/claude.mjs +168 -0
- package/src/sources/index.mjs +26 -0
- package/src/voices/clone.mjs +66 -0
- package/src/voices/fx.mjs +22 -0
- package/src/voices/index.mjs +46 -0
- package/src/voices/module.mjs +18 -0
- package/src/voices/none.mjs +23 -0
- package/src/voices/openai-speech.mjs +34 -0
- package/src/voices/say.mjs +32 -0
- package/test/fixtures/claude-session.jsonl +23 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Any OpenAI-compatible /v1/audio/speech server: oMLX (Qwen3-TTS presets),
|
|
2
|
+
// Kokoro-FastAPI, LM Studio, or OpenAI itself if you choose to send text out.
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { TTS_URL, TTS_MODEL, TTS_KEY, authHeaders } from "../lib/config.mjs";
|
|
5
|
+
|
|
6
|
+
export function openaiSpeech(voice = "alloy") {
|
|
7
|
+
return {
|
|
8
|
+
name: `openai-speech:${voice}`,
|
|
9
|
+
async available() {
|
|
10
|
+
if (!TTS_URL) return { ok: false, reason: "RIKROK_TTS_URL not set" };
|
|
11
|
+
try {
|
|
12
|
+
const r = await fetch(`${TTS_URL}/v1/models`, { headers: authHeaders(TTS_KEY), signal: AbortSignal.timeout(5000) });
|
|
13
|
+
return r.ok ? { ok: true } : { ok: false, reason: `HTTP ${r.status} from ${TTS_URL}/v1/models` };
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return { ok: false, reason: `${TTS_URL} unreachable (${e.message})` };
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
async synth(text, outPath) {
|
|
19
|
+
try {
|
|
20
|
+
const r = await fetch(`${TTS_URL}/v1/audio/speech`, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: { ...authHeaders(TTS_KEY), "Content-Type": "application/json" },
|
|
23
|
+
body: JSON.stringify({ model: TTS_MODEL, input: text, voice, response_format: "wav" }),
|
|
24
|
+
signal: AbortSignal.timeout(180_000),
|
|
25
|
+
});
|
|
26
|
+
if (!r.ok) return { ok: false, error: `HTTP ${r.status}: ${(await r.text()).slice(0, 200)}` };
|
|
27
|
+
fs.writeFileSync(outPath, Buffer.from(await r.arrayBuffer()));
|
|
28
|
+
return { ok: true };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return { ok: false, error: e.message };
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// macOS built-in speech synthesis. Zero setup, the default on a Mac.
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
export function saySpeech(voice = "Samantha") {
|
|
6
|
+
return {
|
|
7
|
+
name: `say:${voice}`,
|
|
8
|
+
async available() {
|
|
9
|
+
if (process.platform !== "darwin") return { ok: false, reason: "macOS only" };
|
|
10
|
+
try {
|
|
11
|
+
const list = execFileSync("say", ["-v", "?"], { stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
12
|
+
if (!list.split("\n").some((l) => l.startsWith(voice + " ") || l.startsWith(voice + "\t"))) {
|
|
13
|
+
return { ok: false, reason: `voice "${voice}" not installed (see \`say -v ?\`)` };
|
|
14
|
+
}
|
|
15
|
+
return { ok: true };
|
|
16
|
+
} catch {
|
|
17
|
+
return { ok: false, reason: "`say` not found" };
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async synth(text, outPath) {
|
|
21
|
+
try {
|
|
22
|
+
const aiff = outPath.replace(/\.wav$/, ".aiff");
|
|
23
|
+
execFileSync("say", ["-v", voice, "-r", process.env.RIKROK_SAY_RATE || "182", "-o", aiff, text]);
|
|
24
|
+
execFileSync("ffmpeg", ["-y", "-i", aiff, "-ar", "24000", "-ac", "1", outPath], { stdio: "ignore" });
|
|
25
|
+
fs.unlinkSync(aiff);
|
|
26
|
+
return { ok: true };
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return { ok: false, error: e.message };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:03:00Z", "userType": "external", "message": {"role": "user", "content": "The signup form double-submits when the user taps the button twice. Fix it and add a test."}}
|
|
2
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:03:48Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "I'll look at the form handler first."}, {"type": "tool_use", "id": "t", "name": "Read", "input": {"file_path": "/home/dev/example-app/src/components/SignupForm.tsx"}}]}}
|
|
3
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:04:06Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
4
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:04:54Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "The submit handler has no guard. I'll check how other forms handle it."}, {"type": "tool_use", "id": "t", "name": "Grep", "input": {"pattern": "isSubmitting", "path": "/home/dev/example-app/src"}}]}}
|
|
5
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:05:12Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
6
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:06:00Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "There's a shared useSubmitGuard hook already. I'll use it here."}, {"type": "tool_use", "id": "t", "name": "Edit", "input": {"file_path": "/home/dev/example-app/src/components/SignupForm.tsx", "old_string": "onSubmit={submit}", "new_string": "onSubmit={guarded(submit)}"}}]}}
|
|
7
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:06:18Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
8
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:07:06Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Now the test."}, {"type": "tool_use", "id": "t", "name": "Write", "input": {"file_path": "/home/dev/example-app/src/components/SignupForm.test.tsx", "content": "..."}}]}}
|
|
9
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:07:24Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
10
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:08:12Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Running the suite."}, {"type": "tool_use", "id": "t", "name": "Bash", "input": {"command": "npm test -- SignupForm", "description": "Run the signup form tests"}}]}}
|
|
11
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:09:42Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
12
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:10:30Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "One failure: the button stays disabled after a server error. I'll reset the guard in the catch block."}, {"type": "tool_use", "id": "t", "name": "Edit", "input": {"file_path": "/home/dev/example-app/src/hooks/useSubmitGuard.ts", "old_string": "finally {}", "new_string": "finally { setBusy(false); }"}}]}}
|
|
13
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:10:48Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
14
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:11:36Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Re-running."}, {"type": "tool_use", "id": "t", "name": "Bash", "input": {"command": "npm test -- SignupForm", "description": "Re-run the signup form tests"}}]}}
|
|
15
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:12:48Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
16
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:13:36Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "All 4 tests pass. Committing."}, {"type": "tool_use", "id": "t", "name": "Bash", "input": {"command": "git commit -am 'Guard signup form against double submit'", "description": "Commit the fix"}}]}}
|
|
17
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:13:54Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
18
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:19:54Z", "userType": "external", "message": {"role": "user", "content": "Nice. Can you also make sure the same guard is on the checkout button?"}}
|
|
19
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:20:42Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Checking the checkout button."}, {"type": "tool_use", "id": "t", "name": "Read", "input": {"file_path": "/home/dev/example-app/src/pages/Checkout.tsx"}}]}}
|
|
20
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:21:00Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
21
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:21:48Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Checkout already uses the guard, so nothing to change there. I'll note the open item."}, {"type": "tool_use", "id": "t", "name": "TodoWrite", "input": {"todos": [{"content": "Guard signup form against double submit", "status": "completed"}, {"content": "Add a test for the checkout guard", "status": "pending"}, {"content": "Deploy to staging and verify on a phone", "status": "pending"}]}}]}}
|
|
22
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "user", "timestamp": "2026-03-14T09:22:06Z", "message": {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "t", "content": "ok"}]}}
|
|
23
|
+
{"cwd": "/home/dev/example-app", "gitBranch": "main", "sessionId": "demo-session", "version": "2.1.0", "type": "assistant", "timestamp": "2026-03-14T09:22:36Z", "message": {"role": "assistant", "content": [{"type": "text", "text": "Done. The signup form is guarded and tested, and the fix is committed on main. Checkout already had the guard; the next step is to deploy to staging and verify on a phone. Preview is at https://staging.example.com/signup"}]}}
|