Skip to main content
ClaudeWave

Address any node in your Markdown. A structured read/write engine for Markdown documents.

SubagentsRegistry oficial9 estrellas1 forks● TypeScriptMITActualizado 7d ago
ClaudeWave Trust Score
95/100
✓ Verified
Passed
  • ✓Open-source license (MIT)
  • ✓Actively maintained (<30d)
  • ✓Clear description
  • ✓Topics declared
  • ✓Documented (README)
Last scanned: 10/12/2026
Install as a Claude Code subagent
Method: Clone
Terminal
git clone https://github.com/Synexiom-Labs/nodiom && cp nodiom/*.md ~/.claude/agents/
1. Clone the repository and copy the agent .md definitions into ~/.claude/agents (or .claude/agents inside a project).
2. Start a new Claude Code session to load the agents.
3. Delegate work to them with the Task/Agent tool or by name.
Casos de uso

Resumen de Subagents

# nodiom

**Address any node in your Markdown.**

[![npm version](https://img.shields.io/npm/v/@synexiom-labs%2Fnodiom)](https://www.npmjs.com/package/@synexiom-labs/nodiom)
[![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
[![test](https://img.shields.io/github/actions/workflow/status/Synexiom-Labs/nodiom/test.yml?label=tests)](https://github.com/Synexiom-Labs/nodiom/actions)

Markdown has structure. Every tool ignores it. Nodiom doesn't.

---

## The Problem

When an AI agent or any program needs to modify a Markdown document, it loads the whole file as a string, uses fragile regex to find the right section, splices in new content, and hopes the formatting didn't break.

Nodiom solves this by treating Markdown as what it actually is: a tree. Every heading creates a branch, every list item is a node. You address them with path-like selectors — the same way you'd query a DOM node or a database row.

---

## Quick Example

```typescript
import { Nodiom } from '@synexiom-labs/nodiom';

const doc = await Nodiom.fromFile('./project-wiki.md');

// Read everything under a heading
const tasks = doc.read('# Project Alpha > ## Active Tasks');

// Add a new task
doc.append('# Project Alpha > ## Active Tasks', '- [ ] Deploy to staging');

// Remove a completed item
doc.delete('# Project Alpha > ## Completed > li[0]');

// Save back to disk
await doc.save();
```

---

## Install

```bash
npm install @synexiom-labs/nodiom
```

**Requirements:** Node.js 18+

---

## Core Concepts: Selectors

A selector is a `" > "`-separated path of segments, each matching a node in the document's structure.

### Heading segments

```
"# Project Alpha"              → The H1 "Project Alpha" and all its content
"## Tasks"                     → Any top-level H2 named "Tasks"
"# Project Alpha > ## Tasks"   → The H2 "Tasks" nested under H1 "Project Alpha"
```

The depth of the `#` characters must match the heading depth in the document.

**Duplicate headings.** If the same heading appears more than once in a scope,
a bare segment is ambiguous and throws `AmbiguousSelectorError` — nodiom never
silently picks the first. Qualify it with its parent heading, or address one by
index, zero-based and negative-capable like `li[n]`:

```
"# Doc > ## Notes[0]"    → The first "## Notes" under "# Doc"
"# Doc > ## Notes[1]"    → The second
"# Doc > ## Notes[-1]"   → The last
```

The index must sit directly against the text. `"## References [1]"`, with a
space, is matched as a literal heading, and a heading that genuinely reads
`Tasks[1]` is always tried literally first.

**Frontmatter.** A YAML (`---`) or TOML (`+++`) block at the top of a document
is recognised as frontmatter. It never appears in `tree()`, can't be addressed
by a selector, and is left byte-identical by every edit.

### Element segments

```
"## Tasks > li[0]"     → First list item under ## Tasks
"## Tasks > li[-1]"    → Last list item (negative indexing)
"## Notes > p[0]"      → First paragraph under ## Notes
"## Arch > table[0]"   → First table under ## Architecture
"## Arch > code[0]"    → First code block under ## Architecture
```

Valid element types: `p`, `li`, `code`, `blockquote`, `table`, `hr`

### Content segments

An index is a position, not an identity. If another writer appends to the same
section between the moment you compute `li[2]` and the moment you write to it,
that index now points at a different node — and the write lands on the wrong
one, silently.

Address by content instead whenever more than one writer touches a document:

```
'## In Progress > li[contains="c_7f3a"]'   → The list item whose text contains "c_7f3a"
"## Notes > p[contains='TODO']"            → The paragraph containing "TODO"
```

The substring is matched against the node's own source text, and must match
**exactly one** node in scope. Matching several throws `AmbiguousSelectorError`
rather than picking the first — silently editing one of several candidates is
the failure this exists to prevent.

```js
import { AmbiguousSelectorError } from '@synexiom-labs/nodiom';

try {
  doc.delete('## In Progress > li[contains="c_7f3a"]');
} catch (e) {
  if (e instanceof AmbiguousSelectorError) {
    // Use a longer or more distinctive substring.
  }
}
```

### Heading scope semantics

"Content under `## Tasks`" means all nodes that appear after the `## Tasks` heading until the next heading of equal or lesser depth. This matches how you naturally read a Markdown document.

---

## API Reference

### Initialization

```typescript
// From a file
const doc = await Nodiom.fromFile('./wiki.md');

// From a file with advisory locking (for concurrent access)
const doc = await Nodiom.fromFile('./wiki.md', { lock: true, lockTimeout: 5000 });

// From a string (no filesystem — safe for serverless)
const doc = Nodiom.fromString(markdownContent);
```

### Reading

```typescript
// Returns the Markdown string of everything under the selector
const content: string = doc.read('# Project > ## Summary');

// Returns an array of list item strings
const items: string[] = doc.readList('# Project > ## Tasks');

// Returns metadata about the location
const info = doc.query('# Project > ## Tasks');
// { exists: true, type: 'heading', depth: 2, childCount: 7, index: 3 }

// Returns the structural outline of the document
const outline = doc.tree();
// [{ heading: 'Project', depth: 1, children: [{ heading: 'Tasks', depth: 2, children: [] }] }]
```

### Mutating

All mutation methods return `this`, so they can be chained.

```typescript
// Replace all content within a section (heading is preserved)
doc.write('# Project > ## Summary', 'The project is on track for Q3.');

// Append content after the last item in a section
doc.append('# Project > ## Tasks', '- [ ] Final review');

// Remove a node
doc.delete('# Project > ## Completed > li[0]');

// Chaining
doc
  .append('# Project > ## Tasks', '- [ ] Task A')
  .append('# Project > ## Tasks', '- [ ] Task B')
  .delete('# Project > ## Completed > li[-1]');
```

### Serialization & File I/O

```typescript
// Get the current document as a Markdown string
const output: string = doc.toString();

// Write back to the original file (releases lock if held)
await doc.save();

// Write to a new file
await doc.saveAs('./output.md');

// Explicitly release a lock
await doc.unlock();
```

### Error Handling

```typescript
import { SelectorNotFoundError, SelectorParseError, LockError } from '@synexiom-labs/nodiom';

try {
  doc.read('# Project > ## Taks'); // typo
} catch (e) {
  if (e instanceof SelectorNotFoundError) {
    console.log(e.selector);     // "# Project > ## Taks"
    console.log(e.suggestions);  // ["Tasks"] — fuzzy match
  }
}
```

---

## AI Agent Example

A LangChain-style agent maintaining a project wiki:

```typescript
import { Nodiom } from '@synexiom-labs/nodiom';

async function agentUpdateWiki(wikiPath: string, taskDescription: string) {
  // Load with lock — safe for concurrent agent access on the same machine
  const doc = await Nodiom.fromFile(wikiPath, { lock: true });

  // Add a new active task
  doc.append('## Tasks > ### Active', `- [ ] ${taskDescription}`);

  // Log the current task count
  const activeTasks = doc.readList('## Tasks > ### Active');
  console.log(`Active tasks: ${activeTasks.length}`);

  // Save and release the lock
  await doc.save();
}
```

---

## Concurrency

Nodiom includes advisory file locking via [`proper-lockfile`](https://github.com/moxystudio/node-proper-lockfile).

```typescript
const doc = await Nodiom.fromFile('./shared-wiki.md', { lock: true });
// Other Nodiom processes trying to lock this file will wait or throw LockError

doc.append('## Log', `- ${new Date().toISOString()} Agent B completed task`);
await doc.save(); // writes and releases the lock
```

**Scope of the concurrency guarantee:** Advisory locking prevents corruption from multiple Node.js processes on the **same machine**. For distributed multi-agent environments (multiple machines or serverless functions), use Nodiom Cloud (coming soon) which provides true distributed concurrency with structural merge semantics.

---

## Comparison

| Approach | Structured reads? | Structural writes? | Roundtrip fidelity | Serverless | Concurrent |
|---|---|---|---|---|---|
| `fs.readFile` + regex | No | Fragile | Depends on regex | No | No |
| `gray-matter` | Frontmatter only | Frontmatter only | Yes | Yes | No |
| Databases | No | No | No (loses Markdown) | Yes | Yes |
| Vector DBs | Semantic only | No | No | Yes | Yes |
| **Nodiom** | **Yes** | **Yes** | **Yes** | **Yes** | **Single-machine** |

---

## Roadmap

- **v0.2.0** — `insert()` with positional control (`before` / `after` a sibling), `diff()` structural diffing, glob selectors (`## *Tasks*`)
- **v0.3.0** — LangChain tool wrapper
- **v1.0.0** — Stable selector syntax, production-ready API
- **Nodiom Cloud** — Hosted HTTP API for serverless and distributed multi-agent use. No local setup required. [Join the waitlist →](https://nodiom.md#cloud)

---

## Contributing

Contributions welcome. Please open an issue before submitting a PR for non-trivial changes.

```bash
git clone https://github.com/synexiom-labs/nodiom
cd nodiom
npm install
npm test
```

---

## License

MIT — [Synexiom Labs Inc.](https://synexiomlabs.com)

*Born from the state layer powering Cortexiom's reasoning architecture.*
ai-agentastllmmarkdownmcpmdastnodejsremarkstructured-datatypescript

Lo que la gente pregunta sobre nodiom

¿Qué es Synexiom-Labs/nodiom?

+

Synexiom-Labs/nodiom es subagents para el ecosistema de Claude AI. Address any node in your Markdown. A structured read/write engine for Markdown documents. Tiene 9 estrellas en GitHub y su última actualización registrada es del 2026-10-04.

¿Cómo se instala nodiom?

+

Puedes instalar nodiom clonando el repositorio (https://github.com/Synexiom-Labs/nodiom) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar Synexiom-Labs/nodiom?

+

Nuestro agente de seguridad ha analizado Synexiom-Labs/nodiom y le ha asignado un Trust Score de 95/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.

¿Quién mantiene Synexiom-Labs/nodiom?

+

Synexiom-Labs/nodiom es mantenido por Synexiom-Labs. La última actividad registrada en GitHub es del 2026-10-04, con 1 issues abiertos.

¿Hay alternativas a nodiom?

+

Sí. En ClaudeWave puedes explorar subagents similares en /categories/agents, ordenados por popularidad o actividad reciente.

Despliega nodiom en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: Synexiom-Labs/nodiom
[![Featured on ClaudeWave](https://claudewave.com/api/badge/synexiom-labs-nodiom)](https://claudewave.com/repo/synexiom-labs-nodiom)
<a href="https://claudewave.com/repo/synexiom-labs-nodiom"><img src="https://claudewave.com/api/badge/synexiom-labs-nodiom" alt="Featured on ClaudeWave: Synexiom-Labs/nodiom" width="320" height="64" /></a>

Más Subagents

Alternativas a nodiom