The agent surface

Reference for the jotura CLI agent commands: context, conventions and memory, with every flag and exit code, plus where the Jotura skills come from.

The commands on this page exist so that an automated agent can carry knowledge from one session to the next. They cover two mechanisms. One is a memory store of decisions, facts and follow-ups written as ordinary notes. The other is a shared conventions note that every agent runtime reads.

Everything here writes plain Markdown into your vault, with no hidden database or separate agent format behind it. If you open the vault in the desktop app you will see the memory notes and the conventions note exactly as the CLI wrote them. For the commands that read and edit notes generally, see Reading and searching and Editing notes.

The global flags work on every command on this page. --vault picks the vault for one invocation, --json asks for structured output where the command honors it, and -v or --verbose logs the resolved vault path to standard error. Install and setup covers them in full. The tables below list only the arguments that are specific to each command.

Where agent content lives in the vault

Path Written by What it holds
Agent Conventions.md jotura conventions (first use), then hand edits The shared, synced rules every agent runtime follows in this vault.
Memory/Decisions/, Memory/Facts/, Memory/Followups/ jotura memory log One note per memory. The root folder is configurable.

Because these are ordinary vault files, cloud sync carries them between your machines end to end encrypted, like any other note. Cloud sync is a paid feature, and pricing covers what it costs. See Sync and shares for how the CLI relates to it.

The Jotura skills

The commands on this page are what an agent runs. The skills are what tell it when to run them. They live in the jotura-agents repository, and you install them the way you install any other skill. In Claude Code, run these two commands inside a session:

/plugin marketplace add adamrichardson14/jotura-agents
/plugin install jotura@jotura-agents

For Codex, Gemini CLI, GitHub Copilot, OpenCode or any other agent, copy the SKILL.md files and their references from that repository into wherever the agent loads skills or instructions. Jotura never writes to an agent’s config directory, so your instruction files stay exactly as you wrote them.

jotura context

context is the single call that makes a session memory-aware. It prints a context pack containing the conventions note, any open follow-ups and a set of relevant memories.

jotura context "rewrite the sync retry backoff"
Argument or flag Type Default What it does
[TASK] string none Task description to recall memories against. Omit it for a session digest.
--limit <LIMIT> number 8 Maximum memories to include. This applies only to the task form.

The two forms fill memories differently. With a task, the memories field is a full recall against that task with stale entries excluded, capped by --limit. Without a task, memories lists every non-closed memory marked importance: high, and that list is unbounded because --limit does not apply to it.

context respects --json. The JSON shape is:

{
  "conventions": "<full text of Agent Conventions.md>",
  "openFollowups": [ { "path": "…", "project": "…", "date": "…" } ],
  "memories": []
}

With a task, each entry in memories carries the full recall shape of path, kind, status, importance, project, date, occurred, snippet and score. Without a task, entries carry only path, kind, project and date. If the conventions note does not exist, conventions is an empty string. context does not create the note; only jotura conventions does that.

The text form prints named sections:

=== Conventions ===
…
=== Relevant memory ===
- path (kind)
=== Open follow-ups ===
- path

Empty sections are omitted. To have Claude Code start every session with this pack, add a SessionStart hook that runs jotura context to your own Claude Code settings. Jotura leaves that file alone, so the hook is yours to add, change or remove.

jotura conventions

conventions prints the Agent Conventions note and creates it from a bundled seed the first time you run it.

jotura conventions

The note lives at the fixed vault path Agent Conventions.md, at the vault root, with a space in the filename. It has no command-specific arguments and it respects --json.

{ "path": "Agent Conventions.md", "hash": "…", "body": "…", "created": false }

Note the shape difference against jotura read: the JSON carries body with the frontmatter stripped and there is no frontmatter field. The text form prints the whole file including frontmatter.

The note is an ordinary vault note. It is user-visible, it syncs, every agent runtime shares it, and no installer touches it. To change it, edit it with the normal editing commands. conventions is only the read and bootstrap half of the loop.

jotura memory

Memory records decisions, work-world facts and follow-ups. The schema deliberately has no field for personal traits, and the source says so directly: memory exists to record the work, and describing the person doing it falls outside that job.

Notes are laid out as <root>/<Decisions|Facts|Followups>/<YYYY-MM-DD> <slug>.md, where <root> defaults to Memory. The slug is lowercased and reduced to ASCII alphanumerics, with spaces, hyphens and underscores collapsed to single hyphens and then trimmed.

A note counts as memory if its vault path starts with Memory/ or contains /Memory/. That is what lets you keep two entirely separate roots, for example Whitbread/Memory/ and Personal/Memory/, without them bleeding into each other.

Decisions and facts start with status: active. Follow-ups start with status: open. The closed statuses are superseded, reversed and resolved. A memory is considered stale when it is not closed and its occurred frontmatter key is more than 180 days old, falling back to date when occurred is absent. Staleness comes from those frontmatter keys and ignores the file’s modification time, so editing the body later leaves a stale memory stale, while editing occurred refreshes it.

