IWE: a Markdown knowledge base you and your AI agent work on together
Memory serverOfficial IWE project
Created by IWE
Published in iwe-org/iwe. Publisher page
1,821 GitHub stars
What it is
Markdown knowledge graph — LSP for your editor, CLI + MCP memory for your AI agents
AutoGineer overview, from the brain’s own files
IWE provides a shared workspace for humans and AI agents to collaborate on project documentation, specs, and decision logs. By treating a folder of Markdown files as a structured knowledge graph, it allows agents to read, write, and organize information that persists across sessions. It functions as a memory server via the Model Context Protocol (MCP), enabling compatibility with various AI agents while remaining accessible to users through a native Mac app, CLI, or standard text editors like Neovim and VS Code.
How it works
Memory model: Not stated
AutoGineer overview, from the brain’s own files
IWE organizes notes as a collection of Markdown files within a project folder. It uses a CLI and an MCP server to expose these files to AI agents, allowing them to perform operations like searching, creating, and updating notes. The system supports features such as backlinks, link autocomplete, and graph visualization, ensuring that both the user and the AI agent interact with the same underlying document structure.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 4 |
| docs | docs | 70 |
| .githooks | folder | 1 |
| crates | folder | 381 |
| npm | folder | 15 |
Requirements
Runtimes: Rust
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 1 script. Review .githooks/pre-commit before opening the folder in Claude Code. No step below turns a hook on for you.
.githooks/pre-commit:runs on git pre-commit
- Not scanned by AutoGineerAutoGineer could not scan everything it runs. https://www.npmjs.com/package/@iwe-org/mcp
Created by IWE. Published in iwe-org/iwe. Licence: Apache License 2.0.
Usage
AutoGineer overview, from the brain’s own files
- Connect an AI agent to your project notes using the MCP server.
- Use the CLI to search, create, or reorganize notes from the terminal.
- Write and browse notes in the native Mac app while an agent works alongside you.
- Integrate note navigation and autocomplete into your preferred text editor.
- Maintain a persistent record of project decisions and lessons learned that both you and your agent can access.
- “Explore my IWE notes and summarize what's here.”
- The agent reads the Markdown files in the designated folder and provides a summary of the project context, decisions, or documentation found.
- “Save the decision we just made about the database schema.”
- The agent creates or updates a Markdown note in the IWE folder containing the details of the decision for future reference.
Documentation by IWE
IWE: a Markdown knowledge base you and your AI agent work on together
Your notes are your agent’s memory. IWE is where you work on them together.
iwe.md · Download for Mac · Docs · Reddit
Image: Crates.io Image: License Image: Documentation
Too many AI conversations start from zero. You explain your project, your decisions, your reasoning — then find yourself explaining it all again next session. Your own notes live somewhere else, out of the agent's reach.
Or worse, the agent “remembers” something in its own internal memory, but you can't easily see what it saved or correct what it got wrong. And that memory doesn't travel with you to the other agents you work with.
IWE gives you and your AI agents one shared place to work, right inside your project: a folder of connected Markdown documents — specs, plans, decisions, and more. Keep your own ideas there, ask your agent to save what you learn together, and return to that context as your work grows. You can read, edit, and organize those documents yourself — and they stay yours.
<br/> <p align="center"> <a href="https://www.youtube.com/watch?v=D58bPkOvbxE"> <img src="https://iwe.md/images/video-thumbnail.jpg" alt="Watch the IWE intro video on YouTube" width="600"> </a> </p> <br/>
Choose how you work
There are four ways to work with IWE. Mix and match them to fit your day:
| Tool | How you'll use it |
|---|---|
| iWe for Mac | Write and browse in a native app, and connect your existing Claude/Codex agent. |
| Your own editor | Stay in Neovim, Helix, VS Code, or Zed, with note navigation, backlinks, and autocomplete. |
| CLI | Search and manage your notes from the terminal or scripts. |
| Claude/Codex | Let your agent read, write, and organize memory while you work through conversation. |
All four work with the same Markdown documents. Draft a spec in your editor, develop it with Claude/Codex, browse it in the iWe app, or search it from the CLI. Use any combination you like; each tool also works independently.
Bring your own AI. Neither the IWE CLI nor the iWe Mac app includes an AI model. Both work on their own as tools for your notes. When you want AI help, use your existing Claude/Codex agent — in terminal or directly in the Mac app.
<a href="https://iwe.md/?ref=readme-shot"><img src="https://iwe.md/images/app/GH-screen-1.1.png" alt="iWe for Mac showing project plans and connected documents" width="820"></a>
<p> <a href="https://iwe.md/?ref=readme-shot"><img src="https://iwe.md/images/app/GH-screen-2.png" alt="Bug tracking with charts" width="32%"></a> <a href="https://iwe.md/?ref=readme-shot"><img src="https://iwe.md/images/app/GH-screen-3.png" alt="Technical documents with code" width="32%"></a> <a href="https://iwe.md/?ref=readme-shot"><img src="https://iwe.md/images/app/GH-screen-4.png" alt="Release documents" width="32%"></a> </p>
Plans · Charts · Technical documents · Releases — click a screenshot to see the iWe app in action on iwe.md.
Why IWE
Shared context. Your agent can use the same notes you write for yourself. Notes link to each other, and one note can belong to several topics, so you can follow an idea wherever it connects.
Memory you can read. Decisions and lessons saved by your agent are ordinary notes you can browse, correct, or delete.
Your notes stay yours. Everything is Markdown in a folder on your machine. Back it up, sync it, or keep it in git. You don't need an IWE cloud account to keep your notes; connecting an AI agent involves that agent's own setup and account requirements.
iWe for Mac
If you'd like a dedicated space for your notes, use the native Mac app. Open a folder of notes and you get a fast, keyboard-friendly space to write and browse. You can also connect your existing Claude/Codex agent to work alongside you.
Works with your Claude/Codex agent. Connect the agent you already use, ask questions about your notes, and have it draft or reorganize them. Watch its edits appear in the document as it works.
Room to experiment. Each agent run is one undo step — try an idea, and roll it back if you don't like it.
Follow your ideas. Jump between related notes, search in a few keystrokes, and view Mermaid diagrams right in your documents. Your editor and other tools can keep working on the same files.
Open a notes folder and start with a note about something you're working on. Once Claude/Codex is connected, try asking it to summarize that note or help you develop an idea.
Your Own Editor
Keep writing in Neovim, Helix, VS Code, or Zed. IWE adds link autocomplete, backlinks, search, and safe renaming across your notes.
Follow the guide for your editor, open your notes folder, and try linking two notes with autocomplete. You can use IWE for your own writing without connecting an AI agent.
CLI
Prefer the terminal? Use IWE to search, create, and reorganize notes, or work it into your scripts. The CLI has no AI built in: these commands work on their own. Your existing Claude/Codex agent can use IWE's tools too.
Install with Homebrew, or choose another option from the installation guide:
brew install iwe-org/iwe/iweThen run these commands inside your notes folder:
iwe init
iwe find
iwe find --lexical "project decisions"iwe find lists your notes; --lexical searches their content. Explore more in the CLI reference and workflow examples.
Claude/Codex
You can use IWE entirely through your agent. Ask Claude/Codex to save decisions, look up past work, and organize what you've learned. The agent works with your notes through IWE, and you can open those files yourself whenever you want.
IWE isn't tied to one AI. The same notes work with Claude Code, Claude Desktop, Codex, Gemini, Cursor, and any tool that supports the Model Context Protocol.
Give Claude/Codex a memory — for Claude Code, install the plugin, then run /iwe:init in any project you want remembered. For Codex, follow the agent connection steps below.
/plugin marketplace add iwe-org/skills
/plugin install iwe@iwe-org
/iwe:initThen ask it to save a decision from your current work, and open the note it creates in your project.
Connect another agent — with Homebrew installed, install IWE:
brew install iwe-org/iwe/iweRun iwe init in your notes folder, then follow the agent connection guide to add the iwec MCP server with that folder as its working directory. For other installation options, see the installation guide.
Once connected, ask your agent: “Explore my IWE notes and summarize what's here.”
Or let the agent set itself up — paste this into any agent that can run commands:
Set up IWE for my notes: install it (brew install iwe-org/iwe/iwe,
npm install -g @iwe-org/iwe, or cargo install iwe iwes iwec), run `iwe init`
in my notes directory, then add the `iwec` MCP server with its working
directory set to that folder.
Docs: https://iwe.md/docs/agentic/Learn More
Getting Started — install and set up
Working with AI — connect your agents
Usage Guide — everyday workflows
Examples — how people use IWE
CLI Reference and MCP Server — for the technically curious
Ready-made workspaces: dev-workspace — project memory for a coding agent · marketing-workspace — campaign memory for a marketing agent.
Get Involved
Trying IWE with your own notes? Tell us what felt useful and where you got stuck. Questions, rough edges, and examples of how you use it are all welcome in Discussions.
IWE is open source. You can also report a bug or help improve the documentation.
Community: Twitter/X · Reddit · Discussions
Building on IWE? The CLI and MCP server are the supported ways to integrate today. Tell us what you depend on so we know what not to break.
License
docs/query-language.md
Query Language
Experimental. The query language is under active development. Syntax, operators, defaults, and CLI flag names may change without warning. It is not exposed as a library API and has no stable on-disk format. The supported surfaces today are the CLI subcommands listed below and the MCP
iwe_querytool, which accepts an operation document verbatim; build automation against them at your own risk.
IWE has a YAML-based, MongoDB-style query language for selecting, shaping, and mutating documents in a workspace by their frontmatter, graph relationships, and content. It is reachable through the CLI subcommands iwe find, iwe count, iwe update, and iwe delete (plus the read-only selectors on iwe retrieve, iwe tree, and iwe export), and through the MCP iwe_query tool.
This page is the reference for the language. The Query Language Specification is the formal source of truth for the document-level core (operations, filter, graph operators, projection, output formats) and carries the full grammar, block productions included; the block surface's semantics are documented here.
Operations
| Operation | CLI subcommand | What it does |
|---|---|---|
find | iwe find | Returns matched documents (subject to projection). |
count | iwe count | Returns the integer count of matched documents. |
update | iwe update | Mutates frontmatter and blocks on each matched document. |
delete | iwe delete | Removes each matched document and cleans up references. |
update and delete require an explicit filter — passing {} on purpose is the only way to operate on the whole corpus.
Filter syntax
A filter document is YAML. A document matches when every top-level key matches; multiple top-level keys are AND-composed.
Bare equality
status: draftMatches documents whose status field equals draft. For arrays, a bare scalar tests membership: tags: rust matches when rust is in the tags array. Cross-type comparisons are always false (no implicit coercion: priority: "3" does not match an integer field).
Operator expressions
A mapping with $-prefixed keys is an operator expression:
priority: { $gt: 3 }
priority: { $gte: 3, $lte: 7 } # closed range [3, 7]
status: { $in: [draft, review] }
status: { $nin: [archived, deleted] }
reviewed: { $exists: true }
tags: { $all: [rust, async] }
tags: { $size: 0 } # exact array length
tags: { $size: { $gte: 3 } } # $size also takes count comparisonsOperators in one expression are ANDed together. User frontmatter fields cannot start with $, so an operator and a field name never collide.
Logical composition
$and:
- status: draft
- priority: { $gt: 3 }
$or:
- status: draft
- status: review
$nor:
- status: archived
- status: deletedTop-level AND is implicit. Use explicit $and when you need the same field name on multiple sub-clauses (a YAML mapping cannot have duplicate keys).
Nested fields
Nested fields can be addressed via nested mapping or dotted shorthand; both forms are equivalent:
author.name: alice
author:
name: aliceField names that themselves contain a literal . are not addressable in v1 — the engine always splits paths on ..
Graph operators
Graph operators live alongside frontmatter predicates inside the same filter. They walk inclusion edges (block-reference inclusion links) or reference edges (inline links).
$key — identity
$key: notes/foo # implicit $eq
$key: { $in: [a, b, c] } # any of these
$key: { $nin: [drafts/scratch, drafts/temp] } # none of these
$key: { $startsWith: notes/ } # everything under notes/Every form matches the key as stored, with the document extension already stripped. $startsWith is a plain string prefix, not a path-segment match, so notes also matches notes-archive/gamma and the hub note notes itself; write the trailing separator when you mean the directory alone. The prefix is case-sensitive, unlike wiki link resolution.
Relational operators
| Operator | Reads as | Edge type | Walk parameters |
|---|---|---|---|
$includes | this doc includes an anchor | inclusion | maxDepth, minDepth |
$includedBy | this doc is included by an anchor | inclusion | maxDepth, minDepth |
$references | this doc references an anchor | reference | maxDistance, minDistance |
$referencedBy | this doc is referenced by an anchor | reference | maxDistance, minDistance |
Each takes either a scalar key (shorthand for direct edges) or a mapping with an optional match, walk parameters, and an optional $size:
# Direct edges only — scalar shorthand for { match: { $key: projects/alpha } }
$includedBy: projects/alpha
# Walk inclusion edges from a single anchor, bounded
$includedBy: { match: { $key: projects/alpha }, maxDepth: 5 }
# Anchor by frontmatter predicate (every active project)
$includedBy:
match:
type: project
status: active
maxDepth: 5
# Anchor set is two named documents
$includedBy:
match:
$key: { $in: [projects/alpha, projects/beta] }
maxDepth: 5
# Range bounds — documents 2 to 3 hops from archive/index
$referencedBy: { match: { $key: archive/index }, minDistance: 2, maxDistance: 3 }In the full mapping form, omitting maxDepth / maxDistance means direct edges (depth 1). Set maxDepth: 0 / maxDistance: 0 for an unbounded walk that reaches every transitively-related document. Walks are BFS and de-duplicate via a visited set, so cycles terminate.
$includedBy: { match: { $key: projects/alpha } } # direct children
$includedBy: { match: { $key: projects/alpha }, maxDepth: 0 } # whole treematch is optional and defaults to {} (any document); the empty mapping $includedBy: {} is still a parse error.
A relational operator never matches a document in its own anchor set. To include the anchor, OR it in:
$or:
- $key: projects/alpha
- $includedBy: { match: { $key: projects/alpha }, maxDepth: 0 }Cardinality — $size
By default a relational operator holds when at least one document stands in the relation. Add $size to test the count of related documents instead. $size takes a non-negative integer (an $eq shorthand) or a mapping of count comparisons ($eq, $ne, $gt, $gte, $lt, $lte); multiple comparisons AND together. The count is over the distinct documents in the walk's [min, max] band, matching match, always excluding the document itself.
$includedBy: { $size: 0 } # roots — nothing includes them
$includes: { $size: 0 } # leaves — include nothing
$referencedBy: { $size: 0 } # orphans — no inbound links
$referencedBy: { $size: { $gte: 5 } } # hubs — 5+ direct referrers
$includedBy: { $size: { $gte: 10 }, maxDepth: 0 } # 10+ transitive ancestors
$includedBy: { match: { type: project, status: active },
$size: { $gte: 2 }, maxDepth: 3 } # counted, filtered, boundedBecause the default bounds count direct edges, $size: 0 is the same whether bounded or unbounded: zero direct edges means zero at every depth.
$content — content membership
$content lifts a block predicate into the filter: it matches documents containing at least one block satisfying the predicate. $content: {} matches any document with at least one block.
# Drafts, or documents whose content mentions TODO
filter:
$or:
- status: draft
- $content: { $text: "TODO" }# Documents under a project that LACK a Status section — absence is
# a quantifier over the whole block set, expressible only in filter
filter:
$includedBy: { match: { $key: projects/alpha }, maxDepth: 3 }
$nor:
- $content: { $header: Status }Like $key, it is a noun operator testing the document itself, so it composes with every other filter clause under $and / $or / $nor and inside the match of any relational operator. Like any filter clause, $content decides membership only — it selects nothing; which blocks an operation reads or mutates is designated at the projection or update site.
Search (find only)
search is a top-level clause of a find operation, beside filter. It selects documents by relevance to a full-text query and supplies the default ordering. Search leads, filter refines:
search:
lexical: "broken links cleanup"
filter: { type: note }
limit: 5lexical: <string>— BM25 full-text over title + body.fuzzy: <string>— skim subsequence match over title + key. Both present → RRF fusion, exactly asiwe find --fuzzy --lexical. At least one is required;search: {}is a parse-time error, as is any key other thanlexical/fuzzy.Search selects and orders. A document is in the result iff it matches the search and passes the
filter(search matches ∩ filter matches), ordered by relevance, ties by key ascending. Candidates with no BM25 hit / no skim score are dropped — a text query is a filter here, not a sort variant. The two selections are joint; no evaluation order is implied, andlimit: 5always means the 5 best documents that pass the filter (never the filtered survivors of a globally-truncated top list).search+sortis legal. Search contributes membership and the default ordering; an explicitsortoverrides the ordering only. "The 5 newest documents matching Q" issearch+sort: { modified_at: -1 }+limit: 5.Corpus-global scores. The BM25 index is fit to the whole workspace; IDF is not re-fit per filter (standard Lucene-style behavior). Searching within a filter is therefore not identical to searching a corpus of only the filtered documents.
No searchable terms. A
lexicalquery that stems away to nothing (stop words only) matches nothing; the result carries a structured warning so callers see why it is empty.findonly — notcount/update/delete— and it requires the search-indexed graph. Theiwe find --fuzzy/--lexicalflags and the MCPiwe_querysearchclause are the two surfaces.
Projection (find only)
project shapes each result to exactly the listed fields; addFields keeps the default fields and adds to them. The two are mutually exclusive. Each entry maps an output name to a source: 1, true, and YAML null project the frontmatter field of the same name, a dotted path projects a nested frontmatter field, and a $-selector projects a system field.
project:
title: 1
priority: meta.priority
key: $key
body: $content
parents: $includedByThe $-selectors: $key, $title, $titleSlug, $content, $frontmatter, $includes, $includedBy, $references, $referencedBy.
Projection can also address blocks inside each matched document — narrowed bodies, located blocks, grep lines. See Block projection.
Sort and limit
sort: { modified_at: -1 } # 1 = ascending, -1 = descending
limit: 100 # 0 = no limitsort: { title: { order: 1, collation: binary } } # codepoint order for this field
sort: { title: { order: -1, collation: sv-SE } } # Swedish rules for this fieldv1 accepts exactly one sort key. Ties (and the no-sort case) are broken by document key in ascending lexicographic order. When run through the iwe CLI, the LSP server or the MCP server, text values sort with the collation rules of the workspace locale: case and accents only break ties, Note 2 comes before Note 10, and emoji are ignored unless the remaining text is identical. The engine itself defaults to codepoint order. A per-field collation of binary or a locale tag overrides whichever default applies.
Update operators
update:
$set:
reviewed: true
audited_at: 2026-04-26
"review.reviewer": alice
$unset:
draft_notes: ""$set adds the field if absent, replaces it otherwise. Mapping values replace wholesale; use dotted shorthand to write subset leaves without dropping siblings. $unset removes fields; values are ignored.
Block update operators — $replace, $replaceText, $insertBefore, $insertAfter, $append, $delete — live in the same update document as siblings of $set / $unset and combine freely with them. See Block update operators.
Field names the language cannot address
The engine preserves every frontmatter field a document carries, whatever its name starts with. One name shape cannot be addressed: $-prefixed names are the operator vocabulary, so a path segment beginning with $ — in a filter key, a sort key, a projection source, or a $set / $unset target, at any depth — is a parse-time error. Such a field is still stored, returned and validated; it just cannot be reached by a path.
Blocks
The language also addresses blocks — the structural nodes inside a document: headers, paragraphs, lists, list items, code blocks, quotes, tables, references, horizontal rules. There is no separate block-selection clause; blocks enter through three sites the operation document already owns:
| Site | Addition | Purpose |
|---|---|---|
filter | $content operator | Document membership by content — "documents containing a block matching P" |
project / addFields | { $content: P } narrowing, $blocks and $matches sources | Read a slice of a document: narrowed body, located blocks, grep lines |
update | Block operators ($replace, $replaceText, $insertBefore, $insertAfter, $append, $delete) | Mutate blocks, each operator carrying its own selection |
One grammar — the block predicate — is consumed at all three sites, and one convention governs every argument shape: $-prefixed keys select, bare keys configure. Inside an update operator's argument, the $-keys form the block predicate and the bare keys (from, to, content, expect) are the payload.
Because there is no block-selection clause, membership stays filter's alone: nothing outside filter adds or removes documents from the result. Deliberately out of scope: computed replacements (renumbering, case transforms), stateful walks, inline-element rewriting — the driving agent reads the selection, computes, and applies literal edits. The typical workflow is two operations sharing one predicate: locate with project: { hits: { $blocks: P } }, then mutate with an update operator carrying the same P.
Block model
Each block type has its own predicate operator. Node operators select the matching block alone; tree operators select the block together with everything below it. The own text column is what text predicates match against:
| Operator | Sort | Markdown construct | Own text |
|---|---|---|---|
$header | node | heading (#, ##, …); the blocks of its section are its children | the heading text |
$paragraph | node | paragraph | the paragraph text |
$list | tree | bullet or ordered list — an empty head whose children are the items | none |
$quote | tree | block quote — an empty head wrapping the quoted blocks | none |
$item | node | list item | the item's own line — a loose item's leading paragraph folds into it — excluding nested children |
$code | node | fenced code block | the code text, excluding the fence lines |
$table | node | table | the rows as rendered, one line per row |
$ref | node | block reference (inclusion link) | the authored (piped wikilink) text, when present; a title derived from the target is not own text |
$hr | node | horizontal rule | none |
$html | node | HTML block (<div>, <details>, <script>, a comment followed by text on its line, …), kept exactly as written | the HTML source |
The tree rooted at a header — the header together with every block below it — is a section: $header matches the heading line alone, $section selects the whole tree. Quotes and lists are containers with empty heads: the root carries no own text, only the wrapper, so their operators select the container with its contents, and $within peels the wrapper off. A container is a block that can carry children — a header, an item, a list, a quote; containers are the legal $append targets. HTML blocks never start or end sections; a wrapper such as <div>, blank line, markdown, blank line, </div> is three sibling blocks, and editing one of them does not keep the wrapper balanced. HTML comment lines are not blocks — each belongs to a block, see $comment.
Own text. Text predicates evaluate against a block's own text only, never descendant text. A header whose section contains TODO does not match $text: TODO — the paragraph carrying it does. To select a block because of its contents, use $contains. Own texts are pairwise disjoint, which the update semantics rely on.
Normalized form. Matching operates on the normalized rendering of the document — the same form iwe retrieve and every other read emits — at the byte level: inline markup keeps its markdown syntax (**bold** stays **bold**, never bold), and inline link URLs are part of own text, rendered relative to the containing document. Text copied from a read matches byte-for-byte in a later predicate or $replaceText anchor. In normalized form every block's own text is a single line, except code blocks, tables, and HTML blocks — one line per code line, table row, or HTML source line, with table cells padded to their column width.
Section path. Every block has a section path: the titles of its enclosing sections, ordered from the document root down, with top-level headers omitted — the value block reads emit as path. Path elements are plain text (inline markup stripped); a heading's own text keeps its markdown syntax, so for a heading carrying inline markup the two differ — echo own text, not path elements, into text predicates.
No block identity. No IDs are minted or written to files; addressing is by predicate against normalized text, guarded by expect. Two byte-identical sibling blocks under the same section path are indistinguishable — no predicate can select one and not the other; every mutation touches both or fails its expect. The escape hatch is the whole-body rewrite (iwe update -k KEY -c CONTENT).
Readable but not editable. The task-checkbox marker ([ ] / [x]) sits outside an item's own text: text predicates never see it, no predicate selects items by state, and $replaceText cannot toggle it — toggling remains the editor's code-action surface. Tables select, render, and travel as units — membership, reads, $delete, $replace, and the insertion anchors all work — but $replaceText selecting a table is a validation error; editing inside a table means replacing the table.
Block predicates
A block predicate is a YAML mapping of $-prefixed operators. The empty predicate {} matches every block; top-level keys AND together; unknown $-names and bare keys are parse-time errors.
| Operator | Matches |
|---|---|
$text: S | own text contains S, case-insensitively; $text: { $eq: S } matches the whole own text, also case-insensitively; HTML comments are not part of own text |
$matches: REGEX | own text matches the pattern (Rust regex, case-sensitive — use an inline (?i) flag; no backreferences or lookaround); HTML comments are not part of own text |
$header, $paragraph, $item, $code, $table, $html | blocks of one type — the matching block alone, no implicit subtree; the argument is a nested predicate, and a scalar is exact-text shorthand ($header: Status) |
$ref | block references, selected by target ($ref: { $references: KEY }); the scalar shorthand is a parse-time error (a ref's rendered title is usually derived from its target), and text predicates match the authored (piped) link text alone |
$hr | horizontal rules; no own text — the scalar shorthand and direct text predicates in the argument are parse-time errors |
$section: T | a header matching T together with everything below it; scalar T is exact-text shorthand for the root header |
$quote: P, $list: P | a quote / a list together with its contents; the root has no own text, so scalars and direct text predicates in the argument are parse-time errors — scope with $within or $contains |
$within: T | blocks inside the selection — a section's body, a quote's content, at any depth; scalar T names a section; a mapping argument must select content: {}, or a predicate containing $section / $quote / $list |
$contains: P | blocks with a descendant matching P, at any depth |
$references: KEY | blocks whose own content links to KEY — a ref targeting KEY, or inline text linking to it |
$comment: P | blocks with an HTML comment matching P — an inline comment in the block's text, or a <!-- --> line owned by the block: a comment line belongs to the block before it in the same container (a list, not its last item; a quote, not its last paragraph), or to the container itself when it comes first (a header for the first line of its section, an item for its first nested line); comment lines before a document's first block belong to that block, and a document with nothing but comment lines has no block to own them. {} matches every commented block, a scalar is exact-text shorthand ($comment: TODO), and the argument accepts only $text, $matches, $and, $or, $nor — anything else is a parse-time error |
$and, $or, $nor | logical composition, as in filters |
$header: {} # every header: the outline
$section: Unreleased # one section, header included
$within: Goals # inside Goals, header excluded
$within: { $section: { $matches: "^Q[0-9]" } } # inside any quarterly section
$paragraph: { $references: archive/old-plan } # paragraphs linking to a document
$header: { $matches: "(?i)todo" } # headers whose heading line says TODO
{ $header: {}, $contains: { $matches: "(?i)todo" } } # headers whose section holds a TODO
$within: { $quote: {} } # quoted content, wrapper peeled offDeeper scoping is recursion, not extra syntax — each element of a section path becomes one $section predicate scoped by $within to the elements before it:
# Inside Q3, itself inside Goals (matches Goals > 2026 > Q3, not Q3 > Goals)
$within: { $section: { $text: { $eq: Q3 }, $within: Goals } }Conditions inside a type operator's argument and conditions beside it conjoin identically: { $header: { $matches: "^Q" } } and { $header: {}, $matches: "^Q" } match the same blocks. Type follows the block model, not the visual layout — a loose item's leading paragraph folds into the item, so its text matches $item, not $paragraph. When unsure of a block's type, locate with an untyped predicate ($blocks: { $text: alpha }) and read the entry's type; the type operators narrow a selection, they are not for asserting what kind of block some text lives in.
$within: {} is the interior of the document itself — every block below the top level; a top-level block is therefore $nor: [{ $within: {} }].
Selection semantics
A block predicate is evaluated against every block in the document, at every depth, always. Node operators select each matching block alone; tree operators ($section, $quote, $list) select the matching root together with every block below it. The result is a forest: the selected blocks arranged by ancestry — a selected block's parent is its nearest selected ancestor — in document order. Union over multiple matches is automatic: when a selector matches several headers, $within is the union of all their interiors.
For sections, three selections line up on one predicate:
| Selection | Predicate | Denotes |
|---|---|---|
| the header alone | $header: Goals | one node — the heading line |
| the section — header and contents | $section: Goals | the full tree |
| the contents only | $within: Goals | the tree minus its root |
Each consumer takes from the forest what it needs: membership ($content in filter) tests that it is non-empty; $content in projection renders it; $blocks flattens it to data; the unit update operators act on its roots; $within drops them.
Block projection
Three projection sources consume a block predicate. Each takes the predicate in an options mapping; the bare form is the empty-predicate shorthand.
| Source | Projects |
|---|---|
{ $content: P } | the document body narrowed to the selected blocks, rendered at their original depth — a string |
$blocks / { $blocks: P } | one entry per selected block: type, path (enclosing section titles), text (own text) |
{ $matches: REGEX } | grep — one entry per matching line: path plus the full line as text |
project:
key: $key
toc: { $content: { $header: {} } } # headers-only rendering
notes: { $content: { $section: Unreleased } } # one section, as it appears
hits: { $blocks: { $within: Goals, $text: "Q3" } } # located blocks as data
found: { $matches: "(?i)todo|fixme" } # grep linesBare $content is { $content: {} } — every block, the full body. A matched document in which no block matches projects "". Multiple narrowed entries may coexist in one projection, each with its own predicate.
$content narrowing renders each selected block's own content in document order, at its original depth, normalized. Nothing renders that was not selected; subtrees appear when the predicate selects them ($section, $within), never by implication. Re-assembly is structural: selected items whose list is not selected re-wrap into a list of the original type, and selected blocks inside a selected quote render inside the quote wrapper ($within: { $quote: {} } renders them without it).
$blocks entries carry type (the operator name without the $), path, and text (the own text). A header's entry carries its heading text, not its section — use $content narrowing to read bodies. Every entry carries text — "" for blocks without own text; a ref entry additionally carries target, its text holding the authored (piped) link text or "" when there is none. The entry count is the number $replaceText's expect pins; a tree selection lists every block of its trees flattened, so it over-counts unit-operator targets — list those by selecting the roots as nodes (a $section: P previews as $header: P).
$matches applies the pattern to the own text of every block in scope and reports one entry per matching line, in document order — the full line, not the matched substring, and never the whole block. The argument is a regex scalar, or a mapping combining the pattern with a scope — bare pattern is the payload, $-keys select: { $matches: { pattern: "(?i)todo", $within: Goals } }.
In find markdown output, $blocks and $matches entries render one grep line each — key › section path › text — under the result's index line; a narrowed $content field switches to the fenced-block form like bare $content. See iwe find.
Top-level form
project (not addFields) also accepts a block predicate directly in place of the field map — any $-key at the top level selects this reading, and the result carries the document's key plus a content field with the narrowed body:
project: { $header: {} } # the headers-only form of each document
# exactly the same as
project:
key: $key
content: { $content: { $header: {} } }Top-level keys conjoin as in any block predicate — project: { $header: {}, $within: Usage } is the table of contents of one section. Mixing bare and $-keys is a parse-time error, project: {} keeps its v1 meaning (an explicit empty projection — each result carries no fields), and the projection sources are not predicate operators — project: { $blocks: P } is an unknown-operator error (the data reads keep their field names), and a top-level $matches reads as the block-predicate operator, never the grep source.
Block update operators
Block operators live directly in the update document, as siblings of $set and $unset. Every update operator is an (address, payload) pair: $set addresses by field path, the block operators address by block predicate. An operator's argument is one flat mapping — the $-keys form the predicate, the bare keys are the payload and the optional expect guard. The bare-key set per operator is closed; unknown bare keys are parse-time errors. Each operator appears at most once per update document; applying one operator to several independent selections requires several operations.
| Operator | Payload | Effect per target |
|---|---|---|
$replace | content (markdown) | Replace the target as selected — a tree target wholesale, a node target as its own line(s); on a header node, a heading payload retitles the section |
$replaceText | from (optional), to | With from: replace that substring of the block's own text with to. Without from: to replaces the entire own text |
$insertBefore | content (markdown) | Insert sibling content before the target |
$insertAfter | content (markdown) | Insert sibling content after the target — after a header node: directly below the heading line, the first position of its section; after a $section: below the whole tree |
$append | content (markdown) | Append child content at the end of the block (containers only — a header: at the end of its section; an item: after its nested blocks; a list: after its last item; a quote: after its last block) |
$delete | — | Remove the target as selected — a $section with its contents; a bare $header dissolves into its enclosing section |
filter:
$content: { $text: "Q3" }
update:
$set: { reviewed: true }
$replaceText:
$within: Goals
$text: "Q3 Milestones"
from: "Q3 Milestones"
to: "Q3 2026 Milestones"
expect: 1
$delete:
$paragraph: { $references: archive/old-plan }An argument with no $-keys carries the empty predicate and selects all blocks: $delete: {} clears the document body — title header included — bounded by the required filter and guardable with expect.
Targets and coalescing
The unit operators ($replace, $insertBefore, $insertAfter, $append, $delete) act on targets: the selection's forest roots, each taken as selected — a tree root ($section, $quote, $list) is the whole tree, a node root is the block alone, never its children. This is the same rule projection follows: $content: { $header: Goals } renders one heading line, and $delete: { $header: Goals } removes one heading line — the predicate means the same thing at every site.
Targets coalesce by extent: a target lying inside another target's extent is absorbed by the outer one — selecting a section and a paragraph within it yields one target, the section, so $delete: { $section: {} } deletes each outermost section once rather than tripping over its own descendants. Node targets never absorb one another: own texts are pairwise disjoint, so two node targets always name disjoint lines. Operators apply once per target, in document order.
Insertions are anchored: inserted content stays adjacent to its target, so when $insertAfter on a block and $insertBefore on its following sibling name the same gap, each lands hugging its anchor.
$replaceText operates on own text and applies to every selected block, un-coalesced — own texts are pairwise disjoint, so nested selections cannot conflict. A selected header's $replaceText edits the heading text and nothing below it; a selected ref's edits its authored link text, and the target key never changes. from is optional. When present it must occur exactly once in each selected block's own text, matched byte-exact against the normalized form — case-sensitive, unlike $text; the workflow is to echo text read from $blocks output. When omitted, to replaces the block's entire own text — renaming a header is { $header: Goals, to: Aims }; pair the from-less form with a text-bearing predicate or an expect guard, since it carries no proof the agent read what it overwrites. A selected block with no own text (a quote or list root, a horizontal rule, a ref without authored text) or a table is a validation error.
Payload markdown (content) is parsed and normalized on write. Heading levels inside a supplied fragment define nesting within the fragment only; the fragment's root level is set by its attachment point — child of the target for $append, sibling of the target for the insertions, the target's own position for $replace. At a list attachment point — sibling of an item, child of a list — the fragment must parse as a single list; its items attach, and the target keeps its own list type.
Headers: retitle, dissolve, remove, clear
Every unit edit splices the target's lines in the normalized rendering; the document re-parses, and normalization re-derives nesting and header levels from the result. Headers are where the node/tree distinction matters — the three selections become three different operations:
| Selection | $delete | $replace: { content: C } |
|---|---|---|
$header: Goals — the heading line | Dissolve: the heading line is removed; the former contents re-attach to the enclosing section and re-level | C splices at the heading line. A heading payload retitles — the former contents re-nest beneath it; any other payload dissolves the section around it |
$section: Goals — header and contents | Remove: the header and every block below it | C replaces the whole section |
$within: Goals — contents only | Clear: the header stays, its blocks go | C replaces each content target |
Against
# Roadmap
## Goals
Ship the editor integration
### Q3 Milestones
Deliver block operations spec$delete: { $header: Goals } yields
# Roadmap
Ship the editor integration
## Q3 Milestones
Deliver block operations spec— the paragraph re-attaches to Roadmap, and Q3 Milestones re-levels from ### to ##. Re-leveling ripples through the whole former subtree: after a header edit, expect level changes below the splice point. $replace: { $header: Goals, content: "## Aims" } retitles — the same document with ## Aims in place of ## Goals, contents intact; the payload's own level is advisory, since normalization re-levels to the attachment point. The destructive whole-section forms require naming the tree: $delete: { $section: Goals }.
The asymmetry of mistakes is deliberate: deleting a header node when the section was meant leaves visible, recoverable content behind; the reverse mistake under subtree semantics would silently destroy a section. The intuition is the text editor's — deleting a heading line does not delete the text under it.
$replace is per-target, never "set the body to C": against a body with three top-level blocks, $replace: { content: X } yields X three times. Whole-body replacement is the CLI body-overwrite mode; a single-target $replace is guarded with expect: 1.
Combining operators
$set, $unset, and block operators combine freely in one update document and apply atomically per matched document. Block operator applications must be pairwise disjoint by extent after coalescing: no two applications may touch the same block, or a tree target and anything within it — a $delete taking the Goals section while a $replaceText edits a paragraph inside it is a validation error listing the pair, nothing written. A $delete of the Goals header node beside the same $replaceText is legal: the heading line and the paragraph are disjoint extents.
Frontmatter operators apply to every filter-matched document regardless of block selections — membership is filter's alone. To restrict a combined operation to documents where the block edit lands, conjoin $content into the filter with the same predicate:
filter:
status: active
$content: { $text: "Q3" }
update:
$set: { reviewed: true }
$replaceText: { $text: "Q3", from: "Q3", to: "Q3 2026" }Drop the $content line and reviewed is stamped on every active document while the text edit lands only where Q3 occurs.
expect guards
Any block operator application may carry expect: N or expect: { min: M, max: N } as a bare key, asserting the number of applications it will make, counted across all mutated documents (post-limit):
The unit operators count targets — the coalesced roots: one section, however many blocks it holds, is one target, so
$delete: { $section: Goals, expect: 1 }passes.$replaceTextcounts selected blocks, un-coalesced, matching its per-block application.
update and delete operation documents additionally take a document-level expect — a top-level clause, sibling of filter / sort / limit — asserting the number of matched documents the operation will write. In a find or count operation a top-level expect is a parse-time error. The two levels are independent quantities: a document-level expect: 3 beside a block-level expect: { min: 3 } says three documents will be written with at least three blocks touched across them.
On violation the whole operation fails before writing anything; the error reports the actual count plus each target as key › section path › first line of own text (documents as key › title). expect: 1 is the single-target precision edit; expect: 0 is legal and asserts an empty selection.
# Bounded cleanup: delete completed task paragraphs, refusing a runaway match
filter: { type: meeting }
update:
$delete:
$paragraph: { $matches: "^DONE " }
expect: { max: 20 }Validation and atomicity
The operation validates fully before writing anything, anywhere.
Parse time: unknown operators; unknown bare keys in an operator argument; invalid payload types; regex that fails to compile; a $within argument that cannot select content; block operators in a find / count / delete operation.
Evaluation time, against the original normalized documents — every selection and anchor position is resolved before any edit applies — in order:
Type compatibility —
$appendon a non-container block;$replaceTextselecting a block with no own text or a table; acontentpayload that cannot attach at its site. The error lists every offending block.$replaceTextanchors — a givenfromabsent or occurring more than once in any selected block's own text.Disjointness — block-operator applications with overlapping extents. The error lists the pairs.
expect— any violated guard, block-level or document-level.
Any failure aborts the whole operation: frontmatter operators write nothing if a block operator fails validation, and no document is written if any document fails. Per document, frontmatter and block edits commit as one atomic rewrite.
Strict mode
The expect guards are optional in the language: a human at a terminal, with git behind the store, should not pay ceremony for a bulk edit they can see. Agents should. Strictness is therefore a property of the surface, not the grammar — the same operation document is valid everywhere; a strict surface refuses to run an unguarded mutation.
CLI:
iwe updateandiwe deletetake--strict. Under the flag, every mutating application must carry its guard — the operation's document-level--expectand each block operator'sexpect. A missing guard is an error before anything runs.MCP: the
iwe_querytool is always strict, no opt-out; an unguarded mutation is refused with the missing guards named.
--dry-run writes nothing and is exempt from the guard requirement — it is how the counts are learned. Schema validation under --strict still runs, so the preview fails exactly when the real run would. The flow: dry-run the mutation, read the matched documents, pin expect to what it shows, re-run. An agent that just located its targets with $blocks already knows the counts.
Locate, then mutate
The agent workflow is two operations sharing one predicate:
# 1. Locate (read)
filter:
$includedBy: { match: { $key: projects/roadmap }, maxDepth: 3 }
$content: { $within: Goals, $text: "Q3" }
project:
key: $key
hits: { $blocks: { $within: Goals, $text: "Q3" } }# 2. Mutate (same predicate, now inside the operator)
filter:
$includedBy: { match: { $key: projects/roadmap }, maxDepth: 3 }
update:
$replaceText:
$within: Goals
$text: "Q3 Milestones"
from: "Q3 Milestones"
to: "Q3 2026 Milestones"
expect: 1CLI lowering
On the CLI, structural anchor flags lower to graph operators. A KEY[:DEPTH] suffix sets maxDepth (or maxDistance) for that anchor; depth 0 is the unbounded sentinel.
| CLI flag | Lowers to |
|---|---|
-k KEY | $key: KEY (1 key = $eq; 2+ = $in) |
--includes KEY | $includes: KEY (scalar shorthand, depth 1) |
--included-by KEY:5 | $includedBy: { match: { $key: KEY }, maxDepth: 5 } |
--references KEY:0 | $references: { match: { $key: KEY }, maxDistance: 0 } (unbounded) |
--referenced-by KEY | $referencedBy: KEY |
--max-depth N | session default for --includes / --included-by (default 1) |
--max-distance N | session default for --references / --referenced-by (default 1) |
--filter "EXPR" | inline YAML filter document |
--project "EXPR" | project: EXPR — comma list (title,author, body=$content, bare $blocks) or inline YAML mapping (find, tree) |
--add-fields "EXPR" | addFields: EXPR — same grammar as --project, extends the defaults (find, tree) |
--sort field:1 / --sort field:-1 | sort: { field: 1 / -1 } (find only) |
--sort field:DIR:binary / --sort field:DIR:sv-SE | sort: { field: { order: DIR, collation: binary / sv-SE } } (find only) |
-l, --limit N | limit: N (find, count) |
--blocks "PRED" | addFields: { blocks: { $blocks: PRED } } (find only) |
--matches PATTERN | filter: { $content: { $matches: PATTERN } } and addFields: { matches: { $matches: PATTERN } } (find only) |
--set FIELD=VALUE | $set: { FIELD: VALUE } (update only; repeatable) |
--unset FIELD | $unset: { FIELD: "" } (update only; repeatable) |
--replace, --replace-text, --insert-before, --insert-after, --append, --delete "ARG" | one block-operator entry ($replace, $replaceText, …) in the update document (update only) |
--expect VAL | the document-level expect clause (update, delete) |
--strict | surface policy, not grammar: requires the expect guards on every mutating application (update, delete) |
All filter flags AND together. For OR or NOR, write the composition inside --filter:
iwe find --filter '$or: [{ status: draft }, { status: review }]'
iwe find --filter '$nor: [{ status: archived }]'--matches is a composite flag — it lowers to both a membership clause and a projection entry, because one-flag grep is the point. Each block-operator flag's argument is that operator's { $selector…, payload… } mapping — the operator name is the flag, so the $replaceText: wrapper is dropped.
Combining -k KEY with a --filter whose top level also contains $key is a parse-time error — pick one source, or use -k a -k b for multi-key match.
485 files · 414 not served here
- .gitattributes (not served here)
- .githooks/pre-commit (not served here)
- .github/dependabot.yaml (not served here)
- .github/workflows/release-plz.yaml (not served here)
- .github/workflows/release.yaml (not served here)
- .github/workflows/rust.yml (not served here)
- .gitignore (not served here)
- .release-plz.toml (not served here)
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- CONTRIBUTING.md
- Cargo.lock (not served here)
- Cargo.toml (not served here)
- LICENSE-APACHE
- README.ko.md
- README.md
- README.zh-CN.md
- crates/diwe/CHANGELOG.md (not served here)
- crates/diwe/Cargo.toml (not served here)
- crates/diwe/src/config.rs (not served here)
- crates/diwe/src/file.rs (not served here)
- crates/diwe/src/find.rs (not served here)
- crates/diwe/src/fs.rs (not served here)
- crates/diwe/src/keys.rs (not served here)
- crates/diwe/src/lib.rs (not served here)
- crates/diwe/src/loader.rs (not served here)
- crates/diwe/src/retrieve.rs (not served here)
- crates/diwe/src/schema.rs (not served here)
- crates/diwe/src/search.rs (not served here)
- crates/diwe/src/search_query.rs (not served here)
- crates/diwe/src/stats.rs (not served here)
- crates/diwe/src/tokens.rs (not served here)
- crates/diwe/src/watcher.rs (not served here)
- crates/diwe/tests/config_migration_test.rs (not served here)
- crates/diwe/tests/djot_fs_test.rs (not served here)
- crates/diwe/tests/gitignore_test.rs (not served here)
- crates/diwe/tests/nested_keys_test.rs (not served here)
- crates/diwe/tests/query_search.rs (not served here)
- crates/diwe/tests/search_test.rs (not served here)
- crates/diwe/tests/stats_test.rs (not served here)
- crates/diwe/tests/stats_warnings_test.rs (not served here)
- crates/diwe/tests/windows_paths_test.rs (not served here)
- crates/iwe/CHANGELOG.md (not served here)
- crates/iwe/Cargo.toml (not served here)
- crates/iwe/LICENSE-APACHE
- crates/iwe/README.md (not served here)
- crates/iwe/benches/common.rs (not served here)
- crates/iwe/benches/load.rs (not served here)
- crates/iwe/benches/query.rs (not served here)
- crates/iwe/benches/similarity.rs (not served here)
- crates/iwe/docs/agent.md (not served here)
- crates/iwe/docs/config.md (not served here)
- crates/iwe/docs/index.txt (not served here)
- crates/iwe/docs/query.md (not served here)
- crates/iwe/docs/schema.md (not served here)
- crates/iwe/help/attach/about.txt (not served here)
- crates/iwe/help/attach/after_help.txt (not served here)
- crates/iwe/help/attach/long_about.txt (not served here)
- crates/iwe/help/completions/about.txt (not served here)
- crates/iwe/help/completions/after_help.txt (not served here)
- crates/iwe/help/completions/long_about.txt (not served here)
- crates/iwe/help/count/about.txt (not served here)
- crates/iwe/help/count/after_help.txt (not served here)
- crates/iwe/help/count/long_about.txt (not served here)
- crates/iwe/help/create/about.txt (not served here)
- crates/iwe/help/create/after_help.txt (not served here)
- crates/iwe/help/create/long_about.txt (not served here)
- crates/iwe/help/delete/about.txt (not served here)
- crates/iwe/help/delete/after_help.txt (not served here)
- crates/iwe/help/delete/long_about.txt (not served here)
- crates/iwe/help/docs/about.txt (not served here)
- crates/iwe/help/docs/after_help.txt (not served here)
- crates/iwe/help/docs/long_about.txt (not served here)
- crates/iwe/help/export/about.txt (not served here)
- crates/iwe/help/export/after_help.txt (not served here)
- crates/iwe/help/export/long_about.txt (not served here)
- crates/iwe/help/extract/about.txt (not served here)
- crates/iwe/help/extract/after_help.txt (not served here)
- crates/iwe/help/extract/long_about.txt (not served here)
- crates/iwe/help/find/about.txt (not served here)
- crates/iwe/help/find/after_help.txt (not served here)
- crates/iwe/help/find/long_about.txt (not served here)
- crates/iwe/help/init/about.txt (not served here)
- crates/iwe/help/init/after_help.txt (not served here)
- crates/iwe/help/init/long_about.txt (not served here)
- crates/iwe/help/inline/about.txt (not served here)
- crates/iwe/help/inline/after_help.txt (not served here)
- crates/iwe/help/inline/long_about.txt (not served here)
- crates/iwe/help/new/about.txt (not served here)
- crates/iwe/help/new/after_help.txt (not served here)
- crates/iwe/help/new/long_about.txt (not served here)
- crates/iwe/help/normalize/about.txt (not served here)
- crates/iwe/help/normalize/after_help.txt (not served here)
- crates/iwe/help/normalize/long_about.txt (not served here)
- crates/iwe/help/rename/about.txt (not served here)
- crates/iwe/help/rename/after_help.txt (not served here)
- crates/iwe/help/rename/long_about.txt (not served here)
- crates/iwe/help/retrieve/about.txt (not served here)
- crates/iwe/help/retrieve/after_help.txt (not served here)
- crates/iwe/help/retrieve/long_about.txt (not served here)
- crates/iwe/help/schema/about.txt (not served here)
- crates/iwe/help/schema/after_help.txt (not served here)
- crates/iwe/help/schema/long_about.txt (not served here)
- crates/iwe/help/squash/about.txt (not served here)
- crates/iwe/help/squash/after_help.txt (not served here)
- crates/iwe/help/squash/long_about.txt (not served here)
- crates/iwe/help/stats/about.txt (not served here)
- crates/iwe/help/stats/after_help.txt (not served here)
- crates/iwe/help/stats/long_about.txt (not served here)
- crates/iwe/help/tree/about.txt (not served here)
- crates/iwe/help/tree/after_help.txt (not served here)
- crates/iwe/help/tree/long_about.txt (not served here)
- crates/iwe/help/update/about.txt (not served here)
- crates/iwe/help/update/after_help.txt (not served here)
- crates/iwe/help/update/long_about.txt (not served here)
- crates/iwe/src/export.rs (not served here)
- crates/iwe/src/export/dot_details_exporter.rs (not served here)
- crates/iwe/src/export/dot_exporter.rs (not served here)
- crates/iwe/src/export/graph_colors.rs (not served here)
- crates/iwe/src/export/graph_data.rs (not served here)
- crates/iwe/src/filter_args.rs (not served here)
- crates/iwe/src/help.rs (not served here)
- crates/iwe/src/init.rs (not served here)
- crates/iwe/src/init/agents.rs (not served here)
- crates/iwe/src/init/fit.rs (not served here)
- crates/iwe/src/init/okf.rs (not served here)
- crates/iwe/src/init/report.rs (not served here)
- crates/iwe/src/init/schemas/okf-index.yaml (not served here)
- crates/iwe/src/init/schemas/okf-log.yaml (not served here)
- crates/iwe/src/init/schemas/okf.yaml (not served here)
- crates/iwe/src/init/screen.rs (not served here)
- crates/iwe/src/init/writer.rs (not served here)
- crates/iwe/src/internal.rs (not served here)
- crates/iwe/src/internal/claude.rs (not served here)
- crates/iwe/src/internal/claude/digest.rs (not served here)
- crates/iwe/src/internal/claude/enable.rs (not served here)
- crates/iwe/src/internal/claude/hook.rs (not served here)
- crates/iwe/src/internal/claude/hook/index.rs (not served here)
- crates/iwe/src/internal/claude/hook/post_tool.rs (not served here)
- crates/iwe/src/internal/claude/hook/store.rs (not served here)
- crates/iwe/src/internal/claude/policy.rs (not served here)
- crates/iwe/src/internal/claude/prompt.rs (not served here)
- crates/iwe/src/internal/claude/record.rs (not served here)
- crates/iwe/src/internal/claude/session.rs (not served here)
- crates/iwe/src/lib.rs (not served here)
- crates/iwe/src/main.rs (not served here)
- crates/iwe/src/new.rs (not served here)
- crates/iwe/src/projection_args.rs (not served here)
- crates/iwe/src/render.rs (not served here)
- crates/iwe/src/schema.rs (not served here)
- crates/iwe/src/stats.rs (not served here)
- crates/iwe/templates/claude/enable/queries.md (not served here)
- crates/iwe/templates/claude/enable/schemas/decision.yaml (not served here)
- crates/iwe/templates/claude/enable/schemas/gotcha.yaml (not served here)
- crates/iwe/templates/claude/enable/schemas/learning.yaml (not served here)
- crates/iwe/templates/claude/enable/schemas/memory.yaml (not served here)
- crates/iwe/templates/claude/enable/schemas/topic.yaml (not served here)
- crates/iwe/templates/claude/enable/starter.md (not served here)
- crates/iwe/templates/claude/enable/starter.toml (not served here)
- crates/iwe/templates/claude/enable/typed.md (not served here)
- crates/iwe/templates/claude/enable/typed.toml (not served here)
- crates/iwe/templates/claude/memory_index.md.jinja (not served here)
- crates/iwe/templates/claude/policy.schema.yaml (not served here)
- crates/iwe/templates/claude/prompts/distill.md (not served here)
- crates/iwe/templates/claude/prompts/init.md (not served here)
- crates/iwe/templates/claude/prompts/reflect.md (not served here)
- crates/iwe/templates/stats.md.jinja (not served here)
- crates/iwe/tests/attach_test.rs (not served here)
- crates/iwe/tests/cli_filter_test.rs (not served here)
- crates/iwe/tests/common.rs (not served here)
- crates/iwe/tests/completions_test.rs (not served here)
- crates/iwe/tests/config_test.rs (not served here)
- crates/iwe/tests/create_test.rs (not served here)
- crates/iwe/tests/delete_test.rs (not served here)
- crates/iwe/tests/docs_test.rs (not served here)
- crates/iwe/tests/export_dot_test.rs (not served here)
- crates/iwe/tests/extract_test.rs (not served here)
- crates/iwe/tests/find_test.rs (not served here)
- crates/iwe/tests/init_test.rs (not served here)
- crates/iwe/tests/inline_test.rs (not served here)
- crates/iwe/tests/internal_test.rs (not served here)
- crates/iwe/tests/main.rs (not served here)
- crates/iwe/tests/new_test.rs (not served here)
- crates/iwe/tests/normalize_test.rs (not served here)
- crates/iwe/tests/prompt_test.rs (not served here)
- crates/iwe/tests/query_schema_test.rs (not served here)
- crates/iwe/tests/rename_test.rs (not served here)
- crates/iwe/tests/retrieve_test.rs (not served here)
- crates/iwe/tests/schema_strict_test.rs (not served here)
- crates/iwe/tests/schema_test.rs (not served here)
- crates/iwe/tests/schema_validate_test.rs (not served here)
- crates/iwe/tests/squash_test.rs (not served here)
- crates/iwe/tests/stats_test.rs (not served here)
- crates/iwe/tests/stats_warnings_test.rs (not served here)
- crates/iwe/tests/tree_test.rs (not served here)
- crates/iwe/tests/update_test.rs (not served here)
- crates/iwec/CHANGELOG.md (not served here)
- crates/iwec/Cargo.toml (not served here)
- crates/iwec/README.md (not served here)
- crates/iwec/src/lib.rs (not served here)
- crates/iwec/src/main.rs (not served here)
- crates/iwec/src/watcher.rs (not served here)
- crates/iwec/tests/attach_test.rs (not served here)
- crates/iwec/tests/extract_test.rs (not served here)
- crates/iwec/tests/find_test.rs (not served here)
- crates/iwec/tests/fixture.rs (not served here)
- crates/iwec/tests/http_transport_test.rs (not served here)
- crates/iwec/tests/inline_test.rs (not served here)
- crates/iwec/tests/main.rs (not served here)
- crates/iwec/tests/normalize_test.rs (not served here)
- crates/iwec/tests/prompt_test.rs (not served here)
- crates/iwec/tests/query_test.rs (not served here)
- crates/iwec/tests/resource_test.rs (not served here)
- crates/iwec/tests/retrieve_test.rs (not served here)
- crates/iwec/tests/schema_test.rs (not served here)
- crates/iwec/tests/server_info_test.rs (not served here)
- crates/iwec/tests/squash_test.rs (not served here)
- crates/iwec/tests/stats_test.rs (not served here)
- crates/iwec/tests/stats_warnings_test.rs (not served here)
- crates/iwec/tests/tree_test.rs (not served here)
- crates/iwec/tests/watcher_test.rs (not served here)
- crates/iwec/tests/write_test.rs (not served here)
- crates/iwes/CHANGELOG.md (not served here)
- crates/iwes/Cargo.toml (not served here)
- crates/iwes/LICENSE-APACHE
- crates/iwes/README.md (not served here)
- crates/iwes/src/lib.rs (not served here)
- crates/iwes/src/main.rs (not served here)
- crates/iwes/src/router.rs (not served here)
- crates/iwes/src/router/server.rs (not served here)
- crates/iwes/src/router/server/actions.rs (not served here)
- crates/iwes/src/router/server/actions/attach.rs (not served here)
- crates/iwes/src/router/server/actions/delete.rs (not served here)
- crates/iwes/src/router/server/actions/extract.rs (not served here)
- crates/iwes/src/router/server/actions/extract_all.rs (not served here)
- crates/iwes/src/router/server/actions/inline.rs (not served here)
- crates/iwes/src/router/server/actions/link.rs (not served here)
- crates/iwes/src/router/server/actions/list.rs (not served here)
- crates/iwes/src/router/server/actions/section.rs (not served here)
- crates/iwes/src/router/server/actions/sort.rs (not served here)
- crates/iwes/src/router/server/actions/templates.rs (not served here)
- crates/iwes/src/router/server/actions/transform.rs (not served here)
- crates/iwes/src/router/server/base_path.rs (not served here)
- crates/iwes/src/router/server/extensions.rs (not served here)
- crates/iwes/src/router/server/query.rs (not served here)
- crates/iwes/src/router/server/search.rs (not served here)
- crates/iwes/tests/attach_test.rs (not served here)
- crates/iwes/tests/change_list_type_test.rs (not served here)
- crates/iwes/tests/completion_test.rs (not served here)
- crates/iwes/tests/custom_actions_test.rs (not served here)
- crates/iwes/tests/delete_test.rs (not served here)
- crates/iwes/tests/did_change_test.rs (not served here)
- crates/iwes/tests/did_save_test.rs (not served here)
- crates/iwes/tests/extract_all_test.rs (not served here)
- crates/iwes/tests/extract_test.rs (not served here)
- crates/iwes/tests/fixture.rs (not served here)
- crates/iwes/tests/folding_range_test.rs (not served here)
- crates/iwes/tests/format_document_test.rs (not served here)
- crates/iwes/tests/go_to_definition_test.rs (not served here)
- crates/iwes/tests/handler_panic_test.rs (not served here)
- crates/iwes/tests/hover_test.rs (not served here)
- crates/iwes/tests/inlay_hints_test.rs (not served here)
- crates/iwes/tests/inline_quote_test.rs (not served here)
- crates/iwes/tests/inline_section_test.rs (not served here)
- crates/iwes/tests/link_test.rs (not served here)
- crates/iwes/tests/list_to_sections_test.rs (not served here)
- crates/iwes/tests/main.rs (not served here)
- crates/iwes/tests/reconcile_test.rs (not served here)
- crates/iwes/tests/references_test.rs (not served here)
- crates/iwes/tests/rename_test.rs (not served here)
- crates/iwes/tests/sections_to_list_test.rs (not served here)
- crates/iwes/tests/sort_test.rs (not served here)
- crates/iwes/tests/transform_test.rs (not served here)
- crates/iwes/tests/unknown_method_test.rs (not served here)
- crates/iwes/tests/watched_files_test.rs (not served here)
- crates/iwes/tests/watcher_test.rs (not served here)
- crates/iwes/tests/workspace_symbols_test.rs (not served here)
- crates/liwe/CHANGELOG.md (not served here)
- crates/liwe/Cargo.toml (not served here)
- crates/liwe/LICENSE-APACHE
- crates/liwe/README.md (not served here)
- crates/liwe/src/collation.rs (not served here)
- crates/liwe/src/djot.rs (not served here)
- crates/liwe/src/djot/reader.rs (not served here)
- crates/liwe/src/djot/writer.rs (not served here)
- crates/liwe/src/format.rs (not served here)
- crates/liwe/src/fs.rs (not served here)
- crates/liwe/src/graph.rs (not served here)
- crates/liwe/src/graph/arena.rs (not served here)
- crates/liwe/src/graph/basic_iter.rs (not served here)
- crates/liwe/src/graph/builder.rs (not served here)
- crates/liwe/src/graph/graph_line.rs (not served here)
- crates/liwe/src/graph/graph_node.rs (not served here)
- crates/liwe/src/graph/index.rs (not served here)
- crates/liwe/src/graph/path.rs (not served here)
- crates/liwe/src/graph/sections_builder.rs (not served here)
- crates/liwe/src/graph/squash_iter.rs (not served here)
- crates/liwe/src/graph/walk.rs (not served here)
- crates/liwe/src/init.rs (not served here)
- crates/liwe/src/init/detect.rs (not served here)
- crates/liwe/src/init/evidence.rs (not served here)
- crates/liwe/src/init/notes.rs (not served here)
- crates/liwe/src/init/notes/tests.rs (not served here)
- crates/liwe/src/init/probe.rs (not served here)
- crates/liwe/src/init/settings.rs (not served here)
- crates/liwe/src/lib.rs (not served here)
- crates/liwe/src/locale.rs (not served here)
- crates/liwe/src/markdown.rs (not served here)
- crates/liwe/src/markdown/reader.rs (not served here)
- crates/liwe/src/markdown/writer.rs (not served here)
- crates/liwe/src/model.rs (not served here)
- crates/liwe/src/model/config.rs (not served here)
- crates/liwe/src/model/document.rs (not served here)
- crates/liwe/src/model/frontmatter.rs (not served here)
- crates/liwe/src/model/ids.rs (not served here)
- crates/liwe/src/model/inline.rs (not served here)
- crates/liwe/src/model/key_index.rs (not served here)
- crates/liwe/src/model/node.rs (not served here)
- crates/liwe/src/model/node_iter.rs (not served here)
- crates/liwe/src/model/node_pointer.rs (not served here)
- crates/liwe/src/model/projector.rs (not served here)
- crates/liwe/src/model/reference.rs (not served here)
- crates/liwe/src/model/tree.rs (not served here)
- crates/liwe/src/model/tree_iter.rs (not served here)
- crates/liwe/src/model/writer.rs (not served here)
- crates/liwe/src/operations.rs (not served here)
- crates/liwe/src/operations/attach.rs (not served here)
- crates/liwe/src/operations/changes.rs (not served here)
- crates/liwe/src/operations/config.rs (not served here)
- crates/liwe/src/operations/delete.rs (not served here)
- crates/liwe/src/operations/extract.rs (not served here)
- crates/liwe/src/operations/inline.rs (not served here)
- crates/liwe/src/operations/rename.rs (not served here)
- crates/liwe/src/operations/select.rs (not served here)
- crates/liwe/src/operations/util.rs (not served here)
- crates/liwe/src/parser.rs (not served here)
- crates/liwe/src/query.rs (not served here)
- crates/liwe/src/query/block.rs (not served here)
- crates/liwe/src/query/block_eval.rs (not served here)
- crates/liwe/src/query/block_update.rs (not served here)
- crates/liwe/src/query/builder.rs (not served here)
- crates/liwe/src/query/cli.rs (not served here)
- crates/liwe/src/query/document.rs (not served here)
- crates/liwe/src/query/edges.rs (not served here)
- crates/liwe/src/query/eval.rs (not served here)
- crates/liwe/src/query/execute.rs (not served here)
- crates/liwe/src/query/filter.rs (not served here)
- crates/liwe/src/query/graph_match.rs (not served here)
- crates/liwe/src/query/project.rs (not served here)
- crates/liwe/src/query/schemas.rs (not served here)
- crates/liwe/src/query/schemas/draft-2026-08.json (not served here)
- crates/liwe/src/query/scores.rs (not served here)
- crates/liwe/src/query/search.rs (not served here)
- crates/liwe/src/query/sort.rs (not served here)
- crates/liwe/src/query/update.rs (not served here)
- crates/liwe/src/query/wire.rs (not served here)
- crates/liwe/src/schema.rs (not served here)
- crates/liwe/src/schema/document.rs (not served here)
- crates/liwe/src/schema/infer.rs (not served here)
- crates/liwe/src/state.rs (not served here)
- crates/liwe/tests/blocks.rs (not served here)
- crates/liwe/tests/comments_test.rs (not served here)
- crates/liwe/tests/djot_roundtrip.rs (not served here)
- crates/liwe/tests/formatting_test.rs (not served here)
- crates/liwe/tests/frontmatter_title_test.rs (not served here)
- crates/liwe/tests/html_test.rs (not served here)
- crates/liwe/tests/links_test.rs (not served here)
- crates/liwe/tests/live_resolution_test.rs (not served here)
- crates/liwe/tests/main.rs (not served here)
- crates/liwe/tests/normalization_escapes.rs (not served here)
- crates/liwe/tests/normalization_headers.rs (not served here)
- crates/liwe/tests/normalization_html.rs (not served here)
- crates/liwe/tests/normalization_lists.rs (not served here)
- crates/liwe/tests/normalization_metadata.rs (not served here)
- crates/liwe/tests/normalization_misc.rs (not served here)
- crates/liwe/tests/normalization_raw.rs (not served here)
- crates/liwe/tests/operations_inline_test.rs (not served here)
- crates/liwe/tests/operations_rename_test.rs (not served here)
- crates/liwe/tests/queries.rs (not served here)
- crates/liwe/tests/query_block_update.rs (not served here)
- crates/liwe/tests/query_blocks.rs (not served here)
- crates/liwe/tests/query_content_filter.rs (not served here)
- crates/liwe/tests/query_count.rs (not served here)
- crates/liwe/tests/query_delete.rs (not served here)
- crates/liwe/tests/query_deserialize.rs (not served here)
- crates/liwe/tests/query_filter_expression.rs (not served here)
- crates/liwe/tests/query_find.rs (not served here)
- crates/liwe/tests/query_frontmatter_prefixes.rs (not served here)
- crates/liwe/tests/query_graph.rs (not served here)
- crates/liwe/tests/query_scores.rs (not served here)
- crates/liwe/tests/query_update.rs (not served here)
- crates/liwe/tests/root_keys_test.rs (not served here)
- crates/liwe/tests/roundtrip_test.rs (not served here)
- crates/liwe/tests/squash_test.rs (not served here)
- crates/liwe/tests/task_items_test.rs (not served here)
- crates/liwe/tests/toc_test.rs (not served here)
- crates/liwe/tests/wide_document_test.rs (not served here)
- crates/liwe/tests/writer_escaping_test.rs (not served here)
- docs/Makefile (not served here)
- docs/architecture.md
- docs/benchmark.md
- docs/book.pdf (not served here)
- docs/book.typ (not served here)
- docs/cli-attach.md
- docs/cli-count.md
- docs/cli-create.md
- docs/cli-delete.md
- docs/cli-export.md
- docs/cli-extract.md
- docs/cli-find.md
- docs/cli-init.md
- docs/cli-inline.md
- docs/cli-new.md
- docs/cli-normalize.md
- docs/cli-rename.md
- docs/cli-retrieve.md
- docs/cli-schema.md
- docs/cli-squash.md
- docs/cli-stats.md
- docs/cli-tree.md
- docs/cli-troubleshooting.md
- docs/cli-update.md
- docs/cli-workflows.md
- docs/cli.md
- docs/comparison.md
- docs/configuration.md
- docs/data-model.md
- docs/debug-mode.md
- docs/design-principles.md
- docs/docs-basic.dot (not served here)
- docs/docs-basic.svg (not served here)
- docs/docs-detailed.dot (not served here)
- docs/docs-detailed.svg (not served here)
- docs/document-schema.md
- docs/documents-as-binary-tree.png (not served here)
- docs/feature-ai-commands.md
- docs/feature-attach.md
- docs/feature-auto-format.md
- docs/feature-autocomplete.md
- docs/feature-delete.md
- docs/feature-extract.md
- docs/feature-hover-preview.md
- docs/feature-inlay-hints.md
- docs/feature-inline.md
- docs/feature-link.md
- docs/feature-navigation.md
- docs/feature-normalization.md
- docs/feature-overview.md
- docs/feature-renaming.md
- docs/feature-search.md
- docs/feature-subdirectories.md
- docs/feature-text-manipulation.md
- docs/feature-visualization.md
- docs/helix.md
- docs/how-to-install.md
- docs/how-to-use.md
- docs/inclusion-links.md
- docs/index.md
- docs/keys.md
- docs/mcp.md
- docs/neovim.md
- docs/okf.md
- docs/query-language.md
- docs/search-ranking.md
- docs/spec.md
- docs/vscode.md
- docs/why-it-exists.md (not served here)
- docs/zed.md (not served here)
- glama.json (not served here)
- npm/cli-darwin-arm64/package.json (not served here)
- npm/cli-darwin-x64/package.json (not served here)
- npm/cli-linux-arm64/package.json (not served here)
- npm/cli-linux-x64/package.json (not served here)
- npm/cli-win32-x64/package.json (not served here)
- npm/iwe/README.md (not served here)
- npm/iwe/bin/iwe.js (not served here)
- npm/iwe/bin/iwec.js (not served here)
- npm/iwe/bin/iwes.js (not served here)
- npm/iwe/lib/run.js (not served here)
- npm/iwe/package.json (not served here)
- npm/mcp/README.md (not served here)
- npm/mcp/bin/iwe-mcp.js (not served here)
- npm/mcp/package.json (not served here)
- npm/stamp.js (not served here)
- server.json
Advantages
- Shared context allows agents to access and build upon the same notes written by the user. evidence: README.md
- Memory is stored as human-readable Markdown files that can be easily browsed, corrected, or deleted. evidence: README.md
- Data remains under user control in local folders, avoiding dependency on proprietary cloud storage. evidence: README.md
Limitations
- Requires manual setup of the MCP server or CLI tools to enable agent connectivity. evidence: README.md
- The system does not include an AI model, requiring the user to bring their own agent. evidence: README.md
Security
Risk level: Low risk
This brain runs
Git hooks (1)
.githooks/pre-commit(pre-commit):runs on git pre-commit
- Low risk: scripts-present (.githooks/pre-commit)
Attribution and licence
- Created by
- IWE
- Publisher
- IWE (Official IWE project)
- Source
- iwe-org/iwe · commit b733afe
- Licence
- Apache License 2.0 (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by IWE.
Install from source
414 of its files are not served here, so there is no ZIP. iwe-org/iwe at b733afe