@geml/geml 1.7.1 → 1.7.3
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 +44 -7
- package/codemap/build.mjs +6 -0
- package/codemap/emit.mjs +31 -1
- package/codemap/render-all.mjs +14 -1
- package/dist/block-edit.js +15 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2560 -0
- package/dist/from-md.js +1 -1
- package/dist/geml.d.ts +32 -0
- package/dist/geml.js +197 -2038
- package/dist/history.js +7 -3
- package/dist/mcp.js +46 -8
- package/dist/render-html.d.ts +1 -1
- package/dist/render.d.ts +1 -1
- package/dist/render.js +180 -15
- package/dist/selector.d.ts +7 -0
- package/dist/selector.js +41 -1
- package/package.json +67 -67
- package/skill/SKILL.md +35 -0
- package/skill/references/authoring.geml +8 -1
package/package.json
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@geml/geml",
|
|
3
|
-
"version": "1.7.
|
|
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/
|
|
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.7.3",
|
|
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
|
@@ -50,15 +50,50 @@ Inside the geml-spec repo prefer the local build:
|
|
|
50
50
|
`node geml-parser/dist/geml.js <args>`. If no parser is reachable, follow the
|
|
51
51
|
golden rules and validate once it is.
|
|
52
52
|
|
|
53
|
+
`geml skill install` sets all of this up user-global, and installs this text
|
|
54
|
+
into whatever other agent tools it detects — a tool's directory has to be there
|
|
55
|
+
already; none is ever created for you. `--dry-run` shows what it would do.
|
|
56
|
+
|
|
53
57
|
## Work blockwise (agent editing)
|
|
54
58
|
|
|
55
59
|
```sh
|
|
60
|
+
geml list file.geml # CALL THIS FIRST — every block, its address, kind, lines
|
|
61
|
+
geml find "text" file.geml|dir # search block CONTENT -> file<TAB>address (exit 1 = no hit)
|
|
56
62
|
geml get file.geml '#id' # read ONE block (a heading id = its whole section)
|
|
57
63
|
geml set file.geml '#id' --in f # replace ONE block (re-parsed; never writes a broken doc)
|
|
64
|
+
geml replace file.geml OLD NEW # EXPERIMENTAL literal swap; --within '#id' to narrow
|
|
58
65
|
geml history save file.geml -m "…" # snapshot to .gemlhistory after each meaningful edit
|
|
59
66
|
geml revert file.geml '#id' # roll ONE block back (--rev -2 | changed | <rev-id>)
|
|
60
67
|
```
|
|
61
68
|
|
|
69
|
+
Address a block, never a line range: `#id` · `'## Heading'` (its whole section)
|
|
70
|
+
· `'=== type'` · `@<hex>` (no id) · `L27` or `L27-58` (the smallest block holding
|
|
71
|
+
those lines — how a line number from an editor, a linter or a diff hunk becomes
|
|
72
|
+
an address). `list` and `find` print addresses that paste straight into the
|
|
73
|
+
others, so neither `grep` nor a line count is needed to locate anything.
|
|
74
|
+
|
|
75
|
+
Any section can be cut three ways, on `get` and `set` alike: `--head` (the
|
|
76
|
+
heading line), `--intro` (what it says before its first subheading — empty when
|
|
77
|
+
one follows immediately, the whole body when none does), `--body` (everything
|
|
78
|
+
under it, so it always contains the intro). `--intro` is how you edit a
|
|
79
|
+
section's opening without pulling its subsections into context, and setting an
|
|
80
|
+
empty one writes an opening where the section had none.
|
|
81
|
+
|
|
82
|
+
When the exact old text is already known and nothing needs reading — a version
|
|
83
|
+
string in six places, a renamed term — `geml replace` is the cheap path, and the
|
|
84
|
+
one to prefer over dropping to `sed`: same two short strings, but the result is
|
|
85
|
+
re-parsed before it lands, the blocks it touched are named back to you, and it
|
|
86
|
+
is in `.gemlhistory` to revert. It swaps a LITERAL, never a pattern, and refuses
|
|
87
|
+
a swap that would rename an id (use `geml rename`, which fixes the references
|
|
88
|
+
too). **It is EXPERIMENTAL and may be withdrawn** — reach for it, but do not
|
|
89
|
+
build anything on it that cannot change.
|
|
90
|
+
|
|
91
|
+
A write is refused when it would break the document, never merely because it
|
|
92
|
+
removes something: a replacement that drops blocks is carried out and NAMED on
|
|
93
|
+
stderr — unnamed blocks included — with `geml revert` as the way back. Read,
|
|
94
|
+
edit, write back, and nothing is dropped, because `get` handed those blocks to
|
|
95
|
+
you. Send content that omits them only when removing them is the point.
|
|
96
|
+
|
|
62
97
|
## Full reference — pull ONE section, not the whole file
|
|
63
98
|
|
|
64
99
|
`references/authoring.geml` (under this skill's base directory) holds the
|
|
@@ -214,10 +214,17 @@ All commands accept `-` to read from stdin.
|
|
|
214
214
|
|
|
215
215
|
=== code {#cli-verbs lang=sh}
|
|
216
216
|
geml file.geml # document-model JSON (default --to json)
|
|
217
|
-
geml
|
|
217
|
+
geml list file.geml # CALL FIRST: every block, its address, kind, line range
|
|
218
|
+
geml find "text" file.geml|dir # search block CONTENT -> file<TAB>address; exit 1 = no hit
|
|
219
|
+
geml get file.geml # same listing as `list` (the no-selector default)
|
|
218
220
|
geml get file.geml '#id' # print ONE block (raw span; --json = model node)
|
|
219
221
|
geml get file.geml '=== note' # every block of a type; '@a3f9c1d2' = a block with no #id
|
|
222
|
+
geml get file.geml 'L27-58' # position: the smallest block holding those lines
|
|
223
|
+
geml get file.geml '#sec' --intro # a section cut three ways: --head | --intro | --body
|
|
220
224
|
geml set file.geml '#id' --in f # replace ONE block (guarded: re-parsed, never writes broken)
|
|
225
|
+
geml set file.geml '#sec' --intro # replace just the opening; the subsections stay put
|
|
226
|
+
geml replace file.geml OLD NEW # EXPERIMENTAL, may be withdrawn: literal swap, checked and
|
|
227
|
+
# reported; --within '#id' or '=== type' narrows the scope
|
|
221
228
|
geml add file.geml --after '#id' --in f # insert a fragment (keeps its own ids)
|
|
222
229
|
geml delete file.geml '#id' ['#id2'] # remove one or more blocks
|
|
223
230
|
geml rename file.geml '#old' '#new' # rename an id AND every reference to it
|