memory log

jotura memory log --kind decision --title "Keep tantivy for body search" --project jotura --importance high
Flag Type Default What it does
--kind <KIND> string required decision, fact or followup, case-insensitive. Anything else exits 9. Sets both the subfolder and the initial status.
--title <TITLE> string required Becomes the H1 and the filename slug. A title that slugifies to nothing, such as pure punctuation, exits 9.
--project <PROJECT> string none Written as a project frontmatter key. recall and list filter on it.
--importance <IMPORTANCE> high or normal normal Ranking weight. Any other value exits 9.
--tags <TAGS> comma-separated string none Split on commas, each value trimmed, written as an inline flow sequence such as tags: [a, b].
--root <ROOT> string Memory The memory root folder. Use it to keep work and personal memory apart.
--body <BODY> string or - see below The note body. - reads from stdin.
--occurred <DATE> YYYY-MM-DD none When the memory became true, if that is not today. Anything not in YYYY-MM-DD form exits 9.

The frontmatter block log writes carries four keys always, in this order, plus two more when you supply them:

Key Written when Value
kind always decision, fact or followup.
status always active for decisions and facts, open for follow-ups.
importance always high or normal.
date always The date it was logged, as YYYY-MM-DD.
occurred with --occurred When it became true in the world. Every staleness rule on this page reads this key, falling back to date when it is absent, so memories written before the key existed keep working.
project with --project The project string verbatim.
tags with --tags An inline flow sequence such as tags: [a, b].

If you omit --body, the result depends on the kind. A decision gets a section template with ## Context, ## Options considered, ## Decision & why and ## Outcome headings. A fact or followup gets an empty body. Passing - reads the body from standard input, which is the natural form when a script is generating it:

printf 'Chase the Play review on Monday.\n' | jotura memory log --kind followup --title "Chase Play Store review" --body -

Marking a memory --importance high has two effects: it boosts its recall score, and it makes the note appear in the bare jotura context digest.

The command always emits JSON, whether or not you pass --json:

{ "path": "Memory/Decisions/2026-08-30 keep-tantivy-for-body-search.md",
  "hash": "…", "kind": "decision",
  "similar": [ { "path": "…", "snippet": "…" } ] }

The similar array is a duplicate warning. Before writing, log recalls the title against the same kind with a limit of 5 and returns whatever it finds, on the expectation that you will supersede an existing memory rather than log a near-duplicate. It is advisory only and never blocks the write.

The same-day overwrite gotcha. The filename is derived from the date and the title slug, and the write is unconditional with no expected hash. Logging the same title twice on the same day therefore replaces the first note instead of creating a second one. The similar array is the only guard against this, and it does not stop anything. To record a different memory on the same day, give it a different title.

memory recall

jotura memory recall "sync retry backoff" --kind decision --limit 5 --json
Argument or flag Type Default What it does
<QUERY> string required Free-text query. See the ranking notes below.
--kind <KIND> string none Restrict to decision, fact or followup. An invalid kind exits 9.
--project <PROJECT> string none Exact match on the project frontmatter key.
--limit <LIMIT> number 10 Maximum results returned.
--include-stale boolean false Include superseded, resolved and stale memories. Without it they are filtered out entirely.
--mode <MODE> keyword, semantic or smart smart How candidates are found. An invalid mode exits 9.

recall respects --json. The JSON is an array of objects with path, kind, status, importance, project, date, occurred, snippet and score. The text form is tab-separated: path, kind, status, snippet.

Modes. smart, the default, runs a semantic pass and a keyword pass and fuses them by reciprocal rank, weighting the semantic side twice as heavily because a task description rarely repeats a stored note word for word. semantic uses the vector index alone. keyword is full-text only.

Unlike jotura search, the semantic-backed modes never exit 13. Recall is a mandatory step in the agent loop, so when the vault has no usable vector index it quietly falls back to keyword rather than failing. The semantic pass also scans at full depth and filters to memory paths before applying any limit: memory is typically a low single-digit percentage of a vault, so a vault-wide top-K returns no memory notes at all.

The keyword pass. The underlying search engine ANDs its terms together, which is the wrong behavior for recall. So the keyword pass runs the full query and then each significant term on its own, where a significant term is 4 or more alphanumeric characters. Terms are deduplicated, and at most 7 of them run beside the full query, each with an internal limit of 200 results. The results are merged by path and their scores are summed, so a note that matches more of the query ranks higher than one matching a single word.

Multipliers. Recall keeps two sets of multipliers because the two score scales differ. Keyword-only results span hundreds, where a 1.5x boost is a nudge; fused rank scores put adjacent results about 1.6% apart, where the same multiplier would outrank relevance outright.

