Claude Skill

geml

Address a document by its BLOCKS instead of reading it whole. Use for any long Markdown or documentation file — README, spec, guide, design doc, changelog — when the job is to find where something is documented, read one section, or change one section: `geml list`, `geml find` an

LLM Mart · 0 points · 1 views 13 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download geml-spec-geml-integrations_claude-plugin_skills_geml-0e1662c.zip · 15 KB
Part of geml-spec/geml — 12 skills

Install

skills CLI npx skills add https://github.com/geml-spec/geml/tree/main/integrations/claude-plugin/skills/geml
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install geml-spec-geml@llmmart
Git git clone https://github.com/geml-spec/geml.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole geml-spec/geml collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Reading and editing documents blockwise

Two jobs. The first is the common one, and it needs none of GEML's syntax.

A document that is NOT GEML — use this as a read layer

geml list, geml find and geml get read Markdown directly. Use them to locate and read one block of a long document. Nothing is converted, nothing is written, and the file stays exactly the Markdown it already was.

Only when you would otherwise read the whole file to change part of it. If the file is short, or you already know the exact string to replace, open it the ordinary way — the round trip costs more than it saves. What is saved is only ever the part you did not have to read: on this project's own docs a list map runs 5–11% of the file it maps, but on a changelog of many small sections it is 37%.

  1. geml list <file> — every block: address, kind, line range.
  2. geml find "<text>" <file> — which block holds those words, answered as an ADDRESS rather than a line number, so it survives the next edit. (On a file that is not .geml this needs 1.7.5 or newer; older builds report no match rather than an error, so if a search you expect to hit comes back empty, use list.)
  3. geml get <file> '<address>' — that block and nothing else.
  4. Change it. Two ways, and the choice is about consent, not capability:
    • Ordinary file-editing tool, using the text from step 3 as the string to replace. The default: a document nobody asked to address by block is edited the way its author edits it.
    • geml set <file> '#id' …, when block-addressed editing is what was asked for — a knowledge base, a generated index, a log that only grows. Measured on a real vault: the frontmatter and every block you did not address come out byte-for-byte unchanged, and the body you write lands verbatim — no escaping, no reflowing. Read references/markdown-writes.md first; two of its rules are silent when broken.

Never convert a document to GEML, never leave a .gemlhistory beside one, and do not pitch the format: use the tool, report the change you made, and let the file stay what it is.

If geml --version does not answer, none of this is available. Read and edit the ordinary way, and do not tell anyone to install anything.

A project moving TO GEML

"This project's documents are GEML now" means new documents are authored as .geml — notes, plans, findings, reports — in one directory (docs/geml/ unless the project says otherwise), one file per topic, with an index.geml saying what is there and why. It does not mean converting what is already written, and nobody has to say "leave the existing files alone" for that to hold.

Add, never replace. Writing a .geml version of a document is not licence to delete the Markdown it was drawn from — however completely the content was carried across, and whatever a "one home per topic" convention seems to imply. Deleting a file is a request a person makes, never an inference from a convention. When both exist, say in each what it is for and name one of them as the place a given fact is maintained: two documents describing a project is fine, two documents maintaining the same fact is what drifts.

A GEML document — get the syntax right

