AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Cairn is an agentic AI copilot that lives inside your product and actually completes real multi-step tasks for your customer — clicking, filling, navigating, or talking — while they just say what they
Developer toolsTypeScript · PythonGitHub @Vikasverma9515OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Don't learn the software — just tell it what you want. An agentic AI copilot that reads your app's real source and completes real multi-step tasks in it — not a chatbot that just points at buttons.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Don't learn the software. Just tell it what you want.
Vibe coding is describing the app you want built. Cairn is vibe using — describing what you want done , in software you've never opened before.
▶ Watch the full-quality MP4 · Live site
New here and confused about what's in this repo? Read
DEVELOPMENT.md first — it explains the two separate
things this repo contains and which one this README is about (short
version: this one, the product).Read the full README ↗New here and confused about what's in this repo? Read DEVELOPMENT.md first — it explains the two separate things this repo contains and which one this README is about (short version: this one, the product).
The site lives in
site/(a static Next.js export). The previous version is still served at/classic.
Normally, using new software means learning it — where the buttons are, what they're called, which order to click them in. Cairn flips that around: you describe what you want, out loud or typed, and an agent does it — using the app's real buttons and real actions, the same way you would with a mouse. Say "build me a flow that emails me on a new lead," and it happens in front of you, in a tool you've never opened before.
It's not a chatbot bolted onto the side that tells you what to click. It plans the steps, checks its own work, and gets the whole thing done — clicking, filling, navigating, or talking, whatever the goal actually needs.
Status: working end-to-end today, on any framework — install
(cairn init), analyze (cairn build, either reading Next.js source
directly for the most precise result, or crawling any other framework's
running app), and run (<Copilot/> for React, or the framework-agnostic
<cairn-widget> for Vue, Angular, Svelte, even a plain static HTML page)
are all live-verified, not just planned — including live LLM calls
(Anthropic, Groq, or Gemini) and live voice (Deepgram STT/TTS), identical
either way. Next.js gets the deeper, AST-based analysis; everything else gets
the same real runtime via a headless-browser crawl instead of a source
read. See ROADMAP.md for what's actually left (mostly
open-source polish at this point) and DEVELOPMENT.md
for the full build history, phase by phase.
Published on npm as @cairnvibe/core, @cairnvibe/indexer, and
@cairnvibe/sdk. The Quick start below builds from this repo directly
instead, since that's what you want if you're developing Cairn itself
or running the example app — for installing into your own project
(Next.js or otherwise), see "Install into your own project" right below.
One command, in an existing Next.js app:
npx @cairnvibe/indexer setup
This installs the three packages (with a real retry prompt if that
fails, not a dead end), asks a couple of quick skippable questions
through a polished interactive UI (@clack/prompts — the same one
create-t3-app and create-next-app use) — which LLM provider, its key
(masked as you type, never echoed in plain text), voice on request —
scaffolds the backend route, generates a small components/CairnCopilot.tsx
"use client" wrapper and wires it into your real app/layout.tsx or
pages/_app.tsx automatically (a real AST edit, not a blind string
splice — it never touches a file it can't confidently parse, and falls
back to printing the manual instructions instead of guessing), adds
transpilePackages to your next.config.* so bundlers actually
transform the package's source instead of failing cold on it, builds
the manifest once if a key was given (with a spinner, and a real
retry/switch-provider recovery flow if the build hits a rate limit or a
bad key instead of just dying), and adds a prebuild script so the
manifest regenerates itself on every future npm run build — build
and redeploy, and it stays current with no extra step. A build with no
key configured yet (e.g. before you've set env vars on your hosting
platform) skips that step cleanly instead of failing the whole build.
(The wrapper component matters: app/layout.tsx is a Server Component
by default, and passing <Copilot/>'s onDo function prop to it
directly from a Server Component fails — "Event handlers cannot be
passed to Client Component props." A real project surfaced this live;
setup generates the same small client-wrapper shape
examples/demo-app already uses to avoid it.)
Prefer full manual control instead? cairn init <dir> does the
non-interactive, no-installs, no-prompts version of the same
scaffolding — see the CLI reference below.
Everything generated that isn't forced to live somewhere specific by
Next.js's own routing conventions goes into one dedicated .cairn/
folder — ui-manifest.json, .cairn/.env.example, and a small
install-manifest.json that records exactly what this run touched.
(Two things genuinely can't move there: the API route file itself, since
Next.js only treats it as a real endpoint at its conventional path, and
the one <CairnCopilot/> line in your layout, since it has to render
somewhere real.) That record is what makes uninstalling a real, one-command
operation instead of hunting down every file and edit by hand:
npx cairn remove
Deletes .cairn/ and everything it generated, removes the widget's
import/JSX from your layout via the same AST-precise edit that added it,
strips just the two transpilePackages entries it added (or deletes the
whole config file if it created that too), restores your original dev
script, drops the prebuild script it added, and uninstalls the three
packages. Real API keys in .env/.env.local are never touched — it
reports what's still there so you can clean those up yourself.
your app ──► read source ──► map what's real ──► register actions ──► live agent
(AST scan) (reachability) (your own auth) plans, acts, verifies
At runtime, <Copilot/> reads the user's question + current route +
visible elements (both the build-time manifest AND a live scan of what's
actually rendered right now — see "What's actually running" below),
sends them to your own /api/copilot route, and gets back exactly one
verb from a fixed 16-verb enum — never a raw selector, never
arbitrary code:
explain, highlight, open, navigate, tour, doclick, fill, read, call_tool, drag, select, key, scroll, wait_for, batchA question that needs more than one step ("find this invoice, then
archive it") drives a real Planner → Executor → Critic loop: a Planner
call breaks the goal into ordered tasks, each continuing step actually
runs, and a genuinely separate Critic pass checks the real result before
deciding to continue, replan, or stop — not the executor grading its own
work. Every single verb, on every step, is independently re-validated
server-side against the same fixed schema, so a prompt-injection attempt
in the user's question (or in anything scraped from the page) can never
produce an action that wasn't explicitly registered (see
packages/sdk/src/server.test.ts). A lookup miss always degrades to a
plain explanation — it never guesses and clicks the wrong thing.
data-ai tagging, so the agent can address a
dynamically-rendered row/card the indexer never saw ahead of time, or
even a plain <div onClick> styled as a button (runtime-scan.ts).resolvePlan/resolveCritic in
packages/sdk/src/server.ts).table-crud, kanban, canvas,
search-filter, wizard) and, when one matches, the Planner gets a
starting-point hint for how that kind of page is usually operated —
never a script, still fully re-verified (packages/core/src/ui-patterns.ts,
playbooks.ts).packages/sdk/src/skill-store.ts, compileSkill/matchSkillByGoal in
server.ts).remember, small, always
injected), Recall (every turn, keyword-searchable), and Archive
(long-term facts, pulled in only when relevant) — a real SQLite store,
not just "the last few turns" in one request (memory-sqlite.ts).Invoice { status: "Paid" | "Overdue" }), real guard-clause business rules ("must be logged in"),
real human-authored heading/paragraph copy, and which real project
function actually handles a traced API call — so the agent reasons
from what's real, not a guess (packages/indexer/src/l1-data-shapes.ts,
l1-business-rules.ts, l1-in-app-copy.ts, l1-api-routes.ts).document.modelContext.registerTool) as the highest-trust action
source there is; a tool can require a real end-user confirmation before
it runs, which only the page's own registration can set, never the
model (; auto-generates this from
already-traced actions — see the CLI table below).The widget follows your site. theme="auto" (the default) gives a dark widget on a dark page and a light one
on a light page, and switches live if your site toggles theme (it looks at a dark/light class or
data-theme on <html>/<body>, then the page's background colour, then the OS setting). Force
theme="light" or theme="dark" to override, and pass accent="#8b5cf6" to colour the launcher, your
messages and the send button. The widget's input also holds its own colours against global input { ... !important }
rules, so the text stays readable inside apps that style every input.
Nobody authors a playbook. cairn build reads the code behind every page (the page, its components, server
actions, API routes, data shapes and business rules) and writes an operating guide for each feature:
A second pass links features into workflows that span pages (add a candidate, screen, shortlist, schedule,
send an offer). The skills are stored on the manifest (manifest.skills) and cairn build also writes them to
.cairn/SKILLS.md so you can read exactly what the agent was given. At request time the most relevant guides
(boosted for the page the person is on) go to the Planner and to the model that decides each step, so it knows
how to operate the feature instead of guessing. Controls that delete, reject, send, email or call also get an
ask-first rule: the agent tells the person what it is about to do and waits for a yes in chat.
cairn build runs this by default (--no-skills to skip it; reruns are cached, so only pages whose code changed
cost a model call). Without a written skill a page still gets one derived from the manifest, and the skills work
on serverless hosting with no database (createManifestSkillStore(manifest, [yourSkills]) adds your own).
A request like "open candidates, filter to shortlisted, then open the first one" is several model calls (a step, a Planner pass and a Critic pass for each one). What matters for it to finish:
cairn init/setup now scaffold /api/copilot/plan and /api/copilot/critic
and pass planEndpoint/criticEndpoint to the widget. Without them every request ends after its first step.tsconfig.json paths (so @/components/... works)
and falls back to @/* -> project root. Before this, shared button components imported through an alias
never reached the manifest.CAIRN_MAX_RATE_WAIT_MS, default 20000) and retries. Give the route a matching maxDuration on serverless
hosting. Gemini's free tier allows 15 requests per minute and Groq's free tier 8,000 tokens per minute, so for
heavier use configure several keys (GEMINI_API_KEYS=a,b,c, GROQ_API_KEYS=...) or a paid tier.criticEvery (default 1) sets how often the Critic runs. Raising it saves calls but the Critic is also
what tells the loop a task is finished, so higher values can make the agent keep going.npm install
npm run build -w @cairnvibe/indexer -w @cairnvibe/sdk # compiles the cairn CLI + sdk's server/CLI entry points — needed once
npm install # re-run once so npm links the `cairn`/`cairn-realtime` bins now that dist/ exists
cp .env.example .env # fill in ANTHROPIC_API_KEY, GROQ_API_KEYS, or GEMINI_API_KEY (see .env.example)
npx cairn build ./examples/demo-app # writes examples/demo-app/.cairn/ui-manifest.json
npx cairn build ./examples/demo-app --provider groq # or --provider gemini
npm run dev -w demo-app # or: cd examples/demo-app && npm run dev
# (npm run dev also auto-builds the manifest via predev if it's missing)
The second npm install isn't a typo: npm only links a workspace
package's bin entry into node_modules/.bin if that package's dist/
already exists when npm install runs — and on a fresh clone it
doesn't yet.
app/layout.tsx is a Server Component by default, and React Server
Components reject a plain inline function passed as a prop to a Client
Component — <Copilot/> needs onDo, so it needs a thin "use client"
wrapper in between, not to be rendered in the layout file directly:
// components/CairnCopilot.tsx
"use client";
import { Copilot } from "@cairnvibe/sdk";
export function CairnCopilot() {
return (
<Copilot
registeredActions={["archiveInvoice"]}
onDo={(action, target) => { /* run the write action through YOUR session auth */ }}
reportMissesEndpoint="/api/copilot/misses" // optional — aggregate lookup misses server-side
transcribeEndpoint="/api/copilot/transcribe" // optional — adds a mic button (needs Deepgram)
/>
);
}
// app/layout.tsx
import { CairnCopilot } from "../components/CairnCopilot";
<CairnCopilot />;
(npx @cairnvibe/indexer setup does exactly this for you automatically —
see "Install into your own project" above.)
// app/api/copilot/route.ts — your own route, your own API key, your own auth
import { createCopilotHandler } from "@cairnvibe/sdk/server";
import manifest from "../../../.cairn/ui-manifest.json";
const handler = createCopilotHandler(manifest, {
provider: "groq", // or "anthropic" (default) or "gemini"
registeredActions: ["archiveInvoice"],
});
export async function POST(request: Request) {
const result = await handler(await request.json());
return Response.json(result.body, { status: result.status });
}
Mark anything you want addressed by a stable id, regardless of copy
changes: <button data-ai="create-invoice">New Invoice</button>.
<Link> and any *Button-named component with an onClick are picked up
automatically too — data-ai is for precision, not a requirement.
<Copilot/> is the React entry point. Everything else — Vue, Angular,
Svelte, a plain static HTML page — uses the same widget as a Web
Component instead, self-contained, no dependencies:
<script src="/cairn-widget.js"></script>
<cairn-widget endpoint="/api/copilot" persona="Cairn"></cairn-widget>
Full parity with <Copilot/> — typed Q&A, tours, mic, live voice
conversation. The backend (createCopilotHandler/createRealtimeServer)
is plain Node — no Next.js required server-side either.
npx cairn setup automates all of this for a non-Next project too, not
just Next — it installs everything, scaffolds a standalone backend
(cairn-server.cjs, Express + CORS, with speak/transcribe/realtime wired
in if you turn voice on), injects the <cairn-widget> tag into your real
index.html, and — since a non-Next project has no single dev process to
hook the realtime relay into — generates a small orchestrator
(cairn-dev.cjs) that starts your app, the Cairn backend, and the voice
relay together under your existing npm run dev. Still one command, on
any frontend framework. cairn remove reverses all of it.
What this genuinely needs, honestly stated: every mode needs some
real backend to hold the LLM key and run /api/copilot — a purely static
site with no backend at all can't use Cairn in any mode, since there's
nowhere safe to keep the key. Typed chat and push-to-talk voice are plain
HTTP, so any Node-capable backend works (a serverless function is fine).
Full realtime specifically needs a persistent process running
cairn-realtime — a real, long-lived WebSocket server, not a typical
serverless function (Vercel/Netlify functions are short-lived by design).
On a non-Node site (WordPress, Rails, a static host, a no-backend
builder), that means standing up one small, separate Node service for the
relay + API routes — it doesn't have to be same-origin, realtimeUrl and
the endpoint props just point at wherever it lives.
For an app whose source Cairn can't read (not Next.js, or not this repo at all), point the CLI at a running app instead:
cairn build http://localhost:3000 --provider groq --out .
It crawls same-origin pages with a headless browser and reads the
interactive elements actually rendered — works on any framework's output,
at the cost of less precise "does" descriptions (no source to read for
handler logic). See ROADMAP.md for what crawl mode
doesn't handle yet (client-side-only routing with no real <a href>).
<Copilot
registeredActions={["archiveInvoice"]}
onDo={handleDo}
speakEndpoint="/api/copilot/speak" // typed/mic answers spoken aloud
transcribeEndpoint="/api/copilot/transcribe" // push-to-talk mic button
realtimeUrl="ws://localhost:3010" // full live voice conversation
persona="Cairn"
/>
npx cairn-realtime --port 3010 # its own long-lived process alongside `next dev`
VOICE_CONVERSATION_ADDENDUM
in packages/sdk/src/realtime-server.ts).sampleAncestorBackgroundColor/
pickReadableCaptionColor in packages/sdk/src/index.tsx).memory store
(createSqliteMemoryStore) and a scopeId and you get real, tiered,
cross-session memory — an explicit remember (small, always injected),
a searchable turn history, and long-term facts recalled only when
relevant — not just what fits in one request.explain (read/point-at-things only, plus
tour), guide (adds navigating and clicking), or act (default —
every verb) caps what the agent is allowed to do, independent of
which actions are registered, and can't be bypassed even by a
registered action id.Every spoken/displayed line follows one rule regardless of path: no markdown, never say an internal element id out loud.
| Command | What it does |
|---|---|
cairn setup [dir] | The one-command path — installs dependencies, asks skippable questions, wires the widget into your real layout file, builds once, and sets up auto-rebuild on future builds. |
cairn remove [dir] | Undoes a cairn setup install in one command — the widget, config edits, every generated file, the npm packages. Real credentials in .env/.env.local are never touched. |
cairn init <dir> | The manual-control version of the same scaffolding — no prompts, no installs, no file edits beyond new files. Detects your framework, never overwrites existing files. |
cairn scan <dir> | L1 only, deterministic, no LLM call. |
cairn build <dir> | Full pipeline against Next.js source. |
cairn build <url> | Crawl mode — any framework, from a running app. |
cairn diff <a> <b> | What changed between two manifests. |
cairn docs <dir> | Reads a manifest, writes a human-readable CAIRN_DOCS.md. |
cairn webmcp <dir> | Reads a manifest, generates a CairnWebMcpTools.tsx that registers your already-traced actions as WebMCP tools for any MCP-compatible agent, not just Cairn. |
packages/
core/ @cairnvibe/core — manifest + verb schemas (zod)
indexer/ @cairnvibe/indexer — the `cairn` CLI: scan, reachability, describe, diff, docs
sdk/ @cairnvibe/sdk — <Copilot/> (React) and <cairn-widget> (any framework),
verb executor, server handler, realtime voice, dashboard
examples/demo-app/ — a real Next.js app exercising all of the above
services/graph/ — a separate Python service — see DEVELOPMENT.md before assuming
this is part of the same product
fixtures/ — small fixture project the indexer's unit tests scan
npm test # vitest across all packages
npm run typecheck
npm run determinism # `cairn scan` twice, diff must be empty — no API key required
MIT
webmcp-client.tscairn webmcp <dir>key-rotator.ts, isInvalidKeyError/extractFailedGenerationArguments
in server.ts).window.location.reload() (a common pattern
after a mutation), so a host app refreshing the page mid-conversation
doesn't make it look like the agent simply stopped answering
(loadPersistedConversation/savePersistedConversation in index.tsx).dashboard.ts, reportMissesEndpoint below).buildSystemPromptpackages/sdk/src/server.ts