Condition Keyword-only Semantic or fused
importance: high 1.5 1.05
Stale, meaning open or active and an occurred more than 180 days old 0.5 0.95
Closed, meaning superseded, reversed or resolved 0.25 0.85

Any result whose path is not a memory path is discarded. The vector index is built by the desktop app, so a memory written seconds ago is not semantically searchable until the app next indexes it. smart covers that gap because its keyword pass always runs. For general semantic search over the rest of the vault, see Reading and searching.

memory list

jotura memory list --kind followup --status open --json
Flag Type Default What it does
--kind <KIND> string none Filter by kind. An invalid kind exits 9.
--status <STATUS> string none Exact match on the status key.
--project <PROJECT> string none Exact match on the project key.
--stale boolean false Show only active or open memories whose occurred (or date, when occurred is absent) is more than 180 days old. Closed statuses are excluded entirely.

list respects --json. The JSON array entries carry path, kind, status, importance, project, date and occurred, with no snippet and no score. Text output is tab-separated: path, kind, status.

Two behaviors differ from recall. First, list walks the note tree directly instead of searching, and it skips any note whose frontmatter has no kind. Second, --status is not validated while --kind is. An invalid --kind exits 9, but an invalid --status is accepted and matches nothing, returning an empty list.

memory supersede

jotura memory supersede "Memory/Decisions/2026-05-02 use-polling-for-sync.md" \
  --by "Memory/Decisions/2026-08-30 use-websockets-for-sync.md"
Argument or flag Type Default What it does
<OLD> string required The note being superseded. Its status becomes superseded and a superseded-by key is added.
--by <BY> string required The superseding note. A supersedes key is added to it.

Both notes are checked for existence before either one is modified. The command always emits JSON: { "superseded": "<old>", "by": "<new>" }.

The link itself is written as three separate hash-checked writes, for the status, the superseded-by key and the supersedes key. If another writer edits either note partway through the sequence, the pair can end up half-linked, so check both notes if a supersede reports a conflict.

memory resolve

jotura memory resolve "Memory/Followups/2026-08-12 chase-play-store-review.md"

It takes one positional <PATH> and sets status: resolved. It always emits JSON: { "resolved": "<path>" }.

Despite the help text saying “Mark a follow-up resolved”, the command is not restricted to follow-ups. It will set resolved on a decision or a fact just as readily, so check the kind before running it.

memory doctor

jotura memory doctor
jotura memory doctor --fix --json

memory doctor sweeps the memory store for the problems a long-lived store accumulates that no individual write can catch. It changes nothing unless you pass --fix.

Check Status when clean What it flags
memory-total always info How many memory notes were found.
memory-stale-actives pass Decisions and facts still active whose occurred is more than 180 days old. Confirm them or supersede them.
memory-open-followups pass Follow-ups still open after 60 days.
memory-orphan-supersedes pass A superseded-by edge pointing at a note that no longer exists.
memory-split-roots pass The bare default Memory/ root sitting beside a namespaced one. Several namespaced roots, such as work and personal, are a deliberate split and pass.
memory-missing-occurred pass Notes with no occurred key, which fall back to date. Reported as info, not a problem.

Output matches jotura doctor: a JSON array of { name, status, detail } with --json, otherwise tab-separated status, name, detail lines. status is one of pass, warn or info.

What --fix will and will not do. It backfills a missing occurred from date, and adds a memory-fix-applied check reporting the count. That is the only automatic edit, because it records what the file already implies. A stale active, a stalled follow-up and a broken supersede edge all need a judgement call, so they are reported and left alone, since silently rewriting someone’s memory is the failure the plain-note design exists to prevent.

Exit codes on this page

Code Symbol When you will see it here
0 none Normal completion.
2 HashConflict A hash-checked write lost a race, for example during a memory supersede sequence.
3 NotFound A memory path passed to supersede or resolve does not exist.
6 NoVault No vault resolved.
8 PermissionDenied A vault path that resolves outside the vault, including through a symlink or a Windows junction.
9 InvalidArgs An unknown memory kind, an importance value other than high or normal, or a path argument that is empty or contains . or .. components.

The code field in the error JSON carries the symbol, and that string is the stable thing for a caller to match on. Scripts should guard against one collision. Clap, the argument parser, defaults to exit code 2 for its own usage errors, such as a missing required argument or an unknown flag, and that number is also HashConflict. Usage errors print human-readable text on standard error rather than the JSON error envelope, so a caller that sees exit 2 should check whether stderr parses as JSON before concluding it hit a hash conflict. The full table is in Exit codes and JSON output.

A note on –json

The --json flag is not uniform across the CLI, and the agent commands sit on both sides of that split. context, conventions, memory recall and memory list branch on the flag. memory log, memory supersede and memory resolve emit JSON whether or not you pass it. Errors are always a single-line JSON object on standard error in every case.