kedge
Turn manual Excel processes into reviewable, reproducible marimo notebooks, with an AI copilot that operates the notebook through a controlled tool surface.
Status: under active construction. See
PLAN.mdfor the full brief.
What it does
- Analyses a workbook offline — formula regions, dependency graph, SQL connections,
Power Query M, cached values, and a findings list (circular refs, volatile functions,
IFERRORswallowing, inconsistent formulas within a region). - Plans the conversion — an AI-proposed, user-approved
ProcessPlandescribing stages, open questions, and what it intends to drop. - Scaffolds a marimo notebook from the approved plan.
- Reconciles the generated Python against the values Excel last cached, so the translation checks itself against evidence rather than declaring itself finished.
Quick start
uv tool install kedge
kedge hub # a browser landing page: every workbook kedge has seen
kedge open process.xlsx # straight to one, if you already know which
kedge hub starts the server with nothing open and puts a browser on the list. Add a workbook by
browsing the filesystem, pasting a path, or dropping one on the page; each row shows whether the
file is still there, whether a notebook and an approved plan exist, how many findings the analysis
turned up and how much of the workbook the plan believes it can convert. Opening one runs the same
sequence kedge open runs — clean up, analyse, plan, scaffold, spawn marimo, bootstrap the session
— with the progress streamed into the page, and lands you in the chat-plus-notebook view. Where a
marimo kedge started is still running, the hub offers to reattach rather than starting a second.
The AI half needs an endpoint. Open Settings on the hub and give it an OpenAI-compatible base
URL, a key and a model — the hosted API, a gateway, or something local. The base URL and model go
to ~/.kedge/config.toml; the key goes to the operating system's keyring and never to a file.
Until a key is stored, workbooks open in demo mode, where a scripted agent answers and nothing is
sent to a model — so the analysis, the scaffold and the notebook all work with no endpoint at all.
Behind a corporate proxy, kedge verifies the endpoint against your operating system's trust
store rather than against Python's bundled certifi, so a TLS-inspecting proxy whose root your
IT department has already installed just works. Where it does not, kedge doctor says which
certificates are in play and what to do; the fix is ca_bundle under [model], pointing at the
proxy's root as a PEM. There is no option to disable verification (SECURITY.md says why).
Reasoning is set in the same panel, and left unset by default. kedge prefers the responses API
because it is the only one that carries a reasoning model's thinking across a tool call, and every
kedge turn is tool calls. Neither that choice nor the reasoning setting can end a turn: an endpoint
with no /responses route is discovered on the first call and spoken to in chat completions from
then on, and a request refused over reasoning is retried without it. Pin api under [model] in
~/.kedge/config.toml to skip the probe.
Or, offline and standalone — the analyser is useful on its own:
kedge inspect process.xlsx --out analysis.json --report report.html
docs/analyser-worked-example.md walks that output line by line over two of the test fixtures —
a clean pipeline and a deliberately hostile workbook — which is the quickest way to see what the
analyser actually finds before pointing it at anything of your own.
The planning step is a set of commands as well as a conversation, and the review gate is the same either way:
kedge plan propose process.xlsx --dry-run # read the plan; write nothing
kedge plan propose process.xlsx # save it, as a draft
kedge plan show process.xlsx # stages, open questions, drops, what blocks approval
kedge plan acknowledge process.xlsx --all # sign off the ranges it proposes to drop
kedge plan approve process.xlsx # nothing is scaffolded before this
Only propose needs a model. Every other verb reads a plan from disk and writes a decision back,
so a plan can be read, questioned and approved with no endpoint configured at all. Approving is
always a separate act — there is no flag that proposes and approves in one breath — and a plan
that proposes dropping a range cannot be approved until each drop has been confirmed or refused,
because silent removal is indistinguishable from a bug. kedge plan reject and
kedge plan request-changes are the other two answers, and kedge plan history lists every
version with its approval state: when the process changes next quarter, the diff of the plan is
the change record, which only works if last quarter's plan is still there.
Replacing a plan that is already approved shows you what changes before it happens. propose
prints the diff against the version in force, and approve prints it again and asks, so nobody
swaps one decomposition for another without seeing the two side by side; --yes skips the
question for scripts. Taking an approval back is deliberate in the same way: reject and
request-changes refuse an approved plan unless you pass --withdraw-approval, because a
notebook may already have been scaffolded from it. A rejection is terminal — the way on from one
is a new plan, not an edit to the rejected one.
propose exits 2 when triage recommends against converting the workbook at all. That is a
result rather than a failure, and it is a different exit code from an ordinary error so a script
can tell "this workbook should not be converted" from "no such workbook". --force overrides it.
Once the notebook exists, hand-ins arrive through a watched folder rather than by hand:
kedge watch process.xlsx --dir \\share\inbox --once # sweep and exit; for a scheduled task
kedge watch process.xlsx # watch until Ctrl-C
Every file is copied into the workbook's managed store, hashed, dated and receipted, so "this run
consumed this file" is a claim you can defend. --once is idempotent: a file already in the store
is skipped by hash. --dir is relative to where you are standing; set ingest.watch_dir in a
kedge.toml beside the workbook instead and it is relative to the workbook, so a scheduled sweep
finds the same folder you did.
Design notes
- Generated code is polars, never pandas — enforced in the validation gate.
- Excel's semantics do not match polars' (rounding mode, empty-vs-null, divide-by-zero).
kedge.xlis a registered polars namespace that makes each compatibility choice explicit and greppable:col("amount").xl.round(2). - Single-user, local, loopback-bound. No accounts, no server deployment.
Contributing
CONTRIBUTING.md is the short version; CONVENTIONS.md is binding — read it before opening a
pull request. CLAUDE.md is the shortest useful orientation to the codebase, RELEASING.md
covers how a version tag becomes a release, and SECURITY.md describes the actual trust boundary
(loopback, no auth, the machine rather than the account), which is worth reading before touching
anything that binds a socket or logs a payload.
uv sync
uv run pytest # unit + corpus
uv run pytest -m contract # live-kernel tests; spawns a real marimo
uv run ruff check . && uv run ruff format --check .
uv run python scripts/guardrails.py
Apache-2.0.
Release files for kedge 0.0.9
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kedge-0.0.9.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kedge-0.0.9-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.9 MB
Release files / kedge-0.0.9.tar.gz
| Download URL | kedge-0.0.9.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c719fd4939ef2f204609d7d64dae70a6b128866eb422b93f897f419dfe2ac0b
|
|
BLAKE2b-256 checksum How to use checksums |
7aadb4f77d61666d5d8b65623d62cb752a330357861c0dd932c94c8f701ab5d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.
Transparency logRelease files / kedge-0.0.9-py3-none-any.whl
| Download URL | kedge-0.0.9-py3-none-any.whl |
|---|---|
| Size | 669.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b9cc88ef29ed7de4ccc2e0d471c0adde7ec07e33f8b734c28ee0ca76c1d567e7
|
|
BLAKE2b-256 checksum How to use checksums |
0063923f2b815f0b58ce07ba4c77b835bbd6888399f5c0963591cc405cbb57fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.
Transparency log