@aisystemresources/emdee 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -50
- package/bin/emdee.js +7 -1
- package/package.json +2 -2
- package/src/mcp/server.ts +2 -1
package/README.md
CHANGED
|
@@ -1,85 +1,134 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Emdee
|
|
2
2
|
|
|
3
|
-
Local-first knowledge graph backed by plain markdown. Humans browse it through a
|
|
3
|
+
Local-first knowledge graph backed by plain markdown. Humans browse it through a Next.js renderer; agents (Claude Code, Claude.ai, Cursor, Codex) read and write the same files through an MCP server. The vault is the source of truth — anything an LLM says traces back to a file you wrote.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
LLM agents need a stable, human-readable substrate to read and write their own context over time. Most knowledge-graph tools are either built for humans (Obsidian) or built for agents (vector stores).
|
|
7
|
+
LLM agents need a stable, human-readable substrate to read and write their own context over time. Most knowledge-graph tools are either built for humans (Obsidian) or built for agents (vector stores). Emdee is a single substrate for both: the markdown a human edits is the exact bytes an agent reads, with no hidden index, no parallel summaries, no schema gymnastics. Build up a working journal that survives across sessions.
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
Published on npm as `@aisystemresources/emdee`. The CLI (`emdee init`, `emdee mcp`, `emdee list`, `emdee drift-batch`) is globally installable; `emdee start` / `emdee serve-next` still require a repo checkout because they run the full Vite / Next viewer.
|
|
12
|
-
|
|
13
|
-
## Install (consumer)
|
|
9
|
+
## Install
|
|
14
10
|
|
|
15
11
|
```bash
|
|
16
12
|
npm install -g @aisystemresources/emdee
|
|
17
13
|
cd ~/my-vault
|
|
18
|
-
emdee init --nickname "Your Name"
|
|
19
|
-
emdee list
|
|
20
|
-
emdee mcp
|
|
14
|
+
emdee init --nickname "Your Name" # writes ./docs/YOUR-NAME.md as your owner node
|
|
15
|
+
emdee list # your owner + 5 virtual system nodes
|
|
16
|
+
emdee mcp # stdio MCP server — point Claude Code at it
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
##
|
|
19
|
+
## The 5-node OS layer
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
git clone https://github.com/elz-ming/silent-mane.git
|
|
27
|
-
cd silent-mane
|
|
28
|
-
npm install
|
|
29
|
-
./bin/mane.js init # seeds docs/ with the entry doc, conventions, and sample branch
|
|
30
|
-
npm run dev # Vite dev server with hot reload at http://localhost:5173
|
|
31
|
-
npm run mcp # MCP server over stdio (point Claude.ai / Cursor / Codex at it)
|
|
32
|
-
```
|
|
21
|
+
Every Emdee vault has exactly 5 canonical top-level nodes, plus your owner node:
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
| Node | Purpose |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `EMDEE` | Vault root — the anchor everything hangs off. |
|
|
26
|
+
| `VAULT` | Your private notes, projects, and knowledge. |
|
|
27
|
+
| `SHARED` | Content shared with you by others (cloud only). |
|
|
28
|
+
| `GRAVEYARD` | Archived and retired documents. |
|
|
29
|
+
| `IMAGES` | Images and visual assets. |
|
|
30
|
+
| `<YOUR-NAME>` | Your personal subtree, seeded by `emdee init --nickname`. |
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
- `docs/VAULT.md` — meta-pillar grouping the system docs below
|
|
38
|
-
- `docs/INFO.md` — conventions (filenames, relationships, writing format)
|
|
39
|
-
- `docs/INSTRUCTIONS.md` — CEO operating protocol for cross-project agents
|
|
40
|
-
- `docs/BRAIN.md` — cross-project distilled wisdom (always-loaded prior)
|
|
41
|
-
- `docs/WORKFLOWS.md` — concrete procedures the vault runs
|
|
42
|
-
- `docs/SAMPLE.md` + `docs/sample/` — pedagogical examples; delete with `rm -rf docs/sample/` once you've read them
|
|
32
|
+
The 5 system nodes are **virtual** — they appear in every read (`emdee list`, `get_doc`, MCP responses) without being written to disk. Edit any of them via MCP and your version wins. Only the owner node (`<YOUR-NAME>.md`) actually lives on disk after `emdee init`.
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
## Quick start (developer / from source)
|
|
45
35
|
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/AISystemResources/emdee.git
|
|
38
|
+
cd emdee
|
|
39
|
+
npm install
|
|
40
|
+
./bin/emdee.js init --nickname "You" # seeds ./docs/ with your owner node
|
|
41
|
+
npm run dev # Next.js viewer at http://localhost:3000
|
|
42
|
+
npm run mcp # stdio MCP server
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The web viewer (`emdee start`, `emdee serve-next`) is repo-only — it needs the Next.js `app/` tree that isn't in the published tarball. `init`, `list`, `drift-batch`, `mcp` all work from the global install.
|
|
46
46
|
|
|
47
47
|
## MCP tools
|
|
48
48
|
|
|
49
|
-
The
|
|
49
|
+
The stdio server (`emdee mcp`) exposes 18 tools.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
**Reads:**
|
|
52
|
+
- `list_docs` — every doc as `{path, title, summary}`. `format: "text"` returns paths only.
|
|
53
|
+
- `get_doc(path)` — full markdown, per-section `content_hash` for version-guarded patches.
|
|
52
54
|
- `get_summary(path)` — one doc's `{path, title, summary}`. Cheap.
|
|
53
|
-
- `get_neighbors(path)` — focal doc + 1-hop
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
56
|
-
- `
|
|
57
|
-
- `
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
- `get_neighbors(path)` — focal doc + 1-hop neighbourhood, categorised as parents / children / associated, each with the prose note attached to its wiki-link.
|
|
56
|
+
- `get_context(path, hops?, budget_tokens?)` — multi-hop neighbourhood within a token budget.
|
|
57
|
+
- `read_doc_section(path, section_id)` — one section without paying for the whole doc.
|
|
58
|
+
- `search(query, limit?)` — case-insensitive substring over titles, summaries, content.
|
|
59
|
+
- `list_summary_drift` — paths whose body has drifted since their summary was last authored.
|
|
60
|
+
|
|
61
|
+
**Writes (version-guarded where destructive):**
|
|
62
|
+
- `patch_section(path, section_id, body, expected_content_hash)` — replace one section; mismatched hash returns structured `version_conflict`.
|
|
63
|
+
- `append_section(path, section_id, body)` — safer than write_doc for incremental edits.
|
|
64
|
+
- `append_doc(path, body)` — append to end of doc (chronological notes, LOGS entries).
|
|
65
|
+
- `patch_preamble(path, body, expected_content_hash)` — replace the region between H1 and first H2.
|
|
66
|
+
- `write_doc(path, content)` — full-file replace (destructive; prefer section-scoped tools).
|
|
67
|
+
- `write_doc_preview(path, content)` — diff + list of sections that would be removed. Always call before `write_doc`.
|
|
68
|
+
|
|
69
|
+
**Atomic multi-side writes (keep the graph consistent):**
|
|
70
|
+
- `create_child(parent_path, title, body?, summary?)` — writes new doc with canonical scaffold AND patches parent's `## Parent of`. Collapses the 5-round-trip add-child flow into one call.
|
|
71
|
+
- `add_association(a_path, b_path, label?)` — patches both docs' `## Associated with` in one call. Hard-refuses hierarchy or sibling duplicates.
|
|
72
|
+
- `move_doc(path, new_parent_path)` — atomic reparent, three-side edge update.
|
|
73
|
+
- `rename_doc(old_path, new_title, new_path?)` — rewrites H1, moves the file, updates every `[[old_title]]` across the vault.
|
|
74
|
+
- `materialize_subgroup(source_path, subgroup_heading)` — promote an H3 subgroup inside `## Parent of` into a real intermediate parent doc.
|
|
75
|
+
- `split_doc(source_path, rewrite_source_content, extracts)` — atomically refactor a doc into concept nodes.
|
|
76
|
+
|
|
77
|
+
**Lifecycle:**
|
|
78
|
+
- `trash_doc(path)` / `restore_doc(path)` — sidecar-based soft delete, edges preserved for lossless restore.
|
|
79
|
+
- `delete_doc(path)` — permanent, no undo. Returns inbound edges + title conflicts.
|
|
60
80
|
|
|
61
81
|
## Design principles
|
|
62
82
|
|
|
63
83
|
1. **Markdown is the only source of truth.** No persisted index, no derived database, no parallel summaries.
|
|
64
84
|
2. **Same substrate, different lenses.** Renderer and MCP read the same files via the same indexer. Nothing the LLM sees is invisible to the human.
|
|
65
|
-
3. **Convention over schema.** Light structure — H1 + `> blockquote` summary + three relationship sections (`## Parent of`, `## Child of`, `## Associated with`).
|
|
85
|
+
3. **Convention over schema.** Light structure — H1 + `> blockquote` summary + three relationship sections (`## Parent of`, `## Child of`, `## Associated with`). Rigid schemas add authoring friction; the LLM parses English natively.
|
|
66
86
|
4. **Single summary per doc.** The blockquote under the H1 is the routing decision for both humans and LLMs.
|
|
87
|
+
5. **Version-guarded writes.** Every destructive edit takes an `expected_content_hash`. Concurrent edits fail loudly with `version_conflict` instead of silent overwrites.
|
|
67
88
|
|
|
68
|
-
## What's in
|
|
89
|
+
## What's in the repo
|
|
69
90
|
|
|
70
|
-
- `bin/
|
|
91
|
+
- `bin/emdee.js` — the `emdee` CLI (`init`, `list`, `drift-batch`, `mcp`, `start`, `serve-next`)
|
|
71
92
|
- `src/core/indexer.ts` — walks `docs/`, parses wiki-links and relationship sections, derives summaries, skips fenced code blocks
|
|
72
|
-
- `src/
|
|
73
|
-
- `src/
|
|
74
|
-
- `src/
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
93
|
+
- `src/core/syncDocEdges.ts` — incremental edge sync backed by Supabase
|
|
94
|
+
- `src/mcp/server.ts` — stdio MCP server
|
|
95
|
+
- `src/lib/mcp/tools/` — the 18 tools listed above
|
|
96
|
+
- `src/lib/system-nodes.ts` — canonical content for the 5 virtual system nodes
|
|
97
|
+
- `src/lib/storage/` — `FilesystemStorage` (local mode) + `SupabaseStorage` (cloud mode) behind a single `VaultStorage` interface
|
|
98
|
+
- `app/` — Next.js App Router: renderer, `/api/index`, `/api/mcp`, OAuth pages for the claude.ai connector
|
|
99
|
+
- `supabase/migrations/` — schema (new files only, never edited in place)
|
|
100
|
+
- `templates/types/` — archetype scaffolds (`PROJECT`, `NOVEL`, `PERSON`, `HACKATHON`, `CONCEPT`) for future `emdee new <type>` commands
|
|
101
|
+
- `e2e/` — Playwright suite (MCP tools, auth, share RBAC, upload, CLI init)
|
|
102
|
+
|
|
103
|
+
## Conventions
|
|
77
104
|
|
|
78
|
-
|
|
105
|
+
Every doc follows the same shape:
|
|
79
106
|
|
|
80
|
-
|
|
107
|
+
```md
|
|
108
|
+
# TITLE
|
|
109
|
+
|
|
110
|
+
> One-line blockquote summary — this is what routing sees.
|
|
111
|
+
|
|
112
|
+
## Child of
|
|
113
|
+
|
|
114
|
+
* [[PARENT]]
|
|
115
|
+
|
|
116
|
+
## Parent of
|
|
117
|
+
|
|
118
|
+
* [[CHILD1]]
|
|
119
|
+
* [[CHILD2]]
|
|
120
|
+
|
|
121
|
+
## Associated with
|
|
122
|
+
|
|
123
|
+
* [[CROSS-TREE-NODE]] — optional prose about the link
|
|
124
|
+
|
|
125
|
+
## Notes
|
|
126
|
+
|
|
127
|
+
Freeform content.
|
|
128
|
+
```
|
|
81
129
|
|
|
82
|
-
|
|
130
|
+
Rules the lint enforces: one parent per doc; no self-loops; no associates that duplicate a hierarchy edge or sibling relationship; UPPERCASE filenames; lowercase folder names. `lint_doc(path)` surfaces violations; write tools accept `gate_on_warnings: ["code"]` to hard-block on specific ones.
|
|
83
131
|
|
|
84
|
-
|
|
132
|
+
## Cloud deployment
|
|
85
133
|
|
|
134
|
+
The repo also runs as a full Next.js web viewer at [emdee.tech](https://emdee.tech). Vercel auto-detects Next.js — set the standard Supabase + Clerk env vars, push to `main`, done. The `/api/mcp` endpoint speaks the HTTP MCP transport for claude.ai connectors; `/oauth/authorize` runs the PKCE flow for the "Connect to Claude.ai" panel.
|
package/bin/emdee.js
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { mkdir, writeFile, access } from "node:fs/promises";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import readline from "node:readline/promises";
|
|
6
7
|
import path from "node:path";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const pkgRoot = path.resolve(__dirname, "..");
|
|
12
|
+
// Version comes from package.json — `npm version <bump>` is the one
|
|
13
|
+
// place the string ever needs to change. createRequire works on any
|
|
14
|
+
// Node >= 14 without depending on the newer `import ... with { type: "json" }`
|
|
15
|
+
// attribute syntax (stable only in Node 20.10+).
|
|
16
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
11
17
|
|
|
12
18
|
// SPRINT-090: `start` and `serve-next` shell out to Vite / Next.js against
|
|
13
19
|
// the full repo. Those files (app/, next.config.*, etc.) aren't in the
|
|
@@ -60,7 +66,7 @@ function ownerNodeScaffold(title) {
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
const program = new Command();
|
|
63
|
-
program.name("emdee").description("Emdee — local docs + knowledge graph + MCP").version(
|
|
69
|
+
program.name("emdee").description("Emdee — local docs + knowledge graph + MCP").version(pkg.version);
|
|
64
70
|
|
|
65
71
|
program
|
|
66
72
|
.command("init")
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aisystemresources/emdee",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Local-first document management with markdown rendering, knowledge graph, and MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"emdee": "
|
|
7
|
+
"emdee": "bin/emdee.js"
|
|
8
8
|
},
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
package/src/mcp/server.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
restoreDoc,
|
|
28
28
|
} from "../lib/mcp/tools/index.js";
|
|
29
29
|
import type { ToolContext } from "../lib/mcp/tools/types.js";
|
|
30
|
+
import pkg from "../../package.json";
|
|
30
31
|
// SPRINT-021: this stdio entrypoint is hardcoded to local mode (no
|
|
31
32
|
// clerk_id, no namespace), so mcp_activity logging is intentionally
|
|
32
33
|
// skipped here. Cloud-mode logging lives in app/api/mcp/route.ts.
|
|
@@ -37,7 +38,7 @@ const ctx: ToolContext = { mode: "local", docsDir };
|
|
|
37
38
|
const server = new Server(
|
|
38
39
|
{
|
|
39
40
|
name: "emdee",
|
|
40
|
-
version:
|
|
41
|
+
version: pkg.version,
|
|
41
42
|
},
|
|
42
43
|
{
|
|
43
44
|
capabilities: { tools: {} },
|