Litigation runs on pages
A contested legal matter arrives as paper: pleadings, court orders, discovered documents, expert reports, and years of correspondence, often thousands of pages across hundreds of PDFs. Courts and opposing parties work by page reference, so the page, not the file, is the real unit of the work.
For many years this practice has organized every matter as a Curio project. Sections mirror the structure of the case: "Pleadings," "Plaintiff's discovery," "Expert reports," "Correspondence." Each document is imported as a Spread PDF, so every page is its own idea space that can be flagged, annotated, and linked. Curio is where the human reads, marks, and thinks.
Then an AI assistant joined the practice, working from the terminal. That created a gap. The assistant could read and analyze the documents, but everything it produced, including summaries, chronologies, and memoranda, floated free of the organized case file. Every reference it made had to be re-found by hand, and nothing it wrote landed where the human actually works.
The Curio CLI closed that gap. The same project the human sees on screen became something the agent can build, file into, cite from, and verify, using supported commands.
What we wanted to automate
Four repetitive jobs, and one discipline:
- Bundle construction at scale. A new matter can mean hundreds of PDFs. Creating sections, importing each document page by page, and titling every page by hand is days of work.
- Correspondence filing. Emails and their attachments should land in the project as a documentary hierarchy, not a flat pile.
- Page-anchored citation. Every factual claim in a chronology or memorandum should be one click away from the page that supports it.
- A shared margin. Notes the human and the agent both read and write, attached to the pages they concern.
- Verification. The agent must prove its work actually persisted, rather than assume it did.
Building a bundle the agent can cite
Everything below uses fictional examples. Start with a fresh project, created entirely from the shell:
curio project create "~/Matters/Example Matter/Example Matter (Working Bundle)"
The response returns a project_uuid and a default_section_id, and the project opens ready for work. Create the case structure:
curio create section --title "Pleadings" --project-id <project-uuid>
Then import each document as a Spread PDF with a page-title manifest:
curio import "notice-of-motion.pdf" --as spread-pdf \
--section <section-uuid> --page-titles titles.json \
--project-id <project-uuid>
where titles.json follows a simple convention:
[
{"page": 1, "title": "14-03-2025 — Notice of Motion · p1/12"},
{"page": 2, "title": "p2/12"},
{"page": 3, "title": "p3/12"}
]
The first page of each document carries the date and a description; later pages carry only their page number. Two useful things fall out of this. Sections sorted by title read as a chronology of the case, and any page the agent cites can be identified at a glance.
Two habits matter throughout:
--project-idon every call. The CLI accepts a project selector on its live commands, so a scripted build addresses its target explicitly and never depends on which window happens to be frontmost. The build runs in the background without stealing focus from whatever the user is doing.curio batchfor whole-matter builds. One command per line, named results, and$name.idreferences let a single batch file create a folder and then import into it. Each line returns a JSON result, and the batch ends with asave.
The part that makes all of this worth doing: every item the CLI creates returns a stable link field, a curio:// URL addressed by UUID. The build harvests every link into a small JSON "link map." When the agent later writes a chronology or memorandum, each entry carries the link for the page it relies on. Clicking it opens the project at that exact page. Retitling pages or reorganizing the project does not break these links, because they address UUIDs rather than titles.
Filing correspondence as a documentary hierarchy
Email needs more structure than a flat import. The convention we settled on, after some trial and error: folders carry the structure, spread pages are the leaves.
Correspondence (section)
└─ 📁 12-03-2025 — Email — Opposing attorneys — R — extension request
├─ the email's own spread pages
└─ 📁 Attachment 1 — Draft consent order (draft-order.pdf)
└─ that attachment's spread pages
Each email becomes a folder titled with its date, sender, direction (received or sent), and a one-line gist. The email's pages sit directly inside; each attachment gets its own subfolder; attachments of attachments recurse. The CLI supports all of it: folders inside sections, spread imports under a parent folder, and move --parent with an explicit position for anything that needs rearranging. Emails are de-duplicated by their Message-ID before filing, and each item's note records enough identity to answer "where did this come from" later.
Reading the structure back is one command:
curio assets --section <section-uuid> --recursive --organizer_order --project-id <project-uuid>
which returns the full nested tree, with children under each item and both a top-level count and an all-descendants total_count, so a filing script can verify the hierarchy it just built.
The agent verifies its own work
The most transferable lesson from months of daily use is a discipline, not a command. Treat every write as a transaction with three parts:
- Mutate through the CLI, addressing the target with
--project-id. - Save once per group. After a group of imports and related edits, issue one explicit
curio save --project-id <project-uuid>. The save acts as a settlement and persistence barrier: when it returns, the work is on disk. - Read it back. Confirm the result from the saved file, not from the command's response:
curio get --project "~/Matters/Example Matter/Example Matter (Working Bundle).curio" \
--id <figure-uuid> --fields id,kind,display_page,asset_file
The --project <path> transport reads the saved package directly, without needing the app's attention, so a build script can sample figures across a large import (first, middle, and last pages) and confirm each one persisted with the right page displayed. Name the fields you need explicitly: the default field set on get and query is deliberately compact (id, kind, title), which is easy on context windows but will make a naive verification script report false failures.
For visual checks, the agent exports an idea space and looks at it:
curio export --format png --id <ideaspace-uuid> ~/scratch/check.png
An agent that can read images can confirm its own layout and rendering without asking the human to look.
Working together on one surface
Automation is half of the picture. The other half is that the human and the agent now share a workspace.
- A shared margin. Idea-space notes hold observations from both sides. The human writes through the Notes inspector; the agent reads notes and appends through
curio set --id <uuid> --note, prefixing its own lines so authorship stays clear. A morning's marks on the evidence become instructions the agent can collect and act on. - "This page, these documents." When the human says "summarize the document I've selected," the agent resolves it by reading the live selection with
curio get --selected --fields allrather than asking for a filename. - Agent deliverables on the canvas. A generated HTML report imported with
curio import report.htmllands as a live web view figure and renders directly on an idea space, diagrams included. Status dashboards and interactive chronologies can live inside the same project as the evidence they describe.
Lessons, limitations, and what this generalizes to
Lessons.
- Trust the read-back, not the response. Verification against the saved file caught the few problems that mattered; everything else was noise.
- Pin the target on every scripted call. Implicit addressing follows the frontmost window, which is right for a person and wrong for a script running while that person works on something else.
- Make the manifest the artifact. Generating page titles and batch files from a small script, and keeping the JSONL responses and the harvested link map, means every build is reproducible and every link is recoverable.
- The section structure is semantic. A document's placement tells the agent what role it plays, the way it would tell a human colleague. "The same report" means something different in "Expert reports" than in "Plaintiff's discovery," and the agent can use that.
Limitations.
- Writes need Curio running; the read-only
--projecttransport works either way. - A titling or manifest bug replicated across hundreds of imports is tedious to unwind (move-to-Trash is the delete idiom). Rehearse on a scratch project and verify early.
- The links are only as durable as the project file's name and location, so renaming the project file is a deliberate act with consequences.
What this generalizes to. Nothing here is specific to law. Any practice that lives on paginated source documents, such as compliance review, medical-record analysis, academic archives, or investigative research, can use the same pattern: sections as meaning, one page per idea space, dated titles, batch builds, link maps, and an agent that files its work into the same project its human reads.
The included snippet, bundle_builder.py, is a compact, sanitized illustration of the build-and-verify loop: it plans a bundle from a folder of PDFs, writes the page-title manifests and a batch file, runs the batch, harvests the returned links into a link map, and verifies a sample of figures against the saved package.