Write it. Run it. Ship it.
A little natural language. A little Python.
A program that's yours.
uv add kedi

Program with natural language.
Typed by design.
Compose model calls, decisions, and tools as typed programs. Natural language is part of the code, not a string on the side.
# Load the movie lookup tool.> import: tmdb# Let the model use it.> approval: allow> use: movie_details # Find the film by name and capture its details.>> Look up Spirited Away using movie_details. Its director is [director: str], release year [year: int], and runtime [minutes: int] minutes. # Use the result as a program value.= <director>uv add kediThe context, the question, and the shape of the answer.
All in the same line of your program.
>> The next action in is .note: strThe design team will send the event poster to the printer.
~Action(owner: str, task: str) [note] = The design team will send the event poster to the printer.>> The next action in <note> is [action: Action]. = `action`Use the result in the next statement, pass it to a procedure, or hand it to Python.
Let a model evaluate a claim and your program decide what happens next.
[change] = The response field user_id is being replaced by account_id.[track] = standard > if: The API change "<change>" breaks compatibility with existing clients [track] := migration = <track>Does this API change
need a migration?
Natural-language claims and deterministic Python conditions. Same control flow.
[facts] = CSV export ships Monday. PDF is not available.[draft] = Export any report as CSV or PDF today. > loop: The draft "<draft>" makes promises not supported by the facts "<facts>" >> A revision of "<draft>" supported only by "<facts>" would read [revision]. [draft] := <revision> = <draft>Does the copy claim more than the facts support?
Remove unsupported formats and launch dates.
Stop when no unsupported promises are found.
The condition is evaluated again before every iteration.
[reports: list[str]] = `["CSV loses the last row.", "Please add a dark theme."]`[bugs: list[str]] = `[]` > loop [report]: `reports`: >> It is [is_bug: bool] that <report> describes a defect. The issue title is [title: str].> map: > if: `is_bug`: `bugs.append(title)` = `bugs`Each incoming report gets its own iteration scope.
The map receives that report's classification and title.
Collect bug titles; leave feature requests out.
Independent continuations can overlap. Kedi waits for the map stage before moving on; completion order may differ.
Templates are the beginning. Compose them with the tools you already use.
[quote] = 12 seats at 19 dollars per month, billed yearly. >> In <quote>, there are [seats: int] seats at [monthly_usd: float] dollars per seat per month. [annual_usd: float] = `seats * monthly_usd * 12`= `annual_usd`A model reads the pricing terms. Python multiplies the typed numbers to get the annual total.
~Brief(problem: str, next_step: str) @handoff(report: str) -> Brief: >> The on-call handoff for <report> is [brief: Brief]. = `brief` [report] = Checkout times out only after a coupon is applied.[brief: Brief] = <handoff(<report>)> = `brief`Turn a report into a reusable, typed on-call handoff: a problem and a concrete next step.
```import jsonfrom pathlib import Path``` @stock(item: str) -> int: = ``` path = Path("inventory.json") return json.loads(path.read_text())[item] ``` > approval: allow> use: stock>> The stock tool reports [remaining: int] units of "field-notebook". [can_fulfill: bool] = `remaining >= 4`= `can_fulfill`The model retrieves stock through a Kedi tool. Python checks whether there are enough units for the order.
Example inventoryA researcher finds the breaking change. An editor explains the migration. Profiles, tools, subagents, and typed answers, in the same program.
> import: filesystem> model: google/gemini-3-flash-preview > profile: researcher: > instructions: Read CHANGELOG.md. Quote the v2.0 breaking changes. > use: read_text_file > output: list[str] > profile: editor: > instructions: Turn the researcher's findings into concise migration steps. > subagent: researcher > use: editor>> Based on the researcher's findings, API clients should follow [steps: list[str]] to migrate to v2.0. = `steps`Pick a queue. Assess cancellation intent. Let your program decide the priority.
> import: decisions> model: typesafe/jev-latest [ticket] = I was charged twice. Reverse it or I cancel today. >> <ticket> belongs to [queue: Literal["billing", "technical"]].The cancellation intent is [cancellation_intent: Annotated[ Probability, "The customer states an intention to cancel their subscription."]]. = `{"queue": queue, "priority": cancellation_intent >= 0.80}`Literal limits the answer to your defined options.Try a template. Inspect its values. Keep going in the next cell, with your program's state right where you left it.
kedi notebookOpens your browser at localhost:8788/notebook/. Served locally, on your machine.
From a Kedi source checkout with its submodules initialized, install the notebook dependencies and launch it with:
uv run --extra notebook kedi notebookAfter installation, run kedi notebook with that environment active.
[1] > model: openai/gpt-6-luna >> In Türkiye, [city: str] spans the [strait: str]. [2] > show: <city> spans the <strait>. Istanbul spans the Bosphorus Strait.
Start with browser Python, or select a host interpreter with a reusable notebook virtual environment.
Mix Kedi, Markdown, and terminal cells. Save a .kedinb notebook, with optional execution progress and no Secret Manager values.
Write a template, inspect a capture, and build on it. Variables, procedures, and model settings stay with your session.
kedi --idleSyntax highlighting is on by default. Use --no-color for plain text.
$ kedi --idle +++ > model: openai/gpt-6-luna +++ >> [city] is the city of cats. +++ > show: `city.upper()` ISTANBUL +++ > show: Cats love <city>. Cats love Istanbul. +++
Use :multiline for a longer fragment and :show to inspect a value.
Save your session's source to a .kedi file with :dump.
Meet the cat taking the keyboard. An agent harness for your projects, with Kedi as its orchestration language. It can write and run Kedi programs to coordinate its own tools, subagents, and next steps.
Follow the work on GitHub

Co-creatorApplied AI Engineer

Co-creatorPhD Student
A little natural language. A little Python.
A program that's yours.
uv add kedi