GEML expresses every kind of structured content — code, tables, diagrams, math, callouts, metadata — through one primitive: the typed block (=== <type> {#id .class key=val}===). Always finish by validating: a GEML file is correct only when geml check reports no error diagnostics (exit 0).

Golden rules (the things that are easy to get wrong)

  1. Fences are runs of = (≥3). A block closes at a = run of exactly the opening length, or — when the block has an #id — at the labeled fence === #id (any = run ≥3 followed by the id; no length counting).
  2. Nest with longer fences. A body containing === lines needs a longer outer fence: ==== wraps ===. Careful: a same-length bare === in the body closes the block even if you intend a labeled close — the labeled close only spares you length-counting, it does NOT protect same-length inner fences.
  3. Headings are ATX # only (#######). No setext underlines, no --- breaks, no YAML frontmatter — metadata is a === meta block, and the document TITLE lives there (title = "…"), not in an H1. A heading may carry a stable explicit id: ## Title {#sec}.
  4. Give every section a stable {#id}## Findings {#findings} — then keep ids unique per document, with every reference resolving: [t](#id), [[#id]], [^id], src=, data=, other.geml#id. An unresolved reference is a build error. Naming them is the part that pays later: a document with no ids costs what Markdown costs, because there is nothing for geml get to read or geml set to replace short of the file.
  5. No raw HTML. Notes → === note, comments → %% lines, hidden content → {hidden}, addressable prose → === text, verified data → === data (json/jsonl; code shows text, data IS data).

Validate every time

geml check file.geml          # diagnostics + exit code only; exit 0 = correct
geml check --json file.geml   # machine-readable diagnostics array

If geml is not on PATH: npm i -g @geml/geml (package @geml/geml, command geml), or run without installing via npx -y @geml/geml check file.geml. Inside the geml-spec repo prefer the local build: node geml-parser/dist/geml.js <args>. If no parser is reachable, follow the golden rules and validate once it is.

geml skill install sets all of this up user-global, and installs this text into whatever other agent tools it detects — a tool's directory has to be there already; none is ever created for you. --dry-run shows what it would do.

Work blockwise (agent editing)

geml list    file.geml                # CALL THIS FIRST — every block, its address, kind, lines
geml find    "text" file|dir          # search block CONTENT -> file<TAB>address (exit 1 = no hit)
                                      # a NAMED file is searched whatever its extension (.md too);
                                      # a directory walks *.geml and *.md
geml get     file.geml '#id'          # read ONE block (a heading id = its whole section)
geml set     file.geml '#id' --in f   # replace ONE block (re-parsed; never writes a broken doc)
geml history save file.geml -m "…"    # snapshot to .gemlhistory after each meaningful edit
geml revert  file.geml '#id'          # roll ONE block back (--rev -2 | changed | <rev-id>)

Address a block, never a line range: #id · '## Heading' (its whole section) · L27-58 (the smallest block holding those lines — how a line number from an editor, a linter or a diff hunk becomes an address). list and find print addresses that paste straight into the others, so neither grep nor a line count is needed to locate anything.

The rest is one geml get away in the reference below, and stays there because it is needed rarely and this page is read every time: the remaining address forms in #cli, and in #editing the three ways to cut a section (--head/--intro/--body), the experimental replace, and what a write that drops blocks does.

Full reference — pull ONE section, not the whole file

references/authoring.geml (under this skill's base directory) holds the detailed reference. Fetch just the section you need:

geml get <skill-base>/references/authoring.geml '#tables'
section covers
#typed-block block anatomy, attribute object, examples of every registered type
#tables pipe/CSV bodies of FACTS, delim=, printf display — and the view that derives over one: compute=, summary=, where=, order=, limit=, select=, by=/aggregate=
#charts geml-chart diagrams bound to a table via data=#id
#data the data block — value tree, json/jsonl formats, blind append, chart binding
#inline inline markup, links/refs/footnotes, task lists, media embeds
#hidden %% comments, {hidden}, {{key}} interpolation, === embed
#cli every CLI verb — get/set/add/delete/rename, --to conversion, check
#editing the blockwise editing loop + .gemlhistory versioning
#project-config carrying a project's Claude config docs in GEML, quietly
#checklist full pre-flight authoring checklist
Files (geml)
  • references
    • authoring.geml 19.6 KB · in bundle
    • markdown-writes.md 4.7 KB
      # Writing to a Markdown file with `geml set`
      
      `geml set` and `geml add` edit one block of a `.md` in place. Nothing is
      converted: the file stays the Markdown it was, and a reader who does not have
      `geml` sees no trace of it.
      
      Everything below was measured against real documents, not read off the help
      text. Two of the rules are **silent** when broken — no error, exit code 0 — and
      they are the reason to read this before the first write rather than after.
      
      ## What holds
      
      **A write is surgical.** After `geml set <file> '#id' --body --in -`, the YAML
      frontmatter and every block other than `#id` are byte-for-byte what they were.
      The verb splices; it does not re-serialize the file.
      
      **The body lands verbatim.** Whatever you write goes in as typed — a
      `> [!tip]` callout, a `[[wikilink]]`, a `` ```dataview `` fence, a table. No
      escaping, no reflowing, no normalization of your Markdown to anyone else's
      taste.
      
      **A broken result is refused.** The file is re-parsed before the write lands;
      if the result would not parse, nothing is written.
      
      **Heading addresses are stable.** `## Entities` is `#entities` on every run and
      every platform, and it survives anything happening above it. That is the whole
      reason to address a file this way instead of by line number.
      
      ## The five that bite
      
      ### 1. Never `set` the frontmatter block — it destroys the frontmatter
      
      Frontmatter is not a typed block in Markdown; it is an anonymous prose block,
      and its **closing `---` is part of that block's body**. Replace the body and the
      closer is gone, leaving an opener with nothing to close it — no properties at
      all, for every tool that reads them.
      
      Change frontmatter with an ordinary editor. This is silent: exit code 0.
      
      ### 2. `--body` is for a heading. On a prose block it APPENDS
      
      A heading's block is a heading line plus a body. A prose block is body all the
      way down, so it has no separate body to set — `get '#x' --body` on one comes
      back empty, and `set --body` writes into that emptiness, which lands **after**
      the prose already there. Nothing is removed.
      
      | target | `set '#id'` | `set '#id' --body` |
      |---|---|---|
      | a heading block | refused: *content is prose, not a block — use --body* | replaces the section body, keeps the heading line |
      | a prose block | replaces it | **appends to it, silently** |
      
      The rule is the opposite of what one habit would give you, and picking wrong
      fails loudly one way and quietly the other. `geml list` prints the kind in its
      second column — read it before choosing. This is the other silent one.
      
      ### 3. A `[[name#anchor]]` link is a GEML reference, and is checked
      
      `[[file#id]]` is GEML's own cross-document reference syntax, resolved when a
      write lands. A wiki-style `[[Note#Heading]]` has the same shape, and there is
      usually no document called `Note` — only `Note.md` — so the reference does not
      resolve and **the write is refused**. Plain `[[Note]]`, `[[Note|alias]]` and
      `![[image.png]]` are fine, and `[[Note.md#Heading]]` passes.
      
      Refused, note — not mangled. Nothing is written.
      
      ### 4. Repeated heading text makes the whole file unwritable
      
      Two `## Added` headings derive the same id. The guard judges the **result** of a
      write, and the result still carries the collision, so the write is refused —
      including one in a section nowhere near it, and the message names the collision
      rather than your edit. A Keep-a-Changelog file cannot be edited this way at all.
      
      ```sh
      geml check <file>          # exit 0 = writable
      ```
      
      ### 5. `--in <file>` is not "read this text"
      
      `--in F` means *take block `#id` from file F*. Raw text goes in on **stdin**:
      
      ```sh
      printf '…' | geml set page.md '#id' --body --in -      # right
      geml set page.md '#id' --body --in fragment.txt        # looks for #id INSIDE fragment.txt
      ```
      
      ## Smaller things worth knowing
      
      - `find` is a **literal substring**, not a pattern. `Hot.Cache` does not match
        "Hot Cache". Case-insensitive unless `--case`.
      - `find --head` shows **one line per block**. A block with thirteen matches
        reports one; `find` locates, `get` reads.
      - `set --body` replaces a trailing `---` rule too, if the section ends with one.
      - The blank line after a heading is not re-inserted. Cosmetic; renderers do not
        care, a diff does.
      - A directory walk skips hidden directories. A tree that hides sources in
        `.raw/` must name that directory: `geml find 'x' notes .raw`.
      - `@…` addresses are content hashes and change when the content does. Fine to
        read from a fresh `list`; never store one, and never write to one.
      
      ## Do not convert the file
      
      `geml <page>.md --to geml` and back is lossy for anything beyond plain
      Markdown — frontmatter lists collapse, `[[…]]` comes back escaped, thematic
      breaks are dropped. The point of addressing a Markdown file is that it **stays
      Markdown**.
      
  • SKILL.md 9.2 KB
    ---
    name: geml
    description: >-
      Address a document by its BLOCKS instead of reading it whole. Use for any long
      Markdown or documentation file — README, spec, guide, design doc, changelog —
      when the job is to find where something is documented, read one section, or
      change one section: `geml list`, `geml find` and `geml get` read Markdown
      directly and hand back the one block that matters, leaving the file the
      Markdown it already was. Skip it when the whole file is short enough to read
      anyway. Use it also to read, author, edit or validate GEML itself — .geml
      files, .gemlhistory sidecars, typed blocks, === fences, geml-chart, converting
      Markdown to GEML — where the output must parse cleanly (zero error
      diagnostics) against the reference parser.
    ---
    
    # Reading and editing documents blockwise
    
    Two jobs. The first is the common one, and it needs none of GEML's syntax.
    
    ## A document that is NOT GEML — use this as a read layer
    
    `geml list`, `geml find` and `geml get` read **Markdown** directly. Use them to
    locate and read one block of a long document. Nothing is converted, nothing is
    written, and the file stays exactly the Markdown it already was.
    
    **Only when you would otherwise read the whole file to change part of it.** If
    the file is short, or you already know the exact string to replace, open it the
    ordinary way — the round trip costs more than it saves. What is saved is only
    ever the part you did not have to read: on this project's own docs a `list` map
    runs 5–11% of the file it maps, but on a changelog of many small sections it is
    37%.
    
    1. `geml list <file>` — every block: address, kind, line range.
    2. `geml find "<text>" <file>` — which block holds those words, answered as an
       ADDRESS rather than a line number, so it survives the next edit. (On a file
       that is not `.geml` this needs 1.7.5 or newer; older builds report no match
       rather than an error, so if a search you expect to hit comes back empty,
       use `list`.)
    3. `geml get <file> '<address>'` — that block and nothing else.
    4. Change it. Two ways, and the choice is about **consent**, not capability:
       - **Ordinary file-editing tool**, using the text from step 3 as the string to
         replace. The default: a document nobody asked to address by block is edited
         the way its author edits it.
       - **`geml set <file> '#id' …`**, when block-addressed editing is what was
         asked for — a knowledge base, a generated index, a log that only grows.
         Measured on a real vault: the frontmatter and every block you did not
         address come out **byte-for-byte unchanged**, and the body you write lands
         verbatim — no escaping, no reflowing. Read
         [`references/markdown-writes.md`](references/markdown-writes.md) first; two
         of its rules are silent when broken.
    
    Never convert a document to GEML, never leave a `.gemlhistory` beside one, and
    do not pitch the format: use the tool, report the change you made, and let the
    file stay what it is.
    
    If `geml --version` does not answer, none of this is available. Read and edit
    the ordinary way, and do not tell anyone to install anything.
    
    ## A project moving TO GEML
    
    "This project's documents are GEML now" means new documents are authored as
    `.geml` — notes, plans, findings, reports — in one directory (`docs/geml/`
    unless the project says otherwise), one file per topic, with an `index.geml`
    saying what is there and why. It does not mean converting what is already
    written, and nobody has to say "leave the existing files alone" for that to
    hold.
    
    **Add, never replace.** Writing a `.geml` version of a document is not licence
    to delete the Markdown it was drawn from — however completely the content was
    carried across, and whatever a "one home per topic" convention seems to imply.
    Deleting a file is a request a person makes, never an inference from a
    convention. When both exist, say in each what it is for and name one of them as
    the place a given fact is maintained: two documents describing a project is
    fine, two documents maintaining the same fact is what drifts.
    
    ## A GEML document — get the syntax right
    
    GEML expresses **every** kind of structured content — code, tables, diagrams,
    math, callouts, metadata — through **one** primitive: the **typed block**
    (`=== <type> {#id .class key=val}` … `===`). Always finish by **validating**: a
    GEML file is correct only when `geml check` reports **no error diagnostics**
    (exit 0).
    
    ## Golden rules (the things that are easy to get wrong)
    
    1. **Fences are runs of `=` (≥3).** A block closes at a `=` run of **exactly
       the opening length**, or — when the block has an `#id` — at the labeled
       fence `=== #id` (any `=` run ≥3 followed by the id; no length counting).
    2. **Nest with longer fences.** A body containing `===` lines needs a
       **longer** outer fence: `====` wraps `===`. Careful: a same-length bare
       `===` in the body closes the block even if you intend a labeled close —
       the labeled close only spares you length-counting, it does NOT protect
       same-length inner fences.
    3. **Headings are ATX `#` only** (`#`…`######`). No setext underlines, no
       `---` breaks, no YAML frontmatter — metadata is a `=== meta` block, and the
       document TITLE lives there (`title = "…"`), not in an H1. A heading may
       carry a stable explicit id: `## Title {#sec}`.
    4. **Give every section a stable `{#id}`** — `## Findings {#findings}` — then
       keep ids unique per document, with **every reference resolving**:
       `[t](#id)`, `[[#id]]`, `[^id]`, `src=`, `data=`, `other.geml#id`. An
       unresolved reference is a build **error**. Naming them is the part that pays
       later: a document with no ids costs what Markdown costs, because there is
       nothing for `geml get` to read or `geml set` to replace short of the file.
    5. **No raw HTML.** Notes → `=== note`, comments → `%%` lines, hidden content
       → `{hidden}`, addressable prose → `=== text`, verified data → `=== data`
       (json/jsonl; `code` shows text, `data` IS data).
    
    ## Validate every time
    
    ```sh
    geml check file.geml          # diagnostics + exit code only; exit 0 = correct
    geml check --json file.geml   # machine-readable diagnostics array
    ```
    
    If `geml` is not on PATH: `npm i -g @geml/geml` (package `@geml/geml`, command
    `geml`), or run without installing via `npx -y @geml/geml check file.geml`.
    Inside the geml-spec repo prefer the local build:
    `node geml-parser/dist/geml.js <args>`. If no parser is reachable, follow the
    golden rules and validate once it is.
    
    `geml skill install` sets all of this up user-global, and installs this text
    into whatever other agent tools it detects — a tool's directory has to be there
    already; none is ever created for you. `--dry-run` shows what it would do.
    
    ## Work blockwise (agent editing)
    
    ```sh
    geml list    file.geml                # CALL THIS FIRST — every block, its address, kind, lines
    geml find    "text" file|dir          # search block CONTENT -> file<TAB>address (exit 1 = no hit)
                                          # a NAMED file is searched whatever its extension (.md too);
                                          # a directory walks *.geml and *.md
    geml get     file.geml '#id'          # read ONE block (a heading id = its whole section)
    geml set     file.geml '#id' --in f   # replace ONE block (re-parsed; never writes a broken doc)
    geml history save file.geml -m "…"    # snapshot to .gemlhistory after each meaningful edit
    geml revert  file.geml '#id'          # roll ONE block back (--rev -2 | changed | <rev-id>)
    ```
    
    Address a block, never a line range: `#id` · `'## Heading'` (its whole section)
    · `L27-58` (the smallest block holding those lines — how a line number from an
    editor, a linter or a diff hunk becomes an address). `list` and `find` print
    addresses that paste straight into the others, so neither `grep` nor a line
    count is needed to locate anything.
    
    The rest is one `geml get` away in the reference below, and stays there because
    it is needed rarely and this page is read every time: the remaining address
    forms in `#cli`, and in `#editing` the three ways to cut a section
    (`--head`/`--intro`/`--body`), the experimental `replace`, and what a write that
    drops blocks does.
    
    ## Full reference — pull ONE section, not the whole file
    
    `references/authoring.geml` (under this skill's base directory) holds the
    detailed reference. Fetch just the section you need:
    
    ```sh
    geml get <skill-base>/references/authoring.geml '#tables'
    ```
    
    | section | covers |
    |---|---|
    | `#typed-block` | block anatomy, attribute object, examples of every registered type |
    | `#tables` | pipe/CSV bodies of FACTS, `delim=`, printf display — and the `view` that derives over one: `compute=`, `summary=`, `where=`, `order=`, `limit=`, `select=`, `by=`/`aggregate=` |
    | `#charts` | `geml-chart` diagrams bound to a table via `data=#id` |
    | `#data` | the `data` block — value tree, `json`/`jsonl` formats, blind append, chart binding |
    | `#inline` | inline markup, links/refs/footnotes, task lists, media embeds |
    | `#hidden` | `%%` comments, `{hidden}`, `{{key}}` interpolation, `=== embed` |
    | `#cli` | every CLI verb — get/set/add/delete/rename, `--to` conversion, check |
    | `#editing` | the blockwise editing loop + `.gemlhistory` versioning |
    | `#project-config` | carrying a project's Claude config docs in GEML, quietly |
    | `#checklist` | full pre-flight authoring checklist |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related