diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 565b3df..f73a9b4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -coc@openclerk.dev. All complaints will be reviewed and investigated +coc@openclerk.ch. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/apps/frontend/README.md b/apps/frontend/README.md index 1bebebf..a1e5ae4 100644 --- a/apps/frontend/README.md +++ b/apps/frontend/README.md @@ -102,6 +102,6 @@ The frontend is typically deployed alongside the backend in a Docker container ( ## Links -- [Live Site](https://openclerk.dev) +- [Live Site](https://openclerk.ch) - [Main Repository](../../README.md) - [Python Package](../../packages/clerk/README.md) diff --git a/apps/website/README.md b/apps/website/README.md index 8a4abd2..063857a 100644 --- a/apps/website/README.md +++ b/apps/website/README.md @@ -4,7 +4,7 @@ The public-facing marketing website for OpenClerk - built with React, TypeScript ## Overview -This is the landing page and documentation site hosted on Vercel at openclerk.dev, providing: +This is the landing page and documentation site hosted on Vercel at openclerk.ch, providing: - **Landing page** - Product overview and features - **Documentation** - User guides and API reference (static, no backend required) @@ -94,7 +94,7 @@ This copies all `.md` files from `docs/` to `apps/website/public/docs/` and gene - **URL**: `/docs` or `/docs/path/to/file.md` - **Navigation**: Sidebar with sections based on directory structure -- **Features**: +- **Features**: - Syntax-highlighted code blocks - Tables and GitHub-flavored Markdown - Responsive design with mobile sidebar @@ -128,13 +128,13 @@ VITE_API_URL=http://localhost:8000 ## Deployment -The website is deployed to openclerk.dev via **Vercel**. +The website is deployed to openclerk.ch via **Vercel**. See `vercel.json` for deployment configuration. ## Links -- [Live Site](https://openclerk.dev) +- [Live Site](https://openclerk.ch) - [Main Repository](../../README.md) - [Python Package](../../packages/clerk/README.md) - [Main Application](../frontend/README.md) - The full OpenClerk app diff --git a/apps/website/src/components/LifecycleAnimation.tsx b/apps/website/src/components/LifecycleAnimation.tsx new file mode 100644 index 0000000..13ae75b --- /dev/null +++ b/apps/website/src/components/LifecycleAnimation.tsx @@ -0,0 +1,688 @@ +import { useEffect, useRef } from "react"; + +interface LifecycleAnimationProps { + onComplete?: () => void; +} + +export default function LifecycleAnimation({ onComplete }: LifecycleAnimationProps) { + const rootRef = useRef(null); + + useEffect(() => { + const root = rootRef.current; + if (!root) return; + + const q = (sel: string): T => + root.querySelector(sel) as T; + const qa = (sel: string): T[] => + Array.from(root.querySelectorAll(sel)) as T[]; + + const svg = q("#life"); + if (!svg) return; + + const scene = q("#scene"); + const dots = qa("#dots .dot"); + const bodies = dots.map( + (g) => g.querySelector(".bd") as SVGCircleElement, + ); + const track = q("#track"); + const packetG = q("#packetG"); + const ghost1 = q("#ghost1"); + const ghost2 = q("#ghost2"); + const gauge = q("#gauge"); + const delta = q("#delta"); + const head = q("#head"); + const guide = q("#ring-guide"); + const pulse1 = q("#edit-pulse"); + const sweep = q("#sweep"); + const score = q("#score"); + const phaseWord = q("#phaseWord"); + const phaseLine = q("#phaseLine"); + + if ( + !scene || + dots.length !== 5 || + bodies.some((b) => !b) || + !track || + !packetG || + !ghost1 || + !ghost2 || + !gauge || + !delta || + !head || + !guide || + !pulse1 || + !sweep || + !score || + !phaseWord || + !phaseLine + ) { + return; + } + + const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + + /* ---------- tuning ---------- */ + const DUR = 18.2; + const CX = 410; + const CY = 210; + const R = 172; + const ROW_Y = 205; + const BASE = 0.74; + const TARGET = 0.94; + + interface TimingMap { + trackIn: number; + execStart: number; + moveStart: number; + moveEnd: number; + execEnd: number; + morphStart: number; + morphDur: number; + morphStag: number; + evalStart: number; + evalEnd: number; + rerunStart: number; + rerunEnd: number; + deltaStart: number; + deltaEnd: number; + compStart: number; + compEnd: number; + shimmer: number; + holdEnd: number; + fadeEnd: number; + } + + const T: TimingMap = { + trackIn: 3.35, + execStart: 3.7, + moveStart: 3.95, + moveEnd: 6.7, + execEnd: 7.0, + morphStart: 7.0, + morphDur: 1.2, + morphStag: 0.06, + evalStart: 8.55, + evalEnd: 10.55, + rerunStart: 10.75, + rerunEnd: 12.0, + deltaStart: 12.1, + deltaEnd: 15.1, + compStart: 15.25, + compEnd: 16.6, + shimmer: 16.5, + holdEnd: 17.5, + fadeEnd: 18.2, + }; + + /* ---------- helpers ---------- */ + function clamp(v: number, a: number, b: number): number { + return v < a ? a : v > b ? b : v; + } + function lerp(a: number, b: number, t: number): number { + return a + (b - a) * t; + } + function seg(t: number, a: number, b: number): number { + return clamp((t - a) / (b - a), 0, 1); + } + + function bezier(x1: number, y1: number, x2: number, y2: number) { + const cx = 3 * x1; + const bx = 3 * (x2 - x1) - cx; + const ax = 1 - cx - bx; + const cy = 3 * y1; + const by = 3 * (y2 - y1) - cy; + const ay = 1 - cy - by; + const X = (t: number) => ((ax * t + bx) * t + cx) * t; + const Y = (t: number) => ((ay * t + by) * t + cy) * t; + const dX = (t: number) => (3 * ax * t + 2 * bx) * t + cx; + return (x: number) => { + if (x <= 0) return 0; + if (x >= 1) return 1; + let t = x; + for (let i = 0; i < 6; i++) { + const err = X(t) - x; + const d = dX(t); + if (d < 1e-4) break; + t = Math.min(1, Math.max(0, t - err / d)); + } + if (Math.abs(X(t) - x) > 1e-4) { + let lo = 0; + let hi = 1; + t = x; + for (let i = 0; i < 24; i++) { + if (X(t) < x) lo = t; + else hi = t; + t = (lo + hi) / 2; + } + } + return Y(t); + }; + } + + const OUT = bezier(0.23, 1, 0.32, 1); + const INOUT = bezier(0.77, 0, 0.175, 1); + + function qb(p0: number, p1: number, p2: number, e: number): number { + const u = 1 - e; + return u * u * p0 + 2 * u * e * p1 + e * e * p2; + } + + function rowPos(i: number): { x: number; y: number } { + return { x: 410 + (i - 2) * 105, y: ROW_Y }; + } + function circPos(i: number): { x: number; y: number } { + const a = ((-90 + i * 72) * Math.PI) / 180; + return { x: CX + R * Math.cos(a), y: CY + R * Math.sin(a) }; + } + function dotColor(a: number): string { + return ( + "rgb(" + + Math.round(lerp(213, 19, a)) + + "," + + Math.round(lerp(211, 13, a)) + + "," + + Math.round(lerp(249, 221, a)) + + ")" + ); + } + + /* ---------- per-loop state ---------- */ + const pulses: { dot: number; t0: number; dur: number; amp: number }[] = []; + const act = [-1, -1, -1, -1, -1]; + const settled = [false, false, false, false, false]; + const scanned = [false, false, false, false, false]; + const rerun = [false, false, false, false, false]; + let shimmered = false; + let prevPx = 88; + let lastKey: string | null = "Assemble|The steps come together into one kit"; + + function pulse(i: number, t0: number, dur: number, amp: number): void { + pulses.push({ dot: i, t0, dur, amp }); + } + function pulseScale(i: number, t: number): number { + let s = 1; + for (let k = 0; k < pulses.length; k++) { + const p = pulses[k]; + if (p.dot !== i) continue; + const u = (t - p.t0) / p.dur; + if (u > 0 && u < 1) s += p.amp * Math.sin(u * Math.PI); + } + return s; + } + + /* ---------- captions ---------- */ + function setCaption(word: string, line: string): void { + const key = word + "|" + line; + if (key === lastKey) return; + lastKey = key; + const out: Keyframe[] = [ + { opacity: 1, transform: "translateY(0px)", filter: "blur(0px)" }, + { opacity: 0, transform: "translateY(-5px)", filter: "blur(2px)" }, + ]; + phaseWord.animate(out, { duration: 130, easing: "ease-in", fill: "forwards" }); + phaseLine.animate(out, { duration: 130, easing: "ease-in", fill: "forwards" }); + setTimeout(() => { + if (lastKey !== key) return; + phaseWord.textContent = word || "\u00A0"; + phaseLine.textContent = line || "\u00A0"; + if (!word && !line) return; + const inn: Keyframe[] = [ + { opacity: 0, transform: "translateY(6px)", filter: "blur(2px)" }, + { opacity: 1, transform: "translateY(0px)", filter: "blur(0px)" }, + ]; + const ease = "cubic-bezier(0.23,1,0.32,1)"; + phaseWord.animate(inn, { duration: 260, easing: ease, fill: "forwards" }); + phaseLine.animate(inn, { duration: 260, easing: ease, fill: "forwards" }); + }, 135); + } + + const phases = [ + { t: 0, name: "Assemble", line: "The steps come together into one kit" }, + { t: T.execStart, name: "Execute", line: "A document flows through each step" }, + { t: T.evalStart, name: "Evaluate", line: "A judge model scores the output out of 100" }, + { t: T.rerunStart, name: "Improve", line: "A better kit, a better answer" }, + { t: T.compStart, name: "Complete", line: "Every step proven, end to end" }, + { t: T.holdEnd, name: "", line: "" }, + ]; + function phaseAt(t: number): { name: string; line: string } { + for (let i = phases.length - 1; i >= 0; i--) { + if (t >= phases[i].t) return phases[i]; + } + return phases[0]; + } + + /* ---------- assemble ---------- */ + const ASM_DUR = 1.45; + function asmStart(i: number): number { + return 0.15 + i * 0.22; + } + function asmState( + i: number, + t: number, + ): { x: number; y: number; sc: number; o: number } { + const u = (t - asmStart(i)) / ASM_DUR; + const rp = rowPos(i); + if (u <= 0) return { x: 410, y: 440, sc: 0.35, o: 0 }; + if (u >= 1) { + if (!settled[i]) { + settled[i] = true; + pulse(i, t, 0.35, 0.05); + } + return { x: rp.x, y: rp.y, sc: 1, o: 1 }; + } + const dip = seg(u, 0, 0.12); + const rise = seg(u, 0.12, 1); + const o = seg(u, 0, 0.14); + if (rise <= 0) { + const dy = INOUT(dip); + return { x: 410, y: 440 + 5 * dy, sc: lerp(0.35, 0.42, dy), o }; + } + const e = OUT(rise); + const p1x = (410 + rp.x) / 2 + (i - 2) * 22; + const p1y = (445 + rp.y) / 2 - 24; + return { + x: qb(410, p1x, rp.x, e), + y: qb(445, p1y, rp.y, e), + sc: lerp(0.42, 1, e), + o, + }; + } + + /* ---------- frame ---------- */ + function frame(t: number, dt: number): void { + let i: number; + + // execute + const moveU = seg(t, T.moveStart, T.moveEnd); + const px = lerp(88, 732, INOUT(moveU)); + const v = dt > 0 ? (px - prevPx) / dt : 0; + prevPx = px; + const speedN = clamp(Math.abs(v) / 340, 0, 1); + const packO = + seg(t, T.execStart, T.execStart + 0.3) * + (1 - seg(t, T.moveEnd - 0.05, T.execEnd)); + const packS = + lerp(0.8, 1, OUT(seg(t, T.execStart, T.execStart + 0.35))) * + (1 - 0.15 * seg(t, T.moveEnd - 0.05, T.execEnd)); + const sqx = 1 + 0.1 * speedN; + const sqy = 1 - 0.06 * speedN; + packetG.style.transform = + "translate(" + + px.toFixed(1) + + "px," + + ROW_Y + + "px) scale(" + + (packS * sqx).toFixed(3) + + "," + + (packS * sqy).toFixed(3) + + ")"; + packetG.style.opacity = packO.toFixed(3); + ghost1.style.transform = + "translate(" + + (px - v * 0.055).toFixed(1) + + "px," + + ROW_Y + + "px) scale(" + + sqx.toFixed(3) + + "," + + sqy.toFixed(3) + + ")"; + ghost2.style.transform = + "translate(" + + (px - v * 0.11).toFixed(1) + + "px," + + ROW_Y + + "px) scale(" + + sqx.toFixed(3) + + "," + + sqy.toFixed(3) + + ")"; + ghost1.style.opacity = (0.2 * speedN * packO).toFixed(3); + ghost2.style.opacity = (0.1 * speedN * packO).toFixed(3); + track.style.opacity = ( + 0.9 * + seg(t, T.trackIn, T.trackIn + 0.35) * + (1 - seg(t, T.morphStart, T.morphStart + 0.4)) + ).toFixed(3); + + // gauge + const gaugeIn = seg(t, T.morphStart + 0.15, T.morphStart + 0.85); + const fr = OUT(seg(t, T.evalStart, T.evalEnd)) * BASE; + const dfr = OUT(seg(t, T.deltaStart, T.deltaEnd)) * (TARGET - BASE); + const frac = fr + dfr; + gauge.style.opacity = (0.9 * gaugeIn).toFixed(3); + gauge.style.strokeDashoffset = (100 * (1 - fr)).toFixed(2); + delta.style.opacity = (0.9 * gaugeIn).toFixed(3); + delta.style.strokeDashoffset = (100 * (1 - dfr)).toFixed(2); + guide.style.opacity = (0.5 * gaugeIn).toFixed(3); + + // completion + const comp = INOUT(seg(t, T.compStart, T.compEnd)); + sweep.style.strokeDashoffset = (100 * (1 + comp * TARGET)).toFixed(2); + sweep.style.opacity = ( + 0.9 * + seg(t, T.compStart, T.compStart + 0.15) * + gaugeIn + ).toFixed(3); + + // score + score.textContent = Math.round(frac * 100).toString(); + score.style.opacity = seg(t, T.evalStart - 0.1, T.evalStart + 0.25).toFixed(3); + const sa = seg(t, T.deltaStart, T.deltaEnd); + score.setAttribute( + "fill", + "rgb(" + + Math.round(lerp(17, 19, sa)) + + "," + + Math.round(lerp(20, 13, sa)) + + "," + + Math.round(lerp(24, 221, sa)) + + ")", + ); + + // head + const ha = ((-90 + frac * 360) * Math.PI) / 180; + head.setAttribute( + "transform", + "translate(" + + (CX + R * Math.cos(ha)).toFixed(1) + + " " + + (CY + R * Math.sin(ha)).toFixed(1) + + ")", + ); + head.style.opacity = (gaugeIn * (frac > 0.005 ? 1 : 0)).toFixed(3); + head.setAttribute("stroke", dfr > 0 ? "#130DDD" : "#716EEB"); + + // improve + const orU = seg(t, T.rerunStart, T.rerunEnd); + if (orU > 0 && orU < 1) { + const orE = INOUT(orU); + const orA = ((-90 + orE * 360) * Math.PI) / 180; + const oDeg = orE * 360; + packetG.style.transform = + "translate(" + + (CX + R * Math.cos(orA)).toFixed(1) + + "px," + + (CY + R * Math.sin(orA)).toFixed(1) + + "px) rotate(" + + oDeg.toFixed(1) + + "deg) scale(1.08,0.92)"; + packetG.style.opacity = ( + seg(t, T.rerunStart, T.rerunStart + 0.15) * + (1 - seg(t, T.rerunEnd - 0.15, T.rerunEnd)) + ).toFixed(3); + const tl = Math.min(orE, 0.16) * 100; + pulse1.style.strokeDasharray = tl.toFixed(1) + " 200"; + pulse1.style.strokeDashoffset = (-(orE * 100 - tl)).toFixed(1); + pulse1.style.opacity = ( + 0.85 * + (1 - seg(t, T.rerunEnd - 0.2, T.rerunEnd)) + ).toFixed(3); + for (i = 0; i < 5; i++) { + if (!rerun[i] && orE >= i / 5 + 0.01) { + rerun[i] = true; + pulse(i, t, 0.4, 0.14); + } + } + } else { + pulse1.style.opacity = "0"; + } + + // judge scans + for (i = 0; i < 5; i++) { + if (!scanned[i] && frac >= i / 5 + 0.02) { + scanned[i] = true; + pulse(i, t, 0.4, 0.12); + } + } + if (!shimmered && t >= T.shimmer) { + shimmered = true; + for (i = 0; i < 5; i++) pulse(i, T.shimmer + i * 0.06, 0.35, 0.07); + } + + // dots + for (i = 0; i < 5; i++) { + const a = asmState(i, t); + const m = INOUT( + seg( + t, + T.morphStart + i * T.morphStag, + T.morphStart + i * T.morphStag + T.morphDur, + ), + ); + let x: number; + let y: number; + let sc: number; + if (m > 0) { + const cp = circPos(i); + x = lerp(a.x, cp.x, m); + y = lerp(a.y, cp.y, m); + sc = lerp(a.sc, 26 / 30, m); + } else { + x = a.x; + y = a.y; + sc = a.sc; + } + if (act[i] < 0 && moveU > 0 && px >= rowPos(i).x - 12) { + act[i] = t; + pulse(i, t, 0.45, 0.18); + } + let lit = act[i] >= 0 ? seg(t, act[i], act[i] + 0.35) : 0; + if (m >= 1) lit = 1; + sc *= pulseScale(i, t); + const g = dots[i]; + g.style.transform = + "translate(" + + x.toFixed(1) + + "px," + + y.toFixed(1) + + "px) scale(" + + sc.toFixed(3) + + ")"; + g.style.opacity = a.o.toFixed(3); + bodies[i].setAttribute("fill", dotColor(lit)); + } + + scene.style.opacity = (1 - seg(t, T.holdEnd, T.fadeEnd)).toFixed(3); + + const ph = phaseAt(t); + setCaption(ph.name, ph.line); + } + + /* ---------- reduced motion static state ---------- */ + if (reduce) { + for (let i = 0; i < 5; i++) { + const cp = circPos(i); + dots[i].style.transform = + "translate(" + + cp.x.toFixed(1) + + "px," + + cp.y.toFixed(1) + + "px) scale(" + + (26 / 30).toFixed(3) + + ")"; + dots[i].style.opacity = "1"; + bodies[i].setAttribute("fill", "#130DDD"); + } + guide.style.opacity = "0.5"; + gauge.style.opacity = "0.9"; + gauge.style.strokeDashoffset = (100 * (1 - BASE)).toFixed(2); + delta.style.opacity = "0.9"; + delta.style.strokeDashoffset = (100 * (1 - (TARGET - BASE))).toFixed(2); + sweep.style.opacity = "0.9"; + sweep.style.strokeDashoffset = (100 * (1 + TARGET)).toFixed(2); + const fa = ((-90 + TARGET * 360) * Math.PI) / 180; + head.setAttribute( + "transform", + "translate(" + + (CX + R * Math.cos(fa)).toFixed(1) + + " " + + (CY + R * Math.sin(fa)).toFixed(1) + + ")", + ); + head.style.opacity = "1"; + head.setAttribute("stroke", "#130DDD"); + score.textContent = Math.round(TARGET * 100).toString(); + score.style.opacity = "1"; + score.setAttribute("fill", "#130DDD"); + phaseWord.textContent = "Assemble"; + phaseLine.textContent = "Execute · Evaluate · Improve · Complete"; + const doneTimer = window.setTimeout(() => onComplete?.(), 2500); + return () => window.clearTimeout(doneTimer); + } + + /* ---------- loop driver: play once on load ---------- */ + let running = false; + let raf: number | null = null; + let acc = 0; + let last = 0; + let done = false; + + function tick(ts: number): void { + if (!running || done) return; + if (!last) last = ts; + const dt = Math.min((ts - last) / 1000, 0.05); + last = ts; + acc += dt; + const t = Math.min(acc, DUR); + frame(t, dt); + if (acc >= DUR) { + done = true; + stop(); + onComplete?.(); + return; + } + raf = requestAnimationFrame(tick); + } + function start(): void { + if (running) return; + running = true; + last = 0; + raf = requestAnimationFrame(tick); + } + function stop(): void { + running = false; + if (raf) cancelAnimationFrame(raf); + raf = null; + } + + start(); + + return () => { + stop(); + }; + }, [onComplete]); + + return ( +
+ + + + + + + + + + 94 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Assemble
+
The steps come together into one kit
+
+
+ ); +} diff --git a/apps/website/src/index.css b/apps/website/src/index.css index b773825..564b87f 100644 --- a/apps/website/src/index.css +++ b/apps/website/src/index.css @@ -772,4 +772,167 @@ textarea.input { .kit-bookmark-btn.active { opacity: 1; color: #d97706; +} + +/* ── Landing design language (scoped) ── */ +.landing { + --brand: #130DDD; + --brand-40: #716EEB; + --brand-82: #D5D3F9; + --brand-90: #E4E3FB; + --brand-guide: #D9D6F8; + --ink: #111418; + --muted-l: #6b7280; + --hairline: #ececec; + + /* Motion: strong custom curves, never the built-in CSS easings */ + --ease-entrance: cubic-bezier(0.23, 1, 0.32, 1); + --ease-move: cubic-bezier(0.77, 0, 0.175, 1); + + color: var(--ink); +} + +.landing h1 { + font-family: 'Inter', system-ui, sans-serif; + font-weight: 800; + font-size: clamp(2.2rem, 5.4vw, 3.9rem); + line-height: 1.1; + letter-spacing: -0.015em; +} + +.landing h1 .acc { color: var(--brand); } + +.landing .sub { + color: var(--muted-l); + font-size: 1.08rem; + line-height: 1.6; +} + +/* Buttons: solid pill, no gradient, no glass */ +.landing .btn-landing { + display: inline-block; + padding: 12px 26px; + border-radius: 999px; + font-weight: 600; + font-size: 0.95rem; + text-decoration: none; + transition: transform 160ms var(--ease-entrance), box-shadow 160ms ease, border-color 160ms ease, color 160ms ease; +} + +.landing .btn-landing:active { transform: scale(0.97); } + +.landing .btn-solid { background: var(--brand); color: #fff; } + +@media (hover: hover) and (pointer: fine) { + .landing .btn-solid:hover { box-shadow: 0 8px 30px rgba(19, 13, 221, 0.3); transform: translateY(-1px); } + .landing .btn-line:hover { border-color: var(--brand); color: var(--brand); } +} + +.landing .btn-line { color: var(--ink); border: 1px solid var(--hairline); } + +/* ── Animated lifecycle ── */ +.landing .stage { width: 100%; max-width: 760px; } + +.landing #life { width: 100%; height: auto; display: block; } + +.landing #dots .dot { opacity: 0; } + +.landing #dots .sh { fill: rgba(19, 13, 221, 0.12); } + +.landing #track { opacity: 0; } + +.landing #packetG, +.landing #ghost1, +.landing #ghost2 { opacity: 0; } + +.landing #packetG rect, +.landing #ghost1 rect, +.landing #ghost2 rect { fill: #111418; } + +.landing #gauge { + fill: none; + stroke: #716EEB; + stroke-width: 9; + stroke-linecap: round; + opacity: 0; + transform: rotate(-90deg); + transform-origin: 410px 210px; +} + +.landing #delta { + fill: none; + stroke: #130DDD; + stroke-width: 9; + stroke-linecap: round; + opacity: 0; + transform: rotate(176.4deg); + transform-origin: 410px 210px; +} + +.landing #head { + fill: #fff; + opacity: 0; +} + +.landing #ring-guide { + fill: none; + stroke: #D9D6F8; + stroke-width: 2; + stroke-dasharray: 2 8; + opacity: 0; + transform-origin: 410px 210px; + animation: guidespin 96s linear infinite; +} + +.landing #edit-pulse { + fill: none; + stroke: #A8A4F2; + stroke-width: 3; + stroke-linecap: round; + opacity: 0; + transform: rotate(-90deg); + transform-origin: 410px 210px; +} + +.landing #sweep { + fill: none; + stroke: #130DDD; + stroke-width: 9; + stroke-linecap: round; + opacity: 0; + transform: rotate(248.4deg); + transform-origin: 410px 210px; +} + +@keyframes guidespin { to { transform: rotate(360deg); } } + +.landing #score { + font-weight: 800; + font-size: 56px; + text-anchor: middle; + opacity: 0; +} + +.landing .phase { + min-height: 60px; + margin-top: 18px; + text-align: center; +} + +.landing #phaseWord { + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.18em; + color: var(--brand); + text-transform: uppercase; +} + +.landing #phaseLine { + color: var(--muted-l); + font-size: 0.9rem; + margin-top: 3px; +} + +@media (prefers-reduced-motion: reduce) { + .landing #ring-guide { animation: none; } } \ No newline at end of file diff --git a/apps/website/src/pages/LandingPage.tsx b/apps/website/src/pages/LandingPage.tsx index ef37339..abfbfd5 100644 --- a/apps/website/src/pages/LandingPage.tsx +++ b/apps/website/src/pages/LandingPage.tsx @@ -1,199 +1,60 @@ +import { useState } from "react"; import { Link } from "react-router-dom"; -import { ArrowRight, Github, Layers, Zap, Shield, Upload } from "lucide-react"; import { useAuth } from "../hooks/useAuth"; +import LifecycleAnimation from "../components/LifecycleAnimation"; export default function LandingPage() { const { user } = useAuth(); + const [showHero, setShowHero] = useState(false); return ( -
- {/* Hero Section */} -
-

- Executable Reasoning -
- Made Simple -

-

- CLERK is the community library for multi-step LLM reasoning workflows. - Create, share, and run reasoning kits — from simple prompts to complex - LangGraph-powered pipelines. -

-
- {user ? ( - - Go to App - - - ) : ( - - Sign Up for Early Access - - - )} - - - View on GitHub - +
+
+
+ setShowHero(true)} />
-
- {/* Features Section */} -
-
-

Why CLERK?

-

- Everything you need to build and share executable reasoning - workflows +

+

+ Multi-step LLM workflows +
+ you can score. +

+

+ Reusable reasoning pipelines, judged out of 100.

-
- -
- } - title="Multi-Step Workflows" - description="Chain prompts together with dependencies. Each step builds on the previous, creating complex reasoning pipelines." - /> - } - title="LangGraph Powered" - description="Built on LangGraph for robust, stateful execution. Handle loops, branching, and conditional logic with ease." - /> - } - title="Self-Hostable" - description="Own your data. Deploy CLERK on your own infrastructure with full control over your reasoning workflows." - /> - } - title="Kit Marketplace" - description="Share your reasoning kits with the community. Discover and use workflows created by others." - /> -
-
- - {/* How It Works Section */} -
-
-

