@geml/geml 1.8.2 → 1.8.4
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/LICENSE +21 -21
- package/README.md +288 -288
- package/codemap/adapters/crg.mjs +120 -120
- package/codemap/adapters/joern.mjs +131 -131
- package/codemap/adapters/scip.mjs +658 -658
- package/codemap/browser-stub.mjs +34 -34
- package/codemap/build.mjs +629 -629
- package/codemap/cross-stack.mjs +303 -303
- package/codemap/detect.mjs +399 -399
- package/codemap/emit.mjs +510 -510
- package/codemap/entries.mjs +129 -129
- package/codemap/exclude.mjs +56 -56
- package/codemap/find.mjs +49 -49
- package/codemap/foldings.mjs +110 -110
- package/codemap/joern-export.sc +83 -83
- package/codemap/mcp-server.mjs +434 -434
- package/codemap/normalize.mjs +275 -275
- package/codemap/recipe-trust.mjs +103 -103
- package/codemap/refresh.mjs +313 -313
- package/codemap/render-all.mjs +90 -90
- package/codemap/serve.mjs +585 -585
- package/codemap/sfc-virtualize.mjs +367 -367
- package/codemap/verify.mjs +158 -158
- package/dist/cli.js +129 -129
- package/dist/geml.js +73 -25
- package/dist/mcp.js +19 -19
- package/dist/render-html.js +35 -35
- package/dist/render.js +157 -157
- package/dist/serialize.js +7 -1
- package/dist/to-md.js +8 -1
- package/package.json +67 -67
- package/skill/SKILL.md +167 -167
- package/skill/references/authoring.geml +369 -369
package/package.json
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@geml/geml",
|
|
3
|
-
"version": "1.8.
|
|
4
|
-
"mcpName": "io.github.geml-spec/geml",
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"description": "CLI and parser for GEML, a plain-text document format where every block has an id — so an AI patches one block instead of rewriting the file; edits, reference checks and rollbacks are all per block. Ships an MCP server.",
|
|
9
|
-
"type": "module",
|
|
10
|
-
"bin": {
|
|
11
|
-
"geml": "dist/cli.js"
|
|
12
|
-
},
|
|
13
|
-
"main": "dist/geml.js",
|
|
14
|
-
"types": "dist/geml.d.ts",
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"codemap",
|
|
18
|
-
"skill",
|
|
19
|
-
"README.md",
|
|
20
|
-
"LICENSE"
|
|
21
|
-
],
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=22"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"geml",
|
|
27
|
-
"general expressive markup language",
|
|
28
|
-
"mcp",
|
|
29
|
-
"mcp-server",
|
|
30
|
-
"markup",
|
|
31
|
-
"markdown",
|
|
32
|
-
"parser",
|
|
33
|
-
"cli",
|
|
34
|
-
"document",
|
|
35
|
-
"typed-block",
|
|
36
|
-
"ai",
|
|
37
|
-
"agent",
|
|
38
|
-
"llm",
|
|
39
|
-
"addressable",
|
|
40
|
-
"versioning",
|
|
41
|
-
"docs",
|
|
42
|
-
"code-graph"
|
|
43
|
-
],
|
|
44
|
-
"repository": {
|
|
45
|
-
"type": "git",
|
|
46
|
-
"url": "git+https://github.com/geml-spec/geml.git",
|
|
47
|
-
"directory": "geml-parser"
|
|
48
|
-
},
|
|
49
|
-
"homepage": "https://github.com/geml-spec/geml#readme",
|
|
50
|
-
"bugs": {
|
|
51
|
-
"url": "https://github.com/geml-spec/geml/issues"
|
|
52
|
-
},
|
|
53
|
-
"scripts": {
|
|
54
|
-
"build": "tsc",
|
|
55
|
-
"test": "tsc && node test/all.mjs",
|
|
56
|
-
"parse": "node dist/geml.js",
|
|
57
|
-
"coverage": "tsc && c8 --all --include=dist/**/*.js --include=codemap/**/*.mjs --reporter=text --reporter=text-summary node test/all.mjs",
|
|
58
|
-
"coverage:check": "tsc && c8 --all --include=dist/**/*.js --include=codemap/**/*.mjs --check-coverage --lines 95 --statements 95 --functions 95 --branches 95 node test/all.mjs",
|
|
59
|
-
"prepublishOnly": "npm run build"
|
|
60
|
-
},
|
|
61
|
-
"license": "MIT",
|
|
62
|
-
"devDependencies": {
|
|
63
|
-
"@types/node": "^22.19.21",
|
|
64
|
-
"c8": "^10.1.3",
|
|
65
|
-
"typescript": "^5.9.3"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@geml/geml",
|
|
3
|
+
"version": "1.8.4",
|
|
4
|
+
"mcpName": "io.github.geml-spec/geml",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "CLI and parser for GEML, a plain-text document format where every block has an id — so an AI patches one block instead of rewriting the file; edits, reference checks and rollbacks are all per block. Ships an MCP server.",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"geml": "dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"main": "dist/geml.js",
|
|
14
|
+
"types": "dist/geml.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"codemap",
|
|
18
|
+
"skill",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"geml",
|
|
27
|
+
"general expressive markup language",
|
|
28
|
+
"mcp",
|
|
29
|
+
"mcp-server",
|
|
30
|
+
"markup",
|
|
31
|
+
"markdown",
|
|
32
|
+
"parser",
|
|
33
|
+
"cli",
|
|
34
|
+
"document",
|
|
35
|
+
"typed-block",
|
|
36
|
+
"ai",
|
|
37
|
+
"agent",
|
|
38
|
+
"llm",
|
|
39
|
+
"addressable",
|
|
40
|
+
"versioning",
|
|
41
|
+
"docs",
|
|
42
|
+
"code-graph"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/geml-spec/geml.git",
|
|
47
|
+
"directory": "geml-parser"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/geml-spec/geml#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/geml-spec/geml/issues"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc",
|
|
55
|
+
"test": "tsc && node test/all.mjs",
|
|
56
|
+
"parse": "node dist/geml.js",
|
|
57
|
+
"coverage": "tsc && c8 --all --include=dist/**/*.js --include=codemap/**/*.mjs --reporter=text --reporter=text-summary node test/all.mjs",
|
|
58
|
+
"coverage:check": "tsc && c8 --all --include=dist/**/*.js --include=codemap/**/*.mjs --check-coverage --lines 95 --statements 95 --functions 95 --branches 95 node test/all.mjs",
|
|
59
|
+
"prepublishOnly": "npm run build"
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^22.19.21",
|
|
64
|
+
"c8": "^10.1.3",
|
|
65
|
+
"typescript": "^5.9.3"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/skill/SKILL.md
CHANGED
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: geml
|
|
3
|
-
description: >-
|
|
4
|
-
Address a document by its BLOCKS instead of reading it whole. Use for any long
|
|
5
|
-
Markdown or documentation file — README, spec, guide, design doc, changelog —
|
|
6
|
-
when the job is to find where something is documented, read one section, or
|
|
7
|
-
change one section: `geml list`, `geml find` and `geml get` read Markdown
|
|
8
|
-
directly and hand back the one block that matters, leaving the file the
|
|
9
|
-
Markdown it already was. Skip it when the whole file is short enough to read
|
|
10
|
-
anyway. Use it also to read, author, edit or validate GEML itself — .geml
|
|
11
|
-
files, .gemlhistory sidecars, typed blocks, === fences, geml-chart, converting
|
|
12
|
-
Markdown to GEML — where the output must parse cleanly (zero error
|
|
13
|
-
diagnostics) against the reference parser.
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# Reading and editing documents blockwise
|
|
17
|
-
|
|
18
|
-
Two jobs. The first is the common one, and it needs none of GEML's syntax.
|
|
19
|
-
|
|
20
|
-
## A document that is NOT GEML — use this as a read layer
|
|
21
|
-
|
|
22
|
-
`geml list`, `geml find` and `geml get` read **Markdown** directly. Use them to
|
|
23
|
-
locate and read one block of a long document. Nothing is converted, nothing is
|
|
24
|
-
written, and the file stays exactly the Markdown it already was.
|
|
25
|
-
|
|
26
|
-
**Only when you would otherwise read the whole file to change part of it.** If
|
|
27
|
-
the file is short, or you already know the exact string to replace, open it the
|
|
28
|
-
ordinary way — the round trip costs more than it saves. What is saved is only
|
|
29
|
-
ever the part you did not have to read: on this project's own docs a `list` map
|
|
30
|
-
runs 5–11% of the file it maps, but on a changelog of many small sections it is
|
|
31
|
-
37%.
|
|
32
|
-
|
|
33
|
-
1. `geml list <file>` — every block: address, kind, line range.
|
|
34
|
-
2. `geml find "<text>" <file>` — which block holds those words, answered as an
|
|
35
|
-
ADDRESS rather than a line number, so it survives the next edit. (On a file
|
|
36
|
-
that is not `.geml` this needs 1.7.5 or newer; older builds report no match
|
|
37
|
-
rather than an error, so if a search you expect to hit comes back empty,
|
|
38
|
-
use `list`.)
|
|
39
|
-
3. `geml get <file> '<address>'` — that block and nothing else.
|
|
40
|
-
4. Edit with the ORDINARY file-editing tool, using the text from step 3 as the
|
|
41
|
-
string to replace.
|
|
42
|
-
|
|
43
|
-
Step 4 is a safety property, not a shortcut taken for speed. `geml set` and
|
|
44
|
-
`geml replace` write through GEML's own semantics; a Markdown document nobody
|
|
45
|
-
asked to convert is edited the ordinary way. If the block text does not match
|
|
46
|
-
the file byte for byte, that edit fails loudly instead of writing something
|
|
47
|
-
wrong.
|
|
48
|
-
|
|
49
|
-
Never convert a document to GEML, never leave a `.gemlhistory` beside one, and
|
|
50
|
-
do not pitch the format: use the tool, report the change you made, and let the
|
|
51
|
-
file stay what it is.
|
|
52
|
-
|
|
53
|
-
If `geml --version` does not answer, none of this is available. Read and edit
|
|
54
|
-
the ordinary way, and do not tell anyone to install anything.
|
|
55
|
-
|
|
56
|
-
## A project moving TO GEML
|
|
57
|
-
|
|
58
|
-
"This project's documents are GEML now" means new documents are authored as
|
|
59
|
-
`.geml` — notes, plans, findings, reports — in one directory (`docs/geml/`
|
|
60
|
-
unless the project says otherwise), one file per topic, with an `index.geml`
|
|
61
|
-
saying what is there and why. It does not mean converting what is already
|
|
62
|
-
written, and nobody has to say "leave the existing files alone" for that to
|
|
63
|
-
hold.
|
|
64
|
-
|
|
65
|
-
**Add, never replace.** Writing a `.geml` version of a document is not licence
|
|
66
|
-
to delete the Markdown it was drawn from — however completely the content was
|
|
67
|
-
carried across, and whatever a "one home per topic" convention seems to imply.
|
|
68
|
-
Deleting a file is a request a person makes, never an inference from a
|
|
69
|
-
convention. When both exist, say in each what it is for and name one of them as
|
|
70
|
-
the place a given fact is maintained: two documents describing a project is
|
|
71
|
-
fine, two documents maintaining the same fact is what drifts.
|
|
72
|
-
|
|
73
|
-
## A GEML document — get the syntax right
|
|
74
|
-
|
|
75
|
-
GEML expresses **every** kind of structured content — code, tables, diagrams,
|
|
76
|
-
math, callouts, metadata — through **one** primitive: the **typed block**
|
|
77
|
-
(`=== <type> {#id .class key=val}` … `===`). Always finish by **validating**: a
|
|
78
|
-
GEML file is correct only when `geml check` reports **no error diagnostics**
|
|
79
|
-
(exit 0).
|
|
80
|
-
|
|
81
|
-
## Golden rules (the things that are easy to get wrong)
|
|
82
|
-
|
|
83
|
-
1. **Fences are runs of `=` (≥3).** A block closes at a `=` run of **exactly
|
|
84
|
-
the opening length**, or — when the block has an `#id` — at the labeled
|
|
85
|
-
fence `=== #id` (any `=` run ≥3 followed by the id; no length counting).
|
|
86
|
-
2. **Nest with longer fences.** A body containing `===` lines needs a
|
|
87
|
-
**longer** outer fence: `====` wraps `===`. Careful: a same-length bare
|
|
88
|
-
`===` in the body closes the block even if you intend a labeled close —
|
|
89
|
-
the labeled close only spares you length-counting, it does NOT protect
|
|
90
|
-
same-length inner fences.
|
|
91
|
-
3. **Headings are ATX `#` only** (`#`…`######`). No setext underlines, no
|
|
92
|
-
`---` breaks, no YAML frontmatter — metadata is a `=== meta` block, and the
|
|
93
|
-
document TITLE lives there (`title = "…"`), not in an H1. A heading may
|
|
94
|
-
carry a stable explicit id: `## Title {#sec}`.
|
|
95
|
-
4. **Give every section a stable `{#id}`** — `## Findings {#findings}` — then
|
|
96
|
-
keep ids unique per document, with **every reference resolving**:
|
|
97
|
-
`[t](#id)`, `[[#id]]`, `[^id]`, `src=`, `data=`, `other.geml#id`. An
|
|
98
|
-
unresolved reference is a build **error**. Naming them is the part that pays
|
|
99
|
-
later: a document with no ids costs what Markdown costs, because there is
|
|
100
|
-
nothing for `geml get` to read or `geml set` to replace short of the file.
|
|
101
|
-
5. **No raw HTML.** Notes → `=== note`, comments → `%%` lines, hidden content
|
|
102
|
-
→ `{hidden}`, addressable prose → `=== text`, verified data → `=== data`
|
|
103
|
-
(json/jsonl; `code` shows text, `data` IS data).
|
|
104
|
-
|
|
105
|
-
## Validate every time
|
|
106
|
-
|
|
107
|
-
```sh
|
|
108
|
-
geml check file.geml # diagnostics + exit code only; exit 0 = correct
|
|
109
|
-
geml check --json file.geml # machine-readable diagnostics array
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
If `geml` is not on PATH: `npm i -g @geml/geml` (package `@geml/geml`, command
|
|
113
|
-
`geml`), or run without installing via `npx -y @geml/geml check file.geml`.
|
|
114
|
-
Inside the geml-spec repo prefer the local build:
|
|
115
|
-
`node geml-parser/dist/geml.js <args>`. If no parser is reachable, follow the
|
|
116
|
-
golden rules and validate once it is.
|
|
117
|
-
|
|
118
|
-
`geml skill install` sets all of this up user-global, and installs this text
|
|
119
|
-
into whatever other agent tools it detects — a tool's directory has to be there
|
|
120
|
-
already; none is ever created for you. `--dry-run` shows what it would do.
|
|
121
|
-
|
|
122
|
-
## Work blockwise (agent editing)
|
|
123
|
-
|
|
124
|
-
```sh
|
|
125
|
-
geml list file.geml # CALL THIS FIRST — every block, its address, kind, lines
|
|
126
|
-
geml find "text" file|dir # search block CONTENT -> file<TAB>address (exit 1 = no hit)
|
|
127
|
-
# a NAMED file is searched whatever its extension (.md too);
|
|
128
|
-
# a directory walks *.geml only
|
|
129
|
-
geml get file.geml '#id' # read ONE block (a heading id = its whole section)
|
|
130
|
-
geml set file.geml '#id' --in f # replace ONE block (re-parsed; never writes a broken doc)
|
|
131
|
-
geml history save file.geml -m "…" # snapshot to .gemlhistory after each meaningful edit
|
|
132
|
-
geml revert file.geml '#id' # roll ONE block back (--rev -2 | changed | <rev-id>)
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Address a block, never a line range: `#id` · `'## Heading'` (its whole section)
|
|
136
|
-
· `L27-58` (the smallest block holding those lines — how a line number from an
|
|
137
|
-
editor, a linter or a diff hunk becomes an address). `list` and `find` print
|
|
138
|
-
addresses that paste straight into the others, so neither `grep` nor a line
|
|
139
|
-
count is needed to locate anything.
|
|
140
|
-
|
|
141
|
-
The rest is one `geml get` away in the reference below, and stays there because
|
|
142
|
-
it is needed rarely and this page is read every time: the remaining address
|
|
143
|
-
forms in `#cli`, and in `#editing` the three ways to cut a section
|
|
144
|
-
(`--head`/`--intro`/`--body`), the experimental `replace`, and what a write that
|
|
145
|
-
drops blocks does.
|
|
146
|
-
|
|
147
|
-
## Full reference — pull ONE section, not the whole file
|
|
148
|
-
|
|
149
|
-
`references/authoring.geml` (under this skill's base directory) holds the
|
|
150
|
-
detailed reference. Fetch just the section you need:
|
|
151
|
-
|
|
152
|
-
```sh
|
|
153
|
-
geml get <skill-base>/references/authoring.geml '#tables'
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
| section | covers |
|
|
157
|
-
|---|---|
|
|
158
|
-
| `#typed-block` | block anatomy, attribute object, examples of every registered type |
|
|
159
|
-
| `#tables` | pipe/CSV bodies, `compute=`, `summary=`, printf display, `span=` merges |
|
|
160
|
-
| `#charts` | `geml-chart` diagrams bound to a table via `data=#id` |
|
|
161
|
-
| `#data` | the `data` block — value tree, `json`/`jsonl` formats, blind append, chart binding |
|
|
162
|
-
| `#inline` | inline markup, links/refs/footnotes, task lists, media embeds |
|
|
163
|
-
| `#hidden` | `%%` comments, `{hidden}`, `{{key}}` interpolation, `=== embed` |
|
|
164
|
-
| `#cli` | every CLI verb — get/set/add/delete/rename, `--to` conversion, check |
|
|
165
|
-
| `#editing` | the blockwise editing loop + `.gemlhistory` versioning |
|
|
166
|
-
| `#project-config` | carrying a project's Claude config docs in GEML, quietly |
|
|
167
|
-
| `#checklist` | full pre-flight authoring checklist |
|
|
1
|
+
---
|
|
2
|
+
name: geml
|
|
3
|
+
description: >-
|
|
4
|
+
Address a document by its BLOCKS instead of reading it whole. Use for any long
|
|
5
|
+
Markdown or documentation file — README, spec, guide, design doc, changelog —
|
|
6
|
+
when the job is to find where something is documented, read one section, or
|
|
7
|
+
change one section: `geml list`, `geml find` and `geml get` read Markdown
|
|
8
|
+
directly and hand back the one block that matters, leaving the file the
|
|
9
|
+
Markdown it already was. Skip it when the whole file is short enough to read
|
|
10
|
+
anyway. Use it also to read, author, edit or validate GEML itself — .geml
|
|
11
|
+
files, .gemlhistory sidecars, typed blocks, === fences, geml-chart, converting
|
|
12
|
+
Markdown to GEML — where the output must parse cleanly (zero error
|
|
13
|
+
diagnostics) against the reference parser.
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Reading and editing documents blockwise
|
|
17
|
+
|
|
18
|
+
Two jobs. The first is the common one, and it needs none of GEML's syntax.
|
|
19
|
+
|
|
20
|
+
## A document that is NOT GEML — use this as a read layer
|
|
21
|
+
|
|
22
|
+
`geml list`, `geml find` and `geml get` read **Markdown** directly. Use them to
|
|
23
|
+
locate and read one block of a long document. Nothing is converted, nothing is
|
|
24
|
+
written, and the file stays exactly the Markdown it already was.
|
|
25
|
+
|
|
26
|
+
**Only when you would otherwise read the whole file to change part of it.** If
|
|
27
|
+
the file is short, or you already know the exact string to replace, open it the
|
|
28
|
+
ordinary way — the round trip costs more than it saves. What is saved is only
|
|
29
|
+
ever the part you did not have to read: on this project's own docs a `list` map
|
|
30
|
+
runs 5–11% of the file it maps, but on a changelog of many small sections it is
|
|
31
|
+
37%.
|
|
32
|
+
|
|
33
|
+
1. `geml list <file>` — every block: address, kind, line range.
|
|
34
|
+
2. `geml find "<text>" <file>` — which block holds those words, answered as an
|
|
35
|
+
ADDRESS rather than a line number, so it survives the next edit. (On a file
|
|
36
|
+
that is not `.geml` this needs 1.7.5 or newer; older builds report no match
|
|
37
|
+
rather than an error, so if a search you expect to hit comes back empty,
|
|
38
|
+
use `list`.)
|
|
39
|
+
3. `geml get <file> '<address>'` — that block and nothing else.
|
|
40
|
+
4. Edit with the ORDINARY file-editing tool, using the text from step 3 as the
|
|
41
|
+
string to replace.
|
|
42
|
+
|
|
43
|
+
Step 4 is a safety property, not a shortcut taken for speed. `geml set` and
|
|
44
|
+
`geml replace` write through GEML's own semantics; a Markdown document nobody
|
|
45
|
+
asked to convert is edited the ordinary way. If the block text does not match
|
|
46
|
+
the file byte for byte, that edit fails loudly instead of writing something
|
|
47
|
+
wrong.
|
|
48
|
+
|
|
49
|
+
Never convert a document to GEML, never leave a `.gemlhistory` beside one, and
|
|
50
|
+
do not pitch the format: use the tool, report the change you made, and let the
|
|
51
|
+
file stay what it is.
|
|
52
|
+
|
|
53
|
+
If `geml --version` does not answer, none of this is available. Read and edit
|
|
54
|
+
the ordinary way, and do not tell anyone to install anything.
|
|
55
|
+
|
|
56
|
+
## A project moving TO GEML
|
|
57
|
+
|
|
58
|
+
"This project's documents are GEML now" means new documents are authored as
|
|
59
|
+
`.geml` — notes, plans, findings, reports — in one directory (`docs/geml/`
|
|
60
|
+
unless the project says otherwise), one file per topic, with an `index.geml`
|
|
61
|
+
saying what is there and why. It does not mean converting what is already
|
|
62
|
+
written, and nobody has to say "leave the existing files alone" for that to
|
|
63
|
+
hold.
|
|
64
|
+
|
|
65
|
+
**Add, never replace.** Writing a `.geml` version of a document is not licence
|
|
66
|
+
to delete the Markdown it was drawn from — however completely the content was
|
|
67
|
+
carried across, and whatever a "one home per topic" convention seems to imply.
|
|
68
|
+
Deleting a file is a request a person makes, never an inference from a
|
|
69
|
+
convention. When both exist, say in each what it is for and name one of them as
|
|
70
|
+
the place a given fact is maintained: two documents describing a project is
|
|
71
|
+
fine, two documents maintaining the same fact is what drifts.
|
|
72
|
+
|
|
73
|
+
## A GEML document — get the syntax right
|
|
74
|
+
|
|
75
|
+
GEML expresses **every** kind of structured content — code, tables, diagrams,
|
|
76
|
+
math, callouts, metadata — through **one** primitive: the **typed block**
|
|
77
|
+
(`=== <type> {#id .class key=val}` … `===`). Always finish by **validating**: a
|
|
78
|
+
GEML file is correct only when `geml check` reports **no error diagnostics**
|
|
79
|
+
(exit 0).
|
|
80
|
+
|
|
81
|
+
## Golden rules (the things that are easy to get wrong)
|
|
82
|
+
|
|
83
|
+
1. **Fences are runs of `=` (≥3).** A block closes at a `=` run of **exactly
|
|
84
|
+
the opening length**, or — when the block has an `#id` — at the labeled
|
|
85
|
+
fence `=== #id` (any `=` run ≥3 followed by the id; no length counting).
|
|
86
|
+
2. **Nest with longer fences.** A body containing `===` lines needs a
|
|
87
|
+
**longer** outer fence: `====` wraps `===`. Careful: a same-length bare
|
|
88
|
+
`===` in the body closes the block even if you intend a labeled close —
|
|
89
|
+
the labeled close only spares you length-counting, it does NOT protect
|
|
90
|
+
same-length inner fences.
|
|
91
|
+
3. **Headings are ATX `#` only** (`#`…`######`). No setext underlines, no
|
|
92
|
+
`---` breaks, no YAML frontmatter — metadata is a `=== meta` block, and the
|
|
93
|
+
document TITLE lives there (`title = "…"`), not in an H1. A heading may
|
|
94
|
+
carry a stable explicit id: `## Title {#sec}`.
|
|
95
|
+
4. **Give every section a stable `{#id}`** — `## Findings {#findings}` — then
|
|
96
|
+
keep ids unique per document, with **every reference resolving**:
|
|
97
|
+
`[t](#id)`, `[[#id]]`, `[^id]`, `src=`, `data=`, `other.geml#id`. An
|
|
98
|
+
unresolved reference is a build **error**. Naming them is the part that pays
|
|
99
|
+
later: a document with no ids costs what Markdown costs, because there is
|
|
100
|
+
nothing for `geml get` to read or `geml set` to replace short of the file.
|
|
101
|
+
5. **No raw HTML.** Notes → `=== note`, comments → `%%` lines, hidden content
|
|
102
|
+
→ `{hidden}`, addressable prose → `=== text`, verified data → `=== data`
|
|
103
|
+
(json/jsonl; `code` shows text, `data` IS data).
|
|
104
|
+
|
|
105
|
+
## Validate every time
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
geml check file.geml # diagnostics + exit code only; exit 0 = correct
|
|
109
|
+
geml check --json file.geml # machine-readable diagnostics array
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If `geml` is not on PATH: `npm i -g @geml/geml` (package `@geml/geml`, command
|
|
113
|
+
`geml`), or run without installing via `npx -y @geml/geml check file.geml`.
|
|
114
|
+
Inside the geml-spec repo prefer the local build:
|
|
115
|
+
`node geml-parser/dist/geml.js <args>`. If no parser is reachable, follow the
|
|
116
|
+
golden rules and validate once it is.
|
|
117
|
+
|
|
118
|
+
`geml skill install` sets all of this up user-global, and installs this text
|
|
119
|
+
into whatever other agent tools it detects — a tool's directory has to be there
|
|
120
|
+
already; none is ever created for you. `--dry-run` shows what it would do.
|
|
121
|
+
|
|
122
|
+
## Work blockwise (agent editing)
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
geml list file.geml # CALL THIS FIRST — every block, its address, kind, lines
|
|
126
|
+
geml find "text" file|dir # search block CONTENT -> file<TAB>address (exit 1 = no hit)
|
|
127
|
+
# a NAMED file is searched whatever its extension (.md too);
|
|
128
|
+
# a directory walks *.geml only
|
|
129
|
+
geml get file.geml '#id' # read ONE block (a heading id = its whole section)
|
|
130
|
+
geml set file.geml '#id' --in f # replace ONE block (re-parsed; never writes a broken doc)
|
|
131
|
+
geml history save file.geml -m "…" # snapshot to .gemlhistory after each meaningful edit
|
|
132
|
+
geml revert file.geml '#id' # roll ONE block back (--rev -2 | changed | <rev-id>)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Address a block, never a line range: `#id` · `'## Heading'` (its whole section)
|
|
136
|
+
· `L27-58` (the smallest block holding those lines — how a line number from an
|
|
137
|
+
editor, a linter or a diff hunk becomes an address). `list` and `find` print
|
|
138
|
+
addresses that paste straight into the others, so neither `grep` nor a line
|
|
139
|
+
count is needed to locate anything.
|
|
140
|
+
|
|
141
|
+
The rest is one `geml get` away in the reference below, and stays there because
|
|
142
|
+
it is needed rarely and this page is read every time: the remaining address
|
|
143
|
+
forms in `#cli`, and in `#editing` the three ways to cut a section
|
|
144
|
+
(`--head`/`--intro`/`--body`), the experimental `replace`, and what a write that
|
|
145
|
+
drops blocks does.
|
|
146
|
+
|
|
147
|
+
## Full reference — pull ONE section, not the whole file
|
|
148
|
+
|
|
149
|
+
`references/authoring.geml` (under this skill's base directory) holds the
|
|
150
|
+
detailed reference. Fetch just the section you need:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
geml get <skill-base>/references/authoring.geml '#tables'
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| section | covers |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `#typed-block` | block anatomy, attribute object, examples of every registered type |
|
|
159
|
+
| `#tables` | pipe/CSV bodies, `compute=`, `summary=`, printf display, `span=` merges |
|
|
160
|
+
| `#charts` | `geml-chart` diagrams bound to a table via `data=#id` |
|
|
161
|
+
| `#data` | the `data` block — value tree, `json`/`jsonl` formats, blind append, chart binding |
|
|
162
|
+
| `#inline` | inline markup, links/refs/footnotes, task lists, media embeds |
|
|
163
|
+
| `#hidden` | `%%` comments, `{hidden}`, `{{key}}` interpolation, `=== embed` |
|
|
164
|
+
| `#cli` | every CLI verb — get/set/add/delete/rename, `--to` conversion, check |
|
|
165
|
+
| `#editing` | the blockwise editing loop + `.gemlhistory` versioning |
|
|
166
|
+
| `#project-config` | carrying a project's Claude config docs in GEML, quietly |
|
|
167
|
+
| `#checklist` | full pre-flight authoring checklist |
|