@geml/geml 1.7.3 → 1.7.5
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/dist/cli.js +15 -6
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +3 -1
- package/skill/references/authoring.geml +4 -1
package/dist/cli.js
CHANGED
|
@@ -147,7 +147,8 @@ Usage:
|
|
|
147
147
|
(call this first — its addresses are what every verb below takes)
|
|
148
148
|
geml find <pattern> [<file|dir> …] [--json] [--case] [--head] search block content -> file#address
|
|
149
149
|
(an address, not a line number, so a hit pastes into get/set;
|
|
150
|
-
a
|
|
150
|
+
a named file is searched whatever its extension, a dir walks
|
|
151
|
+
*.geml only; exit 1 when nothing matched)
|
|
151
152
|
geml get <file.geml|-> [#id] [--json] [--head|--intro|--body] with #id: print that block
|
|
152
153
|
(a heading id = its whole section; --head = head line;
|
|
153
154
|
--json = model node). Without #id: list all addressable
|
|
@@ -201,7 +202,7 @@ const SUBHELP = {
|
|
|
201
202
|
delete: "usage: geml delete <file.geml|-> #id [#id2 …] [-o out.geml] (remove one or more blocks; a missing id is skipped with a note, not an error; a reference left dangling is a warning, not a refusal — delete never fails on a live reference)",
|
|
202
203
|
rename: "usage: geml rename <file.geml|-> #old #new [-o out.geml] (rewrite an id's declaration AND every reference — [[#id]], [text](#id), chart data=#id, footnote [^id] — id-boundary safe, skipping raw block bodies; #new must be free; refused if it breaks the doc)",
|
|
203
204
|
list: "usage: geml list <file.geml|-> [--json] (list every addressable block with its shortest unique address, its kind and its line range — the same listing `geml get <file>` prints with no selector, under the name the MCP surface already uses. Call it FIRST: the addresses it prints are what get/set/add/delete/rename/revert all take)",
|
|
204
|
-
find: "usage: geml find <pattern> [<file
|
|
205
|
+
find: "usage: geml find <pattern> [<file|dir> …] [--json] [--case] [--head] (search block CONTENT and print `<file>TAB<address>` per hit — an address, never a line number, so a hit is `geml get <file> '<address>'` with no editing. The address is the INNERMOST block holding the match, never its enclosing section, and a block is reported once however many lines in it matched. Substring, case-insensitive unless --case; a file you NAME is searched whatever its extension, including Markdown, while a directory is walked for *.geml only; no path = the current directory; --head adds the matching line as a third column. Exit 1 when nothing matched, so `if geml find …` works in a script)",
|
|
205
206
|
replace: "usage: geml replace <file.geml|-> <old> <new> [--within <selector>] [-o out.geml] (EXPERIMENTAL — this verb MAY BE WITHDRAWN in a later release; it is here to find out whether an addressed, checked replacement earns its place beside `sed`, and if it does not, it goes. Build nothing on it you cannot change, and say so in a discussion if it is doing real work for you. Swaps a LITERAL string — never a pattern, that is what `sed` is for and where the footguns are. Without --within the whole document; with it, only inside the blocks that selector matches, and unlike `set` it may match several: `--within '=== table'` means every table. What this buys over `sed -i`, at the same cost of two short strings and nothing read: the result is re-parsed and refused if it would break the document, the blocks it touched are NAMED on stderr, and the write lands in .gemlhistory where `revert` can undo it. An id is not text — a replacement that would rename one is refused and points at `geml rename`, which fixes every reference too. Exit 1 when nothing matched, so `if geml replace …` works in a script)",
|
|
206
207
|
check: "usage: geml check <file.geml|-> [--root <dir>] [--json] (--root: resolve cross-doc refs within <dir> instead of the file's own directory)",
|
|
207
208
|
revert: "usage: geml revert <file.geml> #id [--rev <sel>] [--append|--before #x|--after #x] [--head] [--dry-run] [-o out] (reconcile #id to a revision: splice / resurrect / remove; sel: 0 | -N | id-prefix | changed; default -1)",
|
|
@@ -1100,7 +1101,14 @@ function runList(args) {
|
|
|
1100
1101
|
// platforms — a listing that reorders between machines is a listing nobody can
|
|
1101
1102
|
// diff. Hidden directories and `node_modules` are skipped: a search verb that
|
|
1102
1103
|
// dredges up vendored copies trains people to stop reading its output.
|
|
1103
|
-
|
|
1104
|
+
// `explicit` marks a path the caller NAMED, as opposed to one this walk found.
|
|
1105
|
+
// A named file is searched whatever it is called: `get` and `list` already read
|
|
1106
|
+
// a `.md` this way, and having only `find` refuse meant
|
|
1107
|
+
// `geml find GEML README.md` exited 1 against a file holding forty-four
|
|
1108
|
+
// matches — a search that answers "no" about a file you pointed straight at.
|
|
1109
|
+
// The `.geml` filter belongs to the DIRECTORY walk, where taking every file
|
|
1110
|
+
// would drag the whole source tree through the parser.
|
|
1111
|
+
function gemlFilesUnder(path, out, explicit = false) {
|
|
1104
1112
|
let dir = false;
|
|
1105
1113
|
try {
|
|
1106
1114
|
dir = statSync(path).isDirectory();
|
|
@@ -1109,7 +1117,7 @@ function gemlFilesUnder(path, out) {
|
|
|
1109
1117
|
return;
|
|
1110
1118
|
}
|
|
1111
1119
|
if (!dir) {
|
|
1112
|
-
if (path.endsWith(".geml"))
|
|
1120
|
+
if (explicit || path.endsWith(".geml"))
|
|
1113
1121
|
out.push(path);
|
|
1114
1122
|
return;
|
|
1115
1123
|
}
|
|
@@ -1136,8 +1144,9 @@ function runFind(args) {
|
|
|
1136
1144
|
const json = args.includes("--json");
|
|
1137
1145
|
const needle = sensitive ? pattern : pattern.toLowerCase();
|
|
1138
1146
|
const files = [];
|
|
1139
|
-
|
|
1140
|
-
|
|
1147
|
+
const named = pos.slice(1);
|
|
1148
|
+
for (const p of named.length ? named : ["."])
|
|
1149
|
+
gemlFilesUnder(p, files, named.length > 0);
|
|
1141
1150
|
const hits = [];
|
|
1142
1151
|
for (const f of files) {
|
|
1143
1152
|
let source;
|
package/dist/mcp.js
CHANGED
|
@@ -275,7 +275,7 @@ export const TOOLS = [
|
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
name: "geml_find",
|
|
278
|
-
description: "Search block CONTENT across the served documents and get back ADDRESSES, one row of `<file>\\t<address>` per hit. This is the other half of geml_list: `list` says what a document contains, `find` says which block holds the words you are looking for — and it answers with an address that pastes straight into geml_get or geml_set, never a line number that the next edit invalidates. The address is the innermost block holding the match, and a block that matches on many lines is reported once. Substring, case-insensitive unless `case` is true. Omit `path` to search every `*.geml` under the server root, or give a file or directory to narrow it. No match is not an error: the result is empty.",
|
|
278
|
+
description: "Search block CONTENT across the served documents and get back ADDRESSES, one row of `<file>\\t<address>` per hit. This is the other half of geml_list: `list` says what a document contains, `find` says which block holds the words you are looking for — and it answers with an address that pastes straight into geml_get or geml_set, never a line number that the next edit invalidates. The address is the innermost block holding the match, and a block that matches on many lines is reported once. Substring, case-insensitive unless `case` is true. Omit `path` to search every `*.geml` under the server root, or give a file or directory to narrow it — a file you name is searched whatever its extension, Markdown included, while a directory walks `*.geml` only. No match is not an error: the result is empty.",
|
|
279
279
|
inputSchema: {
|
|
280
280
|
type: "object",
|
|
281
281
|
properties: {
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -58,7 +58,9 @@ already; none is ever created for you. `--dry-run` shows what it would do.
|
|
|
58
58
|
|
|
59
59
|
```sh
|
|
60
60
|
geml list file.geml # CALL THIS FIRST — every block, its address, kind, lines
|
|
61
|
-
geml find "text" file
|
|
61
|
+
geml find "text" file|dir # search block CONTENT -> file<TAB>address (exit 1 = no hit)
|
|
62
|
+
# a NAMED file is searched whatever its extension (.md too);
|
|
63
|
+
# a directory walks *.geml only
|
|
62
64
|
geml get file.geml '#id' # read ONE block (a heading id = its whole section)
|
|
63
65
|
geml set file.geml '#id' --in f # replace ONE block (re-parsed; never writes a broken doc)
|
|
64
66
|
geml replace file.geml OLD NEW # EXPERIMENTAL literal swap; --within '#id' to narrow
|
|
@@ -215,7 +215,10 @@ All commands accept `-` to read from stdin.
|
|
|
215
215
|
=== code {#cli-verbs lang=sh}
|
|
216
216
|
geml file.geml # document-model JSON (default --to json)
|
|
217
217
|
geml list file.geml # CALL FIRST: every block, its address, kind, line range
|
|
218
|
-
geml find "text" file
|
|
218
|
+
geml find "text" file|dir # search block CONTENT -> file<TAB>address; exit 1 = no hit
|
|
219
|
+
# a NAMED file is searched whatever its extension — `list`,
|
|
220
|
+
# `get` and `find` all read Markdown, so this addresses a
|
|
221
|
+
# plain README without converting it; a DIRECTORY walks *.geml
|
|
219
222
|
geml get file.geml # same listing as `list` (the no-selector default)
|
|
220
223
|
geml get file.geml '#id' # print ONE block (raw span; --json = model node)
|
|
221
224
|
geml get file.geml '=== note' # every block of a type; '@a3f9c1d2' = a block with no #id
|