How It Works

-

- Get started with executable reasoning in three simple steps -

-
- -
- - - - -
-
- - {/* CTA Section */} -
-

Ready to get started?

-

- Join the early access list and be the first to experience the future - of executable reasoning. -

-
- {user ? ( - - Go to Early Access - - - ) : ( - - Sign Up for Early Access - - - )} -
-
- - {/* Footer */} -
-
-
+
+ {user ? ( + + Go to App + + ) : ( + + Sign Up for Early Access + + )} GitHub - {/* - Documentation - */}
-

CLERK — Community Library of Executable Reasoning Kits

+
+ OpenClerk · MIT License +
-
-
- ); -} - -function FeatureCard({ - icon, - title, - description, -}: { - icon: React.ReactNode; - title: string; - description: string; -}) { - return ( -
-
- {icon} -
-

{title}

-

{description}

-
- ); -} - -function StepCard({ - number, - title, - description, -}: { - number: number; - title: string; - description: string; -}) { - return ( -
-
- {number}
-

{title}

-

{description}

); } diff --git a/competitor_analysis.md b/competitor_analysis.md new file mode 100644 index 0000000..8b74c5a --- /dev/null +++ b/competitor_analysis.md @@ -0,0 +1,521 @@ +❯ Evaluate this project and assess in detail how it can be turnded into a product that has a USP against competitors such as n8n + +⏺ Explore(Explore Clerk codebase structure) +⎿  Done (40 tool uses · 87.1k tokens · 2m 0s) +(ctrl+o to expand) + +⏺ Now I have a thorough understanding of the codebase. Let me provide a detailed strategic assessment. + +--- + +Clerk: Strategic Assessment — Product USP vs. n8n and Competitors + +What Clerk Actually Is (vs. What It Could Be) + +Current state: A Python framework for defining and executing multi-step LLM reasoning workflows, with a React management UI, versioning, RAG, tool calling, MCP support, +and built-in step evaluation. + +The fundamental difference from n8n: n8n is a general workflow automation tool that added AI. Clerk is built exclusively around LLM reasoning — every architectural +decision serves that purpose. This is actually its biggest strategic asset, not a limitation. + +--- + +Competitor Landscape + +┌────────────────────┬────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────┐ +│ Tool │ Core Positioning │ Weakness Clerk Can Exploit │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ n8n │ Visual automation with 400+ connectors; AI is one node │ No reasoning depth, no evaluation, JSON-blob workflows, poor Git integration │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ Langflow / Flowise │ Drag-and-drop LangChain builder │ Visual-only, hard to test/version, no production-grade embedding │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ Dify │ No-code LLM app builder │ Black-box, not developer-extensible, SaaS lock-in │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ LangSmith │ Tracing and evaluation for LangChain │ Evaluation-only, not a workflow runner, expensive │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ CrewAI / AutoGen │ Multi-agent frameworks │ Code-only, no UI, no kit sharing, complex setup │ +├────────────────────┼────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────┤ +│ Rivet │ Node-based LLM editor │ Desktop app, no Python SDK, no community library │ +└────────────────────┴────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────┘ + +--- + +Clerk's Genuine Differentiators (Already Built) + +1. File-based, Git-native workflow definition + Kits are plain text files in directories — diffable, reviewable, testable in CI. Every competitor uses opaque JSON blobs or database-only storage. This is profound: AI + reasoning workflows finally live in version control like the rest of your code. + +2. Built-in step-level evaluation + The 0–100 scoring system per step exists nowhere else at this level of integration. LangSmith charges enterprise pricing for comparable observability. Clerk has it out + of the box. + +3. Python-embeddable + pip install openclerk and call it from any Python application. n8n requires running a separate Node.js server. This means Clerk can live inside an existing backend, not + alongside it. + +4. RAG built into the execution graph + Large resources are automatically chunked and retrieved via similarity search — developers don't have to build this themselves. It's invisible complexity that makes kits + work at scale. + +5. Versioned kits with execution history per version + Full audit trail of which version of a kit produced which output. No competitor connects execution results to the exact prompt/resource version that generated them. + +--- + +The Compelling USP + +▎ "Git-native, evaluation-driven AI reasoning — embeddable in any Python app" + +Position Clerk as the reasoning layer that lives inside your product, not a separate automation tool you connect to. The tagline difference: + +- n8n: orchestrate your services +- Clerk: reason inside your product + +This targets a buyer n8n doesn't serve well: Python engineers building AI-powered features who need testability, evaluation, and code-first workflows. + +--- + +Specific Product Changes to Sharpen the USP + +Priority 1: Make Evaluation the Flagship Feature + +This is Clerk's biggest moat. No one does it well. Extend it: + +- LLM-as-judge: Automatically score steps using a secondary LLM — no human needed. Define scoring rubrics in the kit itself. +- A/B kit testing: Run two versions of a kit against the same inputs and compare scores. This is a product teams desperately need and can't find anywhere. +- Regression tracking: Alert when a new kit version performs worse than the previous one (CI integration via clerk eval --compare-to=v2). +- Benchmark suite: Predefined test inputs + expected output profiles. Kits ship with their benchmarks. + +This makes Clerk the tool you reach for when you need to know if your prompts are actually good — a problem every AI team has. + +Priority 2: Formalize "GitOps for AI Reasoning" + +Lean fully into the developer-native angle: + +- clerk diff kit-name --from=v2 --to=v3: Show what changed between kit versions, including evaluation score deltas. +- GitHub Actions integration: clerk eval as a CI step that fails PRs when evaluation scores drop. +- clerk test: Run a kit against a fixture set, assert on output patterns. Works like pytest for reasoning workflows. +- Publish a GitHub Action that the community can add to their repos. + +This directly attacks the biggest weakness of every visual tool: they're impossible to code-review and untestable. + +Priority 3: Build the Community Kit Library + +The kit format is already portable. Add a registry: + +- clerk publish pushes a kit to a public registry with versioning. +- clerk install research/web-summarizer installs a community kit. +- Kits become the unit of sharing — like npm packages but for reasoning. + +This creates the network effect that n8n built with its 400 integrations, but on the reasoning side. A "Code Review Kit", "Due Diligence Kit", "Customer Support +Escalation Kit" — written once, used everywhere. + +Priority 4: Deepen the Python SDK + +Make embedding frictionless: + +from openclerk import Clerk + +result = await Clerk("research/summarizer").run( +resource_1=document_text, +model="claude-opus-4-6" +) + +- Type-safe kit invocation: Generate TypedDict input/output types from kit schemas. +- pytest plugin: @clerk_fixture("my_kit") makes kits first-class in Python test suites. +- FastAPI dependency: kit: Clerk = Depends(clerk_kit("summarizer")). + +This makes Clerk invisible to end-users but indispensable to developers — exactly the kind of infrastructure that gets adopted and stays adopted. + +Priority 5: Avoid Competing on Integrations + +Do not try to build 400 connectors to compete with n8n. Instead, go deep on LLM-relevant integrations only: + +- Document parsers (PDF, Excel, Word, email — partially done) +- Vector databases (Pinecone, Weaviate, Qdrant) as resource sources +- Code execution (run Python/JavaScript as a tool in a kit) +- Web scraping / browser automation as a built-in tool +- MCP (already started) — this becomes a killer feature as MCP adoption grows + +--- + +Go-to-Market Positioning + +Target buyer: Python engineers and ML engineers at companies building AI features (not ops teams building automations). + +Message: "Your LLM workflows deserve the same engineering discipline as your code — versioning, testing, evaluation, and code review. Clerk brings that to AI reasoning." + +Acquisition: Open-source core with cloud-hosted kit registry and team collaboration features. The self-hosted path (already works) gets developers in the door. The +hosted registry converts them to paying customers. + +Competitive wedge against n8n specifically: Position n8n as the right tool for connecting Slack to Jira. Position Clerk as the right tool for building the AI reasoning +inside your product. These are not the same problem — and n8n's visual, connector-centric model is actively bad at deep LLM reasoning with evaluation. + +--- + +What to NOT Build + +- A visual drag-and-drop editor (plays on n8n's turf, costs enormous engineering resources) +- 100+ third-party connectors (wrong market, n8n has an insurmountable lead) +- A no-code offering (contradicts the developer-native positioning) +- A standalone chatbot builder (Dify, Botpress, etc. already own that) + +--- + +Summary: The Honest Assessment + +Clerk already has better LLM-specific architecture than n8n. The gap is not technical — it's: + +1. Evaluation depth (your biggest moat, needs investment) +2. Community kit library (the network effect you don't yet have) +3. Developer UX polish (SDK ergonomics, CLI, CI integration) +4. Positioning clarity (must be explicit that you're the reasoning layer, not a workflow tool) + +The risk is trying to match n8n's breadth. The opportunity is going deeper on reasoning quality than any competitor currently does. No tool today lets you answer "are my +prompts getting better or worse over time?" in a way that integrates naturally into a development workflow. That's the product to build. + +❯ claude --resume + +⏺ It looks like the /usage status dialog was shown and dismissed. Is there something specific you'd like to work on next? + +❯ Okay and now Evaluate this project and assess in detail how it can be turnded into a product that has a USP against competitors such as agent skills + +⏺ I already have a thorough understanding of the codebase from the previous exploration. Let me provide the analysis directly. + +--- + +Clerk: Strategic Assessment — USP vs. Agent Skills Platforms + +Defining the Competitive Space + +"Agent skills" encompasses several overlapping categories: + +┌─────────────────────────────────────┬─────────────────────────────────────────────────────────────────────┐ +│ Category │ Examples │ +├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤ +│ LLM tool/function calling standards │ OpenAI Function Calling, Anthropic Tool Use, MCP │ +├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤ +│ Agent skill SDKs │ LangChain Tools, Semantic Kernel Skills, Haystack Components │ +├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤ +│ Agent orchestration frameworks │ CrewAI, AutoGen, LangGraph (which Clerk already uses internally) │ +├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤ +│ Hosted skill platforms │ OpenAI GPT Actions, Microsoft Copilot Studio, Amazon Bedrock Agents │ +├─────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤ +│ Autonomous agent runners │ AutoGPT, AgentGPT, OpenAgents │ +└─────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘ + +The common thread: a "skill" in all these systems is a single, atomic, stateless capability — search_web(), run_code(), query_database(). An agent then +strings these together at runtime, dynamically. + +Clerk's kits are architecturally something different — and that's the strategic opening. + +--- + +The Core Architectural Difference + +Agent skills = atomic, stateless, agent-directed +Agent → decides → calls skill(params) → gets result → decides next + +Clerk kits = compound, stateful, deterministic reasoning chains +Kit → step 1 → output 1 → step 2 (uses output 1) → output 2 → step 3 → final result + +In agent skill systems, the agent decides what to do next. In Clerk, the reasoning workflow is predefined — the kit author encodes expert reasoning logic +that runs reliably every time. + +This is not a weakness. It's the USP. + +--- + +What Agent Skill Platforms Cannot Do (That Clerk Can) + +1. Encode Expert Reasoning, Not Just Capabilities + +A LangChain tool gives an agent the ability to search the web. A Clerk kit encodes how an expert would research a topic — which sources to check, how to +synthesize conflicting information, how to structure the output. The reasoning process itself is the valuable artifact. + +Agent skills answer: "What can the AI do?" +Clerk answers: "How should the AI think about this problem?" + +2. Guarantee Reproducible Multi-Step Reasoning + +Autonomous agents are notoriously non-deterministic — the same prompt produces different tool call sequences. For production use cases (compliance, +finance, medical, legal), this is a disqualifying problem. + +Clerk kits produce consistent reasoning paths. Step 1 always runs before Step 2. The same inputs produce the same reasoning chain. You can version, +audit, and regression-test the process. + +3. Evaluate the Quality of Reasoning at Each Step + +No agent skill platform has built-in evaluation of intermediate reasoning quality. They tell you what the agent did (tool call logs), not whether the +reasoning at each step was good. + +Clerk's step-level scoring (0–100), LLM-as-judge capability (to be built), and version-to-version comparison is genuinely unique in this space. It +answers: "Did the new version of this reasoning process perform better?" + +4. Work With Large, Unstructured Resources + +Agent skill frameworks expect clean, structured tool inputs. Clerk's built-in RAG handles large, messy documents — a 55KB text file, a multi-sheet Excel +workbook — and automatically retrieves relevant chunks per step. The kit author doesn't manage chunking, embedding, or retrieval. It's invisible. + +5. Be Owned and Versioned by the Development Team + +GPT Actions, Copilot Studio skills, and Bedrock Agents all live in vendor-controlled platforms. Clerk kits are text files in your Git repository. They +get code-reviewed, branched, tagged, and deployed with the rest of your code. + +--- + +Competitor-Specific Positioning + +vs. OpenAI GPT Actions / Anthropic MCP Tools + +These define what data or capability an LLM can access. Clerk defines how the LLM should reason — a higher-level abstraction. A Clerk kit can use MCP +tools internally (already supported), while itself being a reusable reasoning module. Clerk is not a competitor to MCP; it can be a consumer of it. + +Positioning: "MCP gives your agent hands. Clerk gives it a brain." + +vs. LangChain Tools / Semantic Kernel Skills + +These are developer frameworks for building individual skills. Clerk is a framework for building reasoning workflows made of multiple steps with +resources, versioning, and evaluation. A LangChain tool is a function. A Clerk kit is a workflow. + +Positioning: "LangChain tools call APIs. Clerk kits think through problems." + +vs. CrewAI / AutoGen (Multi-Agent) + +These orchestrate multiple agents with different roles, communicating dynamically. This produces emergent, hard-to-audit, often expensive behavior. Clerk +is deterministic, cheaper (fewer LLM calls), and auditable. For well-defined reasoning tasks, Clerk is strictly better. + +Positioning: "CrewAI for when you want AI improvisation. Clerk for when you need AI reliability." + +vs. Microsoft Copilot Studio + +Enterprise skill builder for Microsoft 365. Deeply vendor-locked, requires Microsoft ecosystem, no programmatic access. Clerk is Python-native, +self-hostable, open-source, and embeds in any stack. + +Positioning: "Copilot Studio if you're all-in on Microsoft. Clerk if you want to own your reasoning." + +vs. Amazon Bedrock Agents + +AWS-native, powerful, but requires significant AWS infrastructure investment. Skills are Lambda functions. Debugging and evaluation are weak. Clerk is a +pip install away and runs anywhere. + +Positioning: "Bedrock Agents for AWS-native teams. Clerk for teams who want reasoning that travels with their code." + +--- + +Product Changes to Sharpen the USP + +Priority 1: Kits as Publishable, Installable Reasoning Modules + +The single biggest gap against agent skill platforms is their marketplace. GPT Actions has a store. Semantic Kernel has a growing skill library. Clerk +has no community layer yet. + +Build it: + +- clerk publish reasoning-kits/due-diligence → uploads to a public registry +- clerk install finance/company-analysis → installs a community kit +- Versioned, rated, forkable — like npm but for reasoning workflows +- Kits include their own test fixtures and evaluation benchmarks + +This is the network effect. A "Document Review Kit", "Code Audit Kit", "Market Research Kit" written by domain experts and shared with the community +creates compounding value that no individual agent skill does. + +Priority 2: Make Kits Callable as Agent Tools + +Ironically, Clerk should make its kits first-class agent skills — callable by any agent framework: + +# As a LangChain tool + +from openclerk.langchain import kit_as_tool +research_tool = kit_as_tool("research/summarizer") +agent = initialize_agent(tools=[research_tool], ...) + +# As an MCP server + +clerk mcp-serve # exposes all kits as MCP tools + +This means Clerk kits plug into CrewAI agents, Claude tool use, OpenAI Assistants — wherever agents call tools. Clerk becomes the implementation of agent +skills that require multi-step reasoning, not a replacement for agent frameworks. + +This is a judo move: instead of competing with agent frameworks, become the highest-quality tool they call for complex reasoning tasks. + +Priority 3: Determinism and Auditability as Enterprise Features + +Agent skill platforms have a reliability problem in regulated industries. Lean into Clerk's deterministic execution as an enterprise differentiator: + +- Execution audit trail: Full record of every step — prompt used, output generated, model, tokens, latency — tied to exact kit version. Exportable as + JSON or Markdown (already partially built). +- SOC 2 / HIPAA positioning: "Your AI reasoning runs on your infrastructure, with complete audit logs." +- Reproducibility guarantee: Same kit version + same inputs = same reasoning path. Testable. +- Human-in-the-loop gates: Pause/resume at defined steps (partially built) → a compliance checkpoint pattern. + +No agent skill platform targets this market with conviction. It's available to Clerk now. + +Priority 4: Evaluation as a Developer Workflow, Not an Afterthought + +The step-level evaluation is the deepest moat. Extend it aggressively: + +- LLM-as-judge: Define a scoring rubric in the kit YAML; a secondary LLM scores each step automatically. Zero human time. +- clerk eval --kit=research/summarizer --fixtures=tests/: Runs kit against test inputs, scores outputs, fails if below threshold. CI-native. +- Score dashboards: Track evaluation scores over time per kit version. See regressions instantly. +- A/B kit testing: Run kit-v3 and kit-v4 against the same input set, compare scores side-by-side. + +This makes Clerk the only tool in the agent/skill space that answers: "Is my reasoning actually improving?" + +Priority 5: First-Class Streaming and Observability + +Agent skill platforms are black boxes — you see the final output. Clerk should expose the entire reasoning process in real-time: + +- Step-by-step streaming (already built via SSE) — make this a first-class SDK feature +- OpenTelemetry export: Send step spans, token counts, latency, and evaluation scores to any observability backend (Datadog, Grafana, Honeycomb) +- LangSmith integration: For teams already using it, send Clerk execution traces there +- Reasoning transparency as a marketing point: "See exactly how the AI reached its conclusion, step by step" + +--- + +What to Avoid + +Do not build an autonomous agent. The market is saturated and the failure modes are public. Clerk's determinism is a feature — don't undermine it by +adding dynamic agent loops. + +Do not try to support all LLM frameworks as orchestrators. Clerk uses LangGraph internally; that's fine. Expose kits as tools that other frameworks can +call; don't try to replace those frameworks. + +Do not chase the visual editor. Every agent skill tool has a drag-and-drop interface. Clerk's text-file, code-first approach is genuinely differentiated +for developers. A visual editor makes Clerk worse at the thing it's best at. + +--- + +The Positioning Statement + +▎ Clerk: Expert reasoning workflows that run reliably, evaluate automatically, and live in your Git repository. + +Target buyer: Python teams building AI features who've discovered that autonomous agents are unpredictable, LangChain tools are primitives, and they need +reproducible, testable, auditable reasoning logic they can actually own. + +The USP in one sentence: Clerk is what you use when you want AI to reason the way an expert would — every time, provably, with evaluation built in. + +✻ Brewed for 1m 18s + +❯ Okay now come up with a list of use cases where CLERK could com in handy + +⏺ Based on my understanding of Clerk's architecture — multi-step sequential reasoning, versioned kits, RAG for large documents, tool calling, streaming, evaluation — here +are concrete use cases grouped by domain: + +--- + +Document & Content Analysis + +- Contract review — extract clauses, flag risks, compare against standard terms +- Due diligence on companies — research financials, news, reviews across multiple steps +- Academic paper summarization — extract methodology, findings, limitations in structured form +- Resume screening — score candidates against job requirements with reasoning shown +- RFP/tender analysis — parse requirements, assess fit, generate response outline +- Terms of service comparison — diff two ToS documents and surface user-impacting changes + +Research & Intelligence + +- Competitive intelligence — gather data on a competitor, synthesize into a structured report +- Market research — analyze industry reports, extract trends, produce executive summaries +- Patent analysis — identify prior art, assess novelty, summarize claims +- Scientific literature review — synthesize findings across multiple papers on a topic +- News monitoring & briefing — fetch articles, filter by relevance, produce daily digest + +Software Development + +- Code review kit — analyze a PR diff, flag security issues, style violations, logic bugs +- Architecture review — evaluate a design doc against known patterns and anti-patterns +- Dependency audit — check package versions, known CVEs, license compatibility +- Test coverage analysis — identify untested logic paths and suggest test cases +- Documentation generation — read source code, produce accurate docstrings and READMEs +- Bug triage — given a bug report and relevant code, reason through likely root causes + +Sales & CRM + +- Lead enrichment — research a prospect, synthesize LinkedIn, company site, news into a brief +- Call preparation — given a contact and deal context, produce a tailored agenda and talking points +- Churn risk analysis — review customer activity, support tickets, usage patterns, score risk +- Proposal generation — take a customer brief and produce a structured sales proposal + +Finance & Compliance + +- Earnings call analysis — extract guidance, sentiment, key metrics from transcript +- Invoice reconciliation — compare invoice data against PO and contract terms +- Regulatory compliance check — assess a document against a specific regulation (GDPR, HIPAA) +- Expense report audit — flag policy violations in submitted expenses +- KYC/AML screening — research an entity across multiple sources for risk signals + +Customer Support & Operations + +- Ticket triage and categorization — classify support tickets, assess urgency, route correctly +- Escalation summary — synthesize a customer's full history into a briefing for the escalation team +- Knowledge base gap detection — compare incoming questions against existing KB articles +- SLA breach analysis — identify which tickets breached SLA and reason about contributing factors + +HR & People Operations + +- Job description quality review — flag biased language, missing requirements, poor structure +- Interview debrief synthesis — aggregate interviewer notes into a structured hiring recommendation +- Onboarding checklist generation — given a role and team context, produce a tailored 30/60/90 plan +- Performance review drafting — given manager notes and objectives, produce a structured draft + +Healthcare & Life Sciences + +- Clinical note summarization — extract diagnoses, medications, follow-ups from unstructured notes +- Drug interaction check — reason across a patient's medication list for known interactions +- Clinical trial eligibility screening — match patient profile against trial inclusion/exclusion criteria +- Medical literature synthesis — summarize recent evidence on a treatment question + +Legal + +- Case research — identify relevant precedents, summarize holdings, assess applicability +- Discovery document review — classify documents for relevance and privilege +- Deposition preparation — given facts and deposition transcript, identify inconsistencies +- NDA/contract negotiation prep — highlight non-standard clauses, suggest counter-positions + +Education & Training + +- Curriculum gap analysis — compare a syllabus against learning objectives +- Student essay feedback — multi-step evaluation: structure, argument quality, evidence, style +- Quiz generation — read a text, produce questions at varying difficulty levels with answer keys +- Personalized study plan — given assessment results, generate a structured remediation plan + +--- + +What Makes These Clerk-Specific (Not Just "Use ChatGPT") + +All of the above benefit from Clerk's specific capabilities: + +- Multi-step reasoning — the answer requires sequential thinking, not a single prompt +- Large document handling — source materials exceed context windows; RAG handles it +- Versioned, auditable process — regulated industries need to know exactly how a conclusion was reached +- Reproducibility — the same document analyzed twice should follow the same reasoning path +- Evaluation — teams can measure whether the kit is actually performing well and improving over time +- Embeddable in existing apps — these aren't standalone tools; they slot into existing Python backends + +❯ Okay now write a short text with max. three paragraphs that explains why and how contributers should and can contribute to the project (both technical as well as domain +experts such as legal etc.) + +⏺ Let me quickly check the existing contributing docs before writing. + +Searched for 2 patterns, read 1 file (ctrl+o to expand) + +⏺ Good — I have enough context. Here's the text: + +--- + +Clerk is built on a simple idea: the best reasoning workflows are written by people who deeply understand the problem domain, not just the technology. That's why +contributions are welcome from two equally important directions. If you're a developer, you can improve the core framework — the execution engine, evaluation system, +API, frontend, or tooling — or build new reasoning kits that others can install and use. If you're a domain expert — a lawyer, analyst, clinician, researcher, or anyone +with hard-won expertise in how to think through a problem — your contribution is the kit itself: the resources, the step-by-step reasoning logic, and the judgment calls +that make a workflow actually useful in practice. + +Contributing a kit requires no Python experience. A kit is just a folder of text files: documents that provide context, and plain-English instructions that define each +reasoning step. A legal professional can encode how they'd review a contract clause by clause. A financial analyst can capture how they'd assess a company's earnings +report. That structured expertise, once written as a kit, becomes reusable by anyone — and improvable by the community over time through versioning, evaluation scores, +and forks. + +For developers, setup takes under five minutes: clone the repo, run just setup, and you're running the full stack locally. For domain experts, the fastest path is to +open an issue describing the reasoning workflow you have in mind — the maintainers will help translate it into a kit or pair with you directly. Either way, the goal is +the same: build a library of reliable, evaluated, community-owned reasoning workflows that anyone can use, audit, and improve. diff --git a/docs_for_dev/PYPI_RELEASE.md b/docs_for_dev/PYPI_RELEASE.md index 1913752..fa71007 100644 --- a/docs_for_dev/PYPI_RELEASE.md +++ b/docs_for_dev/PYPI_RELEASE.md @@ -200,7 +200,7 @@ Before proceeding, clarify the following: 4. ~~**TestPyPI**: Should we automate TestPyPI uploads on every merge to main, or only on demand?~~ **Configured**: Index added to workspace `pyproject.toml`; use `just publish-test` on demand. -5. **Homepage URL**: The README shows `openclerk.dev` - is this the correct domain for `project.urls.Homepage`? +5. **Homepage URL**: The README shows `openclerk.ch` - is this the correct domain for `project.urls.Homepage`? 6. ~~**License**: The LICENSE file is at root level. Should it be copied to `packages/clerk/` or is hatchling configured to find it at workspace root?~~ **Resolved**: Copied to `packages/clerk/LICENSE` and verified in wheel/sdist. diff --git a/packages/clerk/README.md b/packages/clerk/README.md index e913ae9..6637b83 100644 --- a/packages/clerk/README.md +++ b/packages/clerk/README.md @@ -45,7 +45,7 @@ clerk web ## Documentation -For full documentation, visit [https://openclerk.dev/docs](https://openclerk.dev/docs) +For full documentation, visit [https://openclerk.ch/docs](https://openclerk.ch/docs) ## License