Expand description
agd-core reads and safely changes org-mode files. It is the library behind
the agd command line, terminal UI and stdio MCP server, and it works on
local files only.
Every caller follows the same path:
load_settingsreadsconfig.tomlintoSettings: the roots agd may touch, the agenda files, TODO keywords and the other Org options.WorkspaceIndex::loadparses every agenda file under the roots into anIndexedDocumentand gives each heading anItemwith a stable ref: its:ID:when unique, elsefile:line#sha256:digest.AgendaEnginereads the index: agenda days, TODO lists, tags matches, org-ql queries and saved views, and exports them as Emacs prints them.WriteEnginechanges files: edits, captures, refiles and undo, each journaled so it can be reverted.
§Conventions
These hold in every module:
- Org files are user data. A write replaces only the bytes it changes and
leaves every other byte exactly as it was, or it is refused and nothing is
written. A file’s own syntax (
#+TODO,#+PRIORITIES,#+STARTUP) wins over configuration. - An error is an
AgdErrorwhosecodeis one ofErrorCode, the one list of agd.error/v1 codes, named by one rule (invalid_<thing>,unknown_<thing>,<thing>_not_found,<thing>_conflict,<thing>_failed; seeErrorCode); its message says what to do. - Every serialized record names its schema,
agd.<name>/v1, with a*_SCHEMAconstant such asITEM_SCHEMAorMUTATION_SCHEMA. - Config keys are snake_case. Values are lowercase, kebab-case when they
name an Org option (
ids = "add-on-write",sorting_strategy = ["time-up"],kind = "tags-todo"). - Output is deterministic: lists keep a defined order, and dates are
computed from the
todayor clock the caller passes. - Nothing touches the network.
§Example
use agd_core::{
AgendaEngine, Change, EditIntent, WorkspaceIndex, WriteContext, WriteEngine, load_settings,
};
use chrono::NaiveDate;
let dir = tempfile::tempdir()?;
let tasks = dir.path().join("tasks.org");
std::fs::write(&tasks, "* TODO Renew passport\n")?;
let config = dir.path().join("config.toml");
std::fs::write(&config, "deadline_warning_days = 7\n")?;
// `load_settings(None, None)` reads the user's own config.toml instead.
let settings = load_settings(Some(&config), Some(dir.path()))?;
let index = WorkspaceIndex::load(settings.clone())?;
let today = NaiveDate::from_ymd_opt(2026, 10, 16).unwrap();
let todo = AgendaEngine::new(&index).todo(None, today)?;
assert_eq!(todo[0].title, "Renew passport");
let engine = WriteEngine::new(settings, dir.path().join("journal.jsonl"))?;
let context = WriteContext {
actor: "example".to_string(),
project: "docs".to_string(),
now: None,
};
let done = EditIntent {
state: Change::Set("DONE".to_string()),
..EditIntent::default()
};
let mutation = engine.edit(&todo[0].item_ref, &done, false, &context)?;
assert!(mutation.diff.contains("+* DONE Renew passport"));
engine.undo(&mutation.journal_id, &context)?;
assert_eq!(std::fs::read_to_string(&tasks)?, "* TODO Renew passport\n");Structs§
- AgdError
- Agenda
Engine - Agenda
Entry - Agenda
Query - Agenda
Summary - What the status band counts, each item once.
- Agent
Profile - A named, filtered agenda an agent may read at session start.
- Batch
Mutation - What
WriteEngine::edit_batchwrote: one mutation per edit, in the order given, all under one journal transaction; undoing any of its journal ids, or the transaction id, reverts them all. A dry run’s transaction and journal ids aredry-run. - Byte
Span - Capture
Draft - Capture
Prompt - One
%^{...}field a template asks for. A date prompt (%^{name}t,%^t, …) takes the same input asdue:and may be left empty.planningnames the keyword (DEADLINE,SCHEDULED) when the prompt is that keyword’s date on the template’s planning line:due:oron:then answers it, so the prompt need not be asked. - Capture
Template - Check
File Report - Check
Problem - Check
Report - Check
Summary - Checklist
Item - One checklist line.
- Config
Sources - The editor config files an import reads, in load order, and what was passed over.
- Edit
Intent - Error
Response - Headline
Span - Import
Diagnostic - Import
Result - The editor’s settings applied over
base, and what could not be. - Imported
Capture - One capture template as the editor reports it; disabled templates are reported, not imported.
- Imported
Command - One custom agenda command as the editor reports it. Enabled commands
become views;
diagnosticsname what was left out. - Indexed
Document - Item
- Item
Edit - One item and the edit to apply to it, for
WriteEngine::edit_batch. - Journal
Record - LogSettings
- Effective logging for one file: configuration overridden by
#+STARTUP. - Mutation
- Occurrence
- OrgFiles
- Org files discovered under the roots, plus the paths discovery left out.
- Patch
- Planning
- Planning
Timestamp - An Org timestamp:
dateisYYYY-MM-DD, andtimeandend_timeare alwaysHH:MM(<… 9:30>reads09:30);rawis the text as the file writes it, which a write that leaves the timestamp alone keeps. - Priority
Range - A file’s priority cookies, highest to lowest, and the priority of a
heading without one:
#+PRIORITIES: A C B(org-priority-highest, -lowest and -default), else the configured range. - Progress
- A checklist’s progress:
doneof itstotalboxes are checked. - Property
Span - Refile
Result - Repeat
- A repeating task marked done (org-auto-repeat-maybe): the done state that was recorded, and the next occurrence the item now has.
- Resolved
Editor Config - RootSet
- Settings
- Everything agd reads from
config.toml, one flat table: every setting is a top-level key, and only capture templates, views and agent profiles are lists of records ([[capture]],[[view]],[[agent_profile]]). Missing keys take the defaults below; unknown keys are errors. - Stored
Patch - A journaled replacement:
start..endis the span ofafterin the post-image;context_before/context_afterare the post-image bytes around it (the rest of its lines plus one line each way), used to find the span again if the file changed elsewhere before undo. - Timestamp
Span - Todo
Keyword - One TODO keyword with the logging markers from
KEYWORD(key!/@). - Undo
Result - View
Block - View
Group - One group of a view block, as an org-super-agenda group: each row goes
to the first group, in list order, whose match it passes;
discarddrops those rows;bymakes one group per category, parent heading, TODO keyword or priority cookie. Groups are shown byorder, then list order, and rows no group takes come last as “Other items”. - View
Section - One block of a saved view: agenda rows for an agenda block, otherwise a
list of items.
from/toare set for agenda blocks only. A block with groups has its rows ingroupsand none inentriesoritems. - View
Section Group - A grouped block’s rows under one group name, in display order.
- View
Settings - A saved agenda view, the declarative part of an org-agenda custom command: one block, or several shown one after another like a block agenda.
- Workspace
Index - Write
Context - Write
Engine
Enums§
- Agenda
Span - How many days an agenda shows, as org-agenda-span. config.toml writes it
as “day”, “week”, “month” or a TOML integer; command-line and MCP input
is the same names or a number of days (
AgendaSpan::from_str). - Change
- Config
Editor - Error
Code - Every agd.error/v1
code, in one place, for agd-core and theagdbinary alike: each variant’sas_stris the snake_case code consumers match on, and an error is raised asAgdError::new(ErrorCode::Variant, message).agd setup checkreports its problems with the same codes. - IdPolicy
- Import
Status - Journal
Operation - What a journaled write did, so every surface names a change the same way without reading its diff. A review, undo or resolve keeps the operation of the change it settles.
- Journal
Status - LogKind
- How a state change or planning change is recorded, as in Org’s
!(time) and@(note) markers and the org-log-* variables. - LogMode
- What a state or planning change records, as an org-log-* variable: no log, a timestamp, or a timestamp and a note.
- Notation
- How tags and a priority are written in a capture line, and how agd
draws tags:
agdis#workand!A;orgis Org’s own:work:(or:a:b:) and#A. Org’s files,--csvand the agenda text are Org’s either way. - Occurrence
Type - Skip
Reason - Why discovery left a path out instead of failing.
- Week
Start - The day a week agenda starts on.
Constants§
- AGENDA_
ENTRY_ SCHEMA - CHECK_
SCHEMA - An
agd setup checkreport (CheckReport). - COLOR_
NAMES - Colors a keyword or view section may name; the TUI maps them onto its light or dark palette.
- DESCRIBE_
SCHEMA - The MCP
describetool’s answer. - ERROR_
SCHEMA - ITEM_
SCHEMA - JOURNAL_
SCHEMA - One line of the write journal (
JournalRecord). - MUTATION_
SCHEMA - REFILE_
SCHEMA - What a refile wrote (
RefileResult). - SORTING_
KEYS - Agenda sorting keys agd implements; habit-* and category-keep keep collection order.
- STATUS_
SCHEMA - The payload of
agd agenda --status --json: the status band’s counts (AgendaSummary) and the overdue rows (overdue_entries). - UNDO_
SCHEMA - What an undo wrote (
UndoResult). - VIEWS_
SCHEMA - The payload of
agd agenda --views --json. - VIEW_
BLOCK_ KINDS - VIEW_
GROUP_ BY - VIEW_
SECTION_ SCHEMA - One block of
agd agenda --view KEY --json.
Functions§
- agenda_
csv - Agenda rows in Emacs’s
org-batch-agenda-csvlayout: all eleven columns (category, head, type, todo, tags, date, time, extra, priority-l, priority-n, agenda-day) with Emacs’s values. As in Emacs, a field’s commas become semicolons and nothing is quoted.todaydecides which scheduled rows Emacs calls past-scheduled. - agenda_
row - One agenda row as org-agenda prints it:
category:, the time, the leader (Scheduled:,In 3 d.:), TODO, priority, headline and tags. - agenda_
text - The agenda from
fromtotoasorg-batch-agendaprints it with the time grid off:header, or Org’s span line such asWeek-agenda (W42):, then every day with its rows (org-agenda-show-all-dates). - agenda_
title - Headline text as org-agenda prints it: active timestamps in the title,
such as
Birthday <1993-10-16 Sat +1y>, are left out. - agent_
actor - The actor an MCP client’s writes are journaled under:
mcp:<client>, without the-mcp-clientsuffix Codex sends (codex-mcp-clientismcp:codex); other names are kept as sent. - agent_
name - The agent an actor names,
codexformcp:codex; None for a person. - apply_
import - Applies resolved editor settings over
base(the current config). The editor’s values replace the keys it reports; agd-only keys (editor, theme, agents, triage) keep theirbasevalues. On a first import (derive_roots) the roots become org-directory, or the directories the agenda entries live in; otherwisebase.rootsstay and entries outside them are left out with a diagnostic. - apply_
patches - capture_
prompts - The
%^{prompt}fields a template asks for, in order and without duplicates;%^gasks for “tags” and a bare%^tfor “date”. - check_
settings - checklist
- The checklist in
headline’s body, in order; empty when it has none. Lines in drawers and blocks are not list items. - default_
config_ path - find_
config_ sources - Finds the files to read under
paths. A file is taken as given; a directory is walked (respecting .gitignore, at most MAX_DEPTH levels, MAX_FILES files and MAX_BYTES each, skipping SKIPPED_DIRECTORIES), keeping Emacs Lisp files that mentionorg-, Lua files that mentionorgmodeand Org files with emacs-lisp source blocks. Both editors’ files under one path is an error: import one editor at a time. - format_
active_ timestamp - import_
root - The root an import writes for
agd -r DIR setup import: DIR made absolute (symlinks kept, as the editor’s paths keep them) with~for the home directory. DIR must exist, since agenda entries and capture files are placed under it. - item_
row - One row of a TODO list or tags match:
category:, TODO, priority, headline and tags. - items_
csv - A TODO-list or tags block’s rows in the same layout: type
tagsmatchfor atagsortags-todoblock andtodofor the others, with no date, time, extra or agenda day. - journal_
problems - One problem per interrupted write whose file changed since (see
WriteEngine::journal_conflicts), naming the command that settles it. - keyword_
color - A TODO keyword’s color name (COLOR_NAMES) wherever agd draws it: the
configured
todo_keyword_colorsentry, else green when done, orange for the first keyword of its sequence (TODO), and blue for the rest and for headings without a keyword. - keyword_
colors - Every TODO keyword the agenda files use, with its
keyword_color. A keyword two files order differently takes the first file’s color. - load_
settings - next_
repeat - The date a repeating timestamp moves on to when its task is marked done
on
today, as org-auto-repeat-maybe moves it:+1wa week from its date,++1wby weeks until after today,.+1wa week from today. - overdue_
entries - The overdue rows among
entries(a day’s agenda), one per item: missed deadlines first, most days late first, then past-scheduled reminders, oldest first. Rows of equal lateness keep the agenda’s order. Done items are never overdue. - parse_
agenda_ day - A day as org-agenda-start-day and
agd agenda --fromtake it: a date, an offset fromtodaysuch as -1d or +2w, or whatparse_date_inputreads, such as “fri”. - parse_
capture_ line - Reads one capture line:
due:DATEandon:DATE(a date asread_datereads it, which may continue into the next words:due:fri 9am,on:oct 30,due:2026-10-30 14:00 +1w -2d), tags and a priority innotation(tag_token,priority_token). Every other token stays in the title. - parse_
config_ file - Parses a config file without validating roots, so
agd setup importcan read one whose roots do not exist yet. - parse_
date_ change - A date answer for a field that may be cleared:
noneclears it, as in MCPplanand the TUI’s date prompts; anything else is a date (parse_date_input). - parse_
date_ input - Reads a whole date answer, relative to
today: one date asread_datereads it, with nothing left over. - parse_
timestamp - read_
date - The date at the start of
wordsand how many words it takes, as Org’s date prompt reads it: a day (an ISO date,today,tomorrow,., a weekday,+2d,-wed,14,oct 30) or a time alone (today at that time), then a time, a repeater (+1w) and a warning (-3d), each optional and taken when the next word is one. None when the first word starts no date. A word after the day that reads as a time but is not one (25:00,13pm) is an error, not the end of the date. - render_
capture_ template - render_
config - Writes
settingsas a config file: grouped, commented top-level keys, then[[capture]],[[view]]and[[agent_profile]]records. Values equal to the defaults are left out, so the file shows only what differs.