@geml/geml 1.5.1 → 1.7.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +248 -217
  3. package/codemap/adapters/crg.mjs +120 -120
  4. package/codemap/adapters/joern.mjs +131 -131
  5. package/codemap/adapters/scip.mjs +658 -658
  6. package/codemap/browser-stub.mjs +34 -29
  7. package/codemap/build.mjs +609 -609
  8. package/codemap/cross-stack.mjs +303 -303
  9. package/codemap/detect.mjs +399 -399
  10. package/codemap/emit.mjs +480 -480
  11. package/codemap/entries.mjs +129 -129
  12. package/codemap/exclude.mjs +52 -52
  13. package/codemap/find.mjs +49 -49
  14. package/codemap/foldings.mjs +110 -110
  15. package/codemap/joern-export.sc +83 -83
  16. package/codemap/mcp-server.mjs +431 -431
  17. package/codemap/normalize.mjs +275 -275
  18. package/codemap/recipe-trust.mjs +103 -103
  19. package/codemap/refresh.mjs +310 -310
  20. package/codemap/render-all.mjs +77 -77
  21. package/codemap/serve.mjs +585 -585
  22. package/codemap/sfc-virtualize.mjs +367 -367
  23. package/codemap/verify.mjs +155 -148
  24. package/dist/chart.d.ts +1 -0
  25. package/dist/chart.js +4 -1
  26. package/dist/diagnostics.d.ts +1 -1
  27. package/dist/diagnostics.js +16 -0
  28. package/dist/geml.d.ts +5 -1
  29. package/dist/geml.js +1277 -283
  30. package/dist/history.d.ts +11 -8
  31. package/dist/history.js +20 -15
  32. package/dist/mcp.d.ts +1 -1
  33. package/dist/mcp.js +108 -38
  34. package/dist/render-html.d.ts +5 -0
  35. package/dist/render-html.js +45 -36
  36. package/dist/render.d.ts +1 -0
  37. package/dist/render.js +172 -136
  38. package/dist/selector.d.ts +55 -0
  39. package/dist/selector.js +112 -0
  40. package/dist/serialize.js +12 -0
  41. package/dist/table.js +27 -1
  42. package/dist/to-md.js +5 -0
  43. package/package.json +67 -66
  44. package/skill/SKILL.md +82 -0
  45. package/skill/references/authoring.geml +333 -0
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 GEML contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GEML contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,217 +1,248 @@
1
- <p align="center">
2
- <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/geml-spec/geml/main/docs/assets/logo/geml-logo-dark.svg">
4
- <img src="https://raw.githubusercontent.com/geml-spec/geml/main/docs/assets/logo/geml-logo-light.svg" alt="GEML" width="300">
5
- </picture>
6
- </p>
7
-
8
- # @geml/geml
9
-
10
- The reference parser, validator, renderer, and CLI for **GEML** (General
11
- Expressive Markup Language) — **one format, two readers.** People and AI agents
12
- co-write the same document: plain text that stays legible for people, and
13
- **addressable, verifiable, and versioned** for machines.
14
-
15
- Every kind of structured content — code, tables, diagrams, math, callouts,
16
- metadata — rides on **one** primitive, the typed block:
17
-
18
- ```
19
- === code {#hello lang=python}
20
- print("hi")
21
- ===
22
- ```
23
-
24
- - **Addressable** — every block has an `#id`; `geml get` / `geml set '#id'`
25
- read or patch one section without re-emitting the whole file (on this repo's
26
- own spec, ~**66× less context** than shipping the whole document).
27
- - **Verifiable** references are checked at build time (a dangling `#id` is an
28
- error, not a silent dead link), and the parser emits a document-model JSON
29
- with a `diagnostics` array, so agents and CI get a structured pass/fail signal.
30
- - **Versioned** `geml history` and `geml revert` snapshot and rewind
31
- revisions over a plain-text `.gemlhistory` sidecar.
32
-
33
- Try the format in the [playground](https://geml-spec.github.io/geml/playground/)
34
- no install. Full pitch, spec, and format comparison live in the
35
- [repository](https://github.com/geml-spec/geml).
36
-
37
- ## Install
38
-
39
- ```sh
40
- npm install -g @geml/geml # global CLI — installs the `geml` command
41
- # or, per project:
42
- npm install @geml/geml # library + local bin
43
- ```
44
-
45
- Requires Node ≥ 22.
46
-
47
- ## CLI
48
-
49
- The CLI is built around one question: can a single agent author and maintain an
50
- entire `.geml` file from the command line create, add, edit, delete, and copy
51
- blocks in from other files? Three tests keep the command set honest:
52
-
53
- - **Complete** — every step of a document's life has a verb, so an agent never
54
- rewrites the whole file to change one block.
55
- - **Ergonomic** few flags, sensible defaults, and pipeline-friendly I/O, so
56
- multi-step edits chain without ceremony.
57
- - **Consistent** behavior is uniform and predictable: name a target `#id` and
58
- the content adopts it, every write is guarded, a file is edited in place while
59
- `-` streams to stdout.
60
-
61
- The command set borrows from two settled models rather than inventing one, and
62
- they overlap where it counts. **A document is a table**: a block is a row, its
63
- `#id` is the primary key (unique per document), and `[[#id]]`/`[t](#id)`/`[^id]`/
64
- `data=#id` are foreign keys so `get`/`add`/`set`/`delete` are
65
- SELECT/INSERT/UPDATE/DELETE. **A block is also a resource** at a URI-like address
66
- (`file#id`), named before the operation, the way REST puts the noun first. Both
67
- models agree on a small orthogonal verb set instead of a method per use case, and
68
- they agree on idempotence: `set` and `delete` are idempotent (deleting a missing
69
- id is a no-op, so a retry is safe), `add` is not.
70
-
71
- Where they diverge, each covers what the other cannot. The relational view names
72
- the integrity rules: the write guard is a constraint check with rollback, and
73
- `delete` merely *warning* about references it leaves dangling is a **deferred**
74
- foreign-key check, not `ON DELETE RESTRICT`. It also explains `rename`, the verb
75
- most open to "can this be cut" a **primary-key update with a cascading
76
- foreign-key rewrite**, irreducible because `delete` + `add` would leave every
77
- reference dangling and nothing else rewrites references in bulk. HTTP has no
78
- method for that at all. The REST view supplies what a database deliberately does
79
- not: every call is **stateless** no session, no current document, no config
80
- file, no environment variablewhich is what lets calls be retried,
81
- parallelized, and piped.
82
-
83
- Both pay off in undo. Because the verbs are orthogonal, each edit has exactly one
84
- inverse, so `revert` never needs to know which verb made a change it reconciles
85
- a block to a revision in three cases (content changed, row missing, row extra)
86
- and there is no fourth, while `rename` is its own inverse and needs no history at
87
- all. A wider, RPC-shaped verb set (`replace`, `move`, `merge`, `split`, …) would
88
- need a per-verb inverse and an operation log to pick one — an undo-stack engine
89
- instead of three branches. Full rationale:
90
- [`docs/design/specs/2026-07-24-geml-block-mutation-cli-design.md`](../docs/design/specs/2026-07-24-geml-block-mutation-cli-design.md).
91
-
92
- Every command reads a file path, or `-` for stdin. Exit codes: `0` ok ·
93
- `1` document/operation error · `2` usage error.
94
-
95
- ```sh
96
- geml doc.geml # document-model JSON (default --to json)
97
- geml doc.geml --to md|html|geml # convert; geml notes.md -> GEML
98
- geml get doc.geml ['#id'] # list addressable ids, or print one block (heading id = its section)
99
- geml set doc.geml '#id' [--head|--body] [--in F[#src]] # replace a block's content (id kept)
100
- geml add doc.geml (--append|--before #id|--after #id) [--in F[#src]] # insert a fragment
101
- geml delete doc.geml '#id' ['#id2' …] # remove one or more blocks
102
- geml rename doc.geml '#old' '#new' # rename an id + every reference to it
103
- geml revert doc.geml '#id' [--rev -1] # undo a block: splice / resurrect / remove
104
- geml check doc.geml [--root <dir>] # validate only: diagnostics + exit code (--json for the array)
105
- geml history <commit|verify|show|restore|log> doc.geml [...] # .gemlhistory version sidecar
106
- geml codemap <build|verify|render|serve|refresh|find|mcp> # your codebase's call graph as GEML docs
107
- geml --help | --version # --version --json prints {"parser","spec"}
108
- ```
109
-
110
- The agent loop: `geml get` a block → `set`/`add`/`delete`/`rename` it →
111
- `geml check` `geml history commit` small, precise, verifiable edits.
112
-
113
- Conversion is one entry — `geml <file> [--to json|html|md|geml]`; the input
114
- format is inferred (`--from` overrides > extension > GEML), the target is `--to`
115
- (default: GEML → JSON, Markdown → GEML), and `-o` names the output path.
116
-
117
- `set` and `add` take their content from `--in F` (F's block whose id equals the
118
- target), `--in F#src` (F's block `#src`), or stdin (raw bytes). `set` **replaces
119
- a whole block** and normalizes the content's id to the target — so you can fork
120
- any block into this slot without hand-editing its id (`--head` swaps just the
121
- head line, `--body` just the body). `add` **inserts a fragment** (one or more
122
- blocks, or bare prose) at `--append` / `--before #id` / `--after #id`, keeping
123
- the content's own ids (a collision is refused). `delete` removes one or more
124
- ids; `rename` rewrites an id's declaration and every reference to it.
125
-
126
- Mutations (`set`/`add`/`delete`/`rename`) write the **whole updated document**:
127
- in place when the input is a file, or to **stdout** when the input is `-`; `-o`
128
- redirects the write (`-o -` forces stdout), so edits pipe cleanly. Every write
129
- is guarded re-parsed and refused if it would break the document or drop an id
130
- (a reference left dangling by `delete` is a warning, not a refusal; `geml check`
131
- flags it later).
132
-
133
- Undo is `revert`, which reconciles one block to a past revision (`--rev`, default
134
- `-1`): it **splices** back changed content, **resurrects** a deleted block (placed
135
- by its old neighbours, or `--append`/`--before`/`--after`), or **removes** a block
136
- that did not exist then. So each forward edit has an inverse:
137
-
138
- | forward edit | undo |
139
- |---|---|
140
- | `set #id` | `revert #id` (splice) |
141
- | `delete #id` | `revert #id` (resurrect) |
142
- | `add #id` | `revert #id` (remove) or `delete #id` |
143
- | `rename #old #new` | `rename #new #old` (self-inverse) |
144
-
145
- `revert` reads the `.gemlhistory` sidecar, so `set`/`delete`/`add` undo needs a
146
- prior `geml history commit`; `rename` is its own inverse and needs no history.
147
-
148
- A **heading's** `#id` addresses its whole **section** the heading line through
149
- the line before the next heading of the same-or-higher level so the prose
150
- under a heading is block-editable with no extra syntax.
151
- Spans overlap: blocks nested in the section keep their own ids, and a `set` on
152
- the section that drops one of them is refused by the guard. `get --json` on a
153
- heading covers the same content as the raw span: a section envelope
154
- `{kind:"section", id, level, blocks:[heading, …its section's blocks]}` (a
155
- block/footnote id still prints its single model node). `--head` narrows
156
- `get`/`set`/`revert` to ANY id's head line — a heading's line, or a typed
157
- block's opening fence line, so an agent renames a heading or edits a block's
158
- attributes (caption, compute, …) without touching the body. Convention: keep
159
- the document title in `=== meta` (`title = "…"`), not an H1 a lone top-level
160
- `#` section is the whole document, the telltale that it is really a title.
161
-
162
- ## MCP Server
163
-
164
- This package includes a standard Model Context Protocol (MCP) server that exposes GEML document CRUD operations. It runs locally and supports Windows, macOS, and Linux.
165
-
166
- To connect it to an MCP-compatible client, provide the `npx` execution command and specify the `--root` argument (the directory containing your `.geml` files).
167
-
168
- ### Claude Desktop
169
- Add to your `claude_desktop_config.json`:
170
- ```json
171
- {
172
- "mcpServers": {
173
- "geml": {
174
- "command": "npx",
175
- "args": [
176
- "-y",
177
- "@geml/geml@latest",
178
- "mcp",
179
- "--root",
180
- "/absolute/path/to/your/docs"
181
- ]
182
- }
183
- }
184
- }
185
- ```
186
-
187
- ### Claude Code / CLI Clients
188
- Run the following command to add the server:
189
- ```sh
190
- /mcp add npx -y @geml/geml@latest mcp --root /absolute/path/to/your/docs
191
- ```
192
-
193
- ## Library
194
-
195
- ```js
196
- import { parse, serialize, renderHtml, gemlToMd, mdToGeml } from "@geml/geml";
197
-
198
- const doc = parse(src); // { kind:"document", children, ids, diagnostics }
199
- const ok = !doc.diagnostics.some(d => d.severity === "error");
200
- const html = renderHtml(doc); // one self-contained HTML string
201
- const md = gemlToMd(doc).md; // GitHub-Flavored Markdown (lossy)
202
- const geml = mdToGeml(markdown).geml; // the inverse
203
- const canonical = serialize(doc); // GEML text; parse(serialize(parse(x))) is stable
204
- ```
205
-
206
- `parse(src, { resolveDoc })` enables cross-document reference checking — pass a
207
- function that returns another file's source by path (or `null`).
208
-
209
- ## Documentation
210
-
211
- Full normative spec, history-sidecar spec, and format comparison live in the
212
- [repository](https://github.com/geml-spec/geml). The spec is itself
213
- written in GEML (`GEML-spec.geml`) and parsed clean on every test run.
214
-
215
- ## License
216
-
217
- MIT.
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/geml-spec/geml/main/docs/assets/logo/geml-logo-dark.svg">
4
+ <img src="https://raw.githubusercontent.com/geml-spec/geml/main/docs/assets/logo/geml-logo-light.svg" alt="GEML" width="300">
5
+ </picture>
6
+ </p>
7
+
8
+ # @geml/geml
9
+
10
+ The reference parser, validator, renderer, and CLI for **GEML** (General
11
+ Expressive Markup Language) — **one format, two readers.** People and AI agents
12
+ co-write the same document: plain text that stays legible for people, and
13
+ **addressable, verifiable, and versioned** for machines.
14
+
15
+ Every kind of structured content — code, tables, diagrams, math, callouts,
16
+ metadata — rides on **one** primitive, the typed block:
17
+
18
+ ```
19
+ === code {#hello lang=python}
20
+ print("hi")
21
+ ===
22
+ ```
23
+
24
+ - **Addressable** — every block can be named: an `#id`, or a content address for
25
+ the ones nobody named; `geml get` / `geml set '<selector>'`
26
+ read or patch one section without re-emitting the whole file (on this repo's
27
+ own spec, ~**66× less context** than shipping the whole document).
28
+ - **Verifiable** references are checked at build time (a dangling `#id` is an
29
+ error, not a silent dead link), and the parser emits a document-model JSON
30
+ with a `diagnostics` array, so agents and CI get a structured pass/fail signal.
31
+ - **Versioned** `geml history` and `geml revert` snapshot and rewind
32
+ revisions over a plain-text `.gemlhistory` sidecar.
33
+
34
+ Try the format in the [playground](https://geml-spec.github.io/geml/playground/)
35
+ — no install. Full pitch, spec, and format comparison live in the
36
+ [repository](https://github.com/geml-spec/geml).
37
+
38
+ ## Install
39
+
40
+ ```sh
41
+ npm install -g @geml/geml # global CLI — installs the `geml` command
42
+ # or, per project:
43
+ npm install @geml/geml # library + local bin
44
+ ```
45
+
46
+ Requires Node ≥ 22.
47
+
48
+ ## CLI
49
+
50
+ The CLI is built around one question: can a single agent author and maintain an
51
+ entire `.geml` file from the command line create, add, edit, delete, and copy
52
+ blocks in from other files? Three tests keep the command set honest:
53
+
54
+ - **Complete** every step of a document's life has a verb, so an agent never
55
+ rewrites the whole file to change one block.
56
+ - **Ergonomic** few flags, sensible defaults, and pipeline-friendly I/O, so
57
+ multi-step edits chain without ceremony.
58
+ - **Consistent** behavior is uniform and predictable: name a target `#id` and
59
+ the content adopts it, every write is guarded, a file is edited in place while
60
+ `-` streams to stdout.
61
+
62
+ The command set borrows from two settled models rather than inventing one, and
63
+ they overlap where it counts. **A document is a table**: a block is a row, its
64
+ `#id` is the primary key (unique per document), and `[[#id]]`/`[t](#id)`/`[^id]`/
65
+ `data=#id` are foreign keys so `get`/`add`/`set`/`delete` are
66
+ SELECT/INSERT/UPDATE/DELETE. **A block is also a resource** at a URI-like address
67
+ (`file#id`), named before the operation, the way REST puts the noun first. Both
68
+ models agree on a small orthogonal verb set instead of a method per use case, and
69
+ they agree on idempotence: `set` and `delete` are idempotent (deleting a missing
70
+ id is a no-op, so a retry is safe), `add` is not.
71
+
72
+ Where they diverge, each covers what the other cannot. The relational view names
73
+ the integrity rules: the write guard is a constraint check with rollback, and
74
+ `delete` merely *warning* about references it leaves dangling is a **deferred**
75
+ foreign-key check, not `ON DELETE RESTRICT`. It also explains `rename`, the verb
76
+ most open to "can this be cut" a **primary-key update with a cascading
77
+ foreign-key rewrite**, irreducible because `delete` + `add` would leave every
78
+ reference dangling and nothing else rewrites references in bulk. HTTP has no
79
+ method for that at all. The REST view supplies what a database deliberately does
80
+ not: every call is **stateless** no session, no current document, no config
81
+ file, no environment variable — which is what lets calls be retried,
82
+ parallelized, and piped.
83
+
84
+ Both pay off in undo. Because the verbs are orthogonal, each edit has exactly one
85
+ inverse, so `revert` never needs to know which verb made a change it reconciles
86
+ a block to a revision in three cases (content changed, row missing, row extra)
87
+ and there is no fourth, while `rename` is its own inverse and needs no history at
88
+ all. A wider, RPC-shaped verb set (`replace`, `move`, `merge`, `split`, …) would
89
+ need a per-verb inverse and an operation log to pick one — an undo-stack engine
90
+ instead of three branches. Full rationale:
91
+ [`docs/design/specs/2026-07-24-geml-block-mutation-cli-design.md`](../docs/design/specs/2026-07-24-geml-block-mutation-cli-design.md).
92
+
93
+ Every command reads a file path, or `-` for stdin. Exit codes: `0` ok ·
94
+ `1` document/operation error · `2` usage error.
95
+
96
+ ```sh
97
+ geml doc.geml # document-model JSON (default --to json)
98
+ geml doc.geml --to md|html|geml # convert; geml notes.md -> GEML
99
+ geml get doc.geml ['<selector>'] # list addressable blocks, or print what the selector matches
100
+ geml set doc.geml '<selector>' [--head|--body] [--in F[#src]] # replace ONE block's content
101
+ geml add doc.geml (--append|--before #id|--after #id) [--in F[#src]] # insert a fragment
102
+ geml delete doc.geml '#id' ['#id2' …] # remove one or more blocks
103
+ geml rename doc.geml '#old' '#new' # rename an id + every reference to it
104
+ geml revert doc.geml '#id' [--rev -1] # undo a block: splice / resurrect / remove
105
+ geml check doc.geml [--root <dir>] # validate only: diagnostics + exit code (--json for the array)
106
+ geml history <save|get|restore|verify> doc.geml [...] # .gemlhistory version sidecar (get = list revisions, or print one)
107
+ geml codemap <build|verify|render|serve|refresh|find|mcp> # your codebase's call graph as GEML docs
108
+ geml --help | --version # --version --json prints {"parser","spec"}
109
+ ```
110
+
111
+ The agent loop: `geml get` a block `set`/`add`/`delete`/`rename` it →
112
+ `geml check` → `geml history save` — small, precise, verifiable edits.
113
+
114
+ ### Selectors
115
+
116
+ `get` and `set` take the same selector, which is a **filter over blocks**:
117
+
118
+ | Selector | Matches |
119
+ |---|---|
120
+ | *(omitted)* | nothing — `get` **lists** every addressable block, one per line, by its shortest unique address |
121
+ | `#id` | that block. A heading id addresses its **whole section** |
122
+ | `'## Heading'` | a heading line copied out of the document, resolved to its id |
123
+ | `'=== note'` | **every** `note` block 0..N of them |
124
+ | `'=== note@a3f9c1d2'` | one block by CONTENT, for blocks that carry no `#id` |
125
+ | `'@a3f9c1d2'` | the same, with the type check dropped |
126
+
127
+ `get` answers with N contents when N match (document order, count on stderr);
128
+ `set` writes ONE block, so a selector matching several is refused (exit 2) with
129
+ the unique address of each candidate. `--head` is the head line, `--body` the
130
+ body; both round-trip `geml get f X --body | geml set f X --body` leaves the
131
+ file byte-identical.
132
+
133
+ A `@<hex>` **content address** is the first 8 hex of the SHA-256 of the block's
134
+ own text (line endings normalized to LF, no trailing newline), with `~1`, `~2`…
135
+ distinguishing byte-identical blocks. Read them out of `geml get doc.geml`
136
+ they are printed for every block that has no `#id`. Being content-derived, an
137
+ address **goes stale when the block changes** and then fails with exit 1 rather
138
+ than silently addressing a different block: it doubles as a precondition. That
139
+ also means `set` through one prints the new address on stderr. The exact hash
140
+ input is pinned in
141
+ [the selector design doc](../docs/design/specs/2026-08-04-geml-get-set-selector-design-change.md)
142
+ §3.4 so a second implementation computes the same values.
143
+
144
+ Conversion is one entry — `geml <file> [--to json|html|md|geml]`; the input
145
+ format is inferred (`--from` overrides > extension > GEML), the target is `--to`
146
+ (default: GEML JSON, Markdown GEML), and `-o` names the output path.
147
+
148
+ `set` and `add` take their content from `--in F` (F's block whose id equals the
149
+ target), `--in F#src` (F's block `#src`), or stdin (raw bytes). `set` **replaces
150
+ a whole block** and normalizes the content's id to the target — so you can fork
151
+ any block into this slot without hand-editing its id (`--head` swaps just the
152
+ head line, `--body` just the body). `add` **inserts a fragment** (one or more
153
+ blocks, or bare prose) at `--append` / `--before #id` / `--after #id`, keeping
154
+ the content's own ids (a collision is refused). `delete` removes one or more
155
+ ids; `rename` rewrites an id's declaration and every reference to it.
156
+
157
+ Mutations (`set`/`add`/`delete`/`rename`) write the **whole updated document**:
158
+ in place when the input is a file, or to **stdout** when the input is `-`; `-o`
159
+ redirects the write (`-o -` forces stdout), so edits pipe cleanly. Every write
160
+ is guarded re-parsed and refused if it would break the document or drop an id
161
+ (a reference left dangling by `delete` is a warning, not a refusal; `geml check`
162
+ flags it later).
163
+
164
+ Undo is `revert`, which reconciles one block to a past revision (`--rev`, default
165
+ `-1`): it **splices** back changed content, **resurrects** a deleted block (placed
166
+ by its old neighbours, or `--append`/`--before`/`--after`), or **removes** a block
167
+ that did not exist then. So each forward edit has an inverse:
168
+
169
+ | forward edit | undo |
170
+ |---|---|
171
+ | `set #id` | `revert #id` (splice) |
172
+ | `delete #id` | `revert #id` (resurrect) |
173
+ | `add #id` | `revert #id` (remove) — or `delete #id` |
174
+ | `rename #old #new` | `rename #new #old` (self-inverse) |
175
+
176
+ `revert` reads the `.gemlhistory` sidecar, so `set`/`delete`/`add` undo needs a
177
+ prior `geml history save`; `rename` is its own inverse and needs no history.
178
+
179
+ A **heading's** `#id` addresses its whole **section** — the heading line through
180
+ the line before the next heading of the same-or-higher level — so the prose
181
+ under a heading is block-editable with no extra syntax.
182
+ Spans overlap: blocks nested in the section keep their own ids, and a `set` on
183
+ the section that drops one of them is refused by the guard. `get --json` on a
184
+ heading covers the same content as the raw span: a section envelope
185
+ `{kind:"section", id, level, blocks:[heading, …its section's blocks]}` (a
186
+ block/footnote id still prints its single model node). `--head` narrows
187
+ `get`/`set`/`revert` to ANY id's head line — a heading's line, or a typed
188
+ block's opening fence line, so an agent renames a heading or edits a block's
189
+ attributes (caption, compute, …) without touching the body. Convention: keep
190
+ the document title in `=== meta` (`title = "…"`), not an H1 — a lone top-level
191
+ `#` section is the whole document, the telltale that it is really a title.
192
+
193
+ ## MCP Server
194
+
195
+ This package includes a standard Model Context Protocol (MCP) server that exposes GEML document CRUD operations. It runs locally and supports Windows, macOS, and Linux.
196
+
197
+ To connect it to an MCP-compatible client, provide the `npx` execution command and specify the `--root` argument (the directory containing your `.geml` files).
198
+
199
+ ### Claude Desktop
200
+ Add to your `claude_desktop_config.json`:
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "geml": {
205
+ "command": "npx",
206
+ "args": [
207
+ "-y",
208
+ "@geml/geml@latest",
209
+ "mcp",
210
+ "--root",
211
+ "/absolute/path/to/your/docs"
212
+ ]
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### Claude Code / CLI Clients
219
+ Run the following command to add the server:
220
+ ```sh
221
+ /mcp add npx -y @geml/geml@latest mcp --root /absolute/path/to/your/docs
222
+ ```
223
+
224
+ ## Library
225
+
226
+ ```js
227
+ import { parse, serialize, renderHtml, gemlToMd, mdToGeml } from "@geml/geml";
228
+
229
+ const doc = parse(src); // { kind:"document", children, ids, diagnostics }
230
+ const ok = !doc.diagnostics.some(d => d.severity === "error");
231
+ const html = renderHtml(doc); // one self-contained HTML string
232
+ const md = gemlToMd(doc).md; // GitHub-Flavored Markdown (lossy)
233
+ const geml = mdToGeml(markdown).geml; // the inverse
234
+ const canonical = serialize(doc); // GEML text; parse(serialize(parse(x))) is stable
235
+ ```
236
+
237
+ `parse(src, { resolveDoc })` enables cross-document reference checking — pass a
238
+ function that returns another file's source by path (or `null`).
239
+
240
+ ## Documentation
241
+
242
+ Full normative spec, history-sidecar spec, and format comparison live in the
243
+ [repository](https://github.com/geml-spec/geml). The spec is itself
244
+ written in GEML (`GEML-spec.geml`) and parsed clean on every test run.
245
+
246
+ ## License
247
+
248
+ MIT.