geml 命令行做三类事:转换一份文档(json、html、md、geml 之间),按块读写(list、find、get、set、add、delete、rename、revert),校验(check,以及 profile 各自的 style check、history verify、codemap verify)。核心动词从不带 profile 名。写操作有一道守卫:结果解析不过就拒绝写入。这一页把每个动词、它接受的地址形式、退出码,用一份小文档实跑一遍。
已定 geml --help 或规范写死的。实测 跑出来的行为。GEP 草案 地址形式来自草案。
| 规则 | 出处 | 状态 | |
|---|---|---|---|
| 通用 | |||
| 1 | 文件位置可以写 - 读 stdin。写动词(set / add / delete / rename)对文件原地整写,对 - 输出到 stdout;-o f 重定向,-o - 也是 stdout。退出码 0 成功、1 文档或操作错误。 | --help | 已定 |
| 2 | --root d 把跨文档解析放宽到目录 d,读写动词都吃它。写入在结果解析不过时被拒绝,所以一份靠仓库根才能解析 ../x.md 的文档不带 --root 就根本不可编辑,否则守卫会把自己的盲区读成损坏。 | --help | 已定 |
| 3 | 先调 list:它打印的地址就是其余动词接受的东西。get 无 #id 时也列出全部 id。 | --help | 已定 |
| 读 | |||
| 4 | list <file> [--json]:每个可寻址块一行:地址、kind、行范围;匿名块显示为 === type 或带内容哈希的 type@hash;块间散文以派生地址出现。 | --help · §4 | 已定 |
| 5 | find <pattern> [file|dir …]:搜块内容,回答 file<TAB>address,是地址不是行号,所以能直接粘进 get / set。命名文件不限扩展名(.md 也搜),目录只走 *.geml。无命中 exit 1。 | --help | 已定 |
| 6 | get <file> [#id] [--json] [--head|--intro|--body]:标题 id = 整节;--head 只标题行,--body 标题下全部,--intro 到第一个子标题前;--json 给模型节点。选择器也可以是位置 L27 / L27-58:包含这些行的最小块,grep 命中或堆栈行号由此变成地址。 | --help | 已定 |
| 写 | |||
| 7 | set <file> #id [--head|--intro|--body] [--in f[#src]|-]:替换一块。--in F 取 F 的块 #id,F#src 取 #src,否则 stdin 原样;缺省整块,--head 只换标题行,--body 只换正文。 | --help | 已定 |
| 8 | 守卫:写入结果重新解析,不通过就不写,exit 1,原文不动。实测:把一个未关闭的 code 块塞进 set,得到「replacement would break the document … not written」。 | --help · 实测 | 已定 |
| 9 | add (--append | --before #id | --after #id) --in f|-:插入片段,一个或多个块和散文;内容保留自己的 id,冲突拒绝。 | --help | 已定 |
| 10 | delete #id [#id2…]:缺的 id 跳过;留下悬空引用只是 warning,不拒绝,提示跑 check 时它是 error。实测:删掉 #cmd 后 warning 一条、写入成功、随后 check exit 1。 | --help · 实测 | 已定 |
| 11 | rename #old #new:改 id 和每一处引用,按 id 边界匹配(不会把 #cmd 改进 #cmdline)。实测:块和 [[#c]] 一起变成 cmd。 | --help · 实测 | 已定 |
| 12 | replace <old> <new> [--within sel]:EXPERIMENTAL 的字面替换,受检并报告。revert <file> #id [--rev sel]:从 .gemlhistory 把一个块回到过去某版(拼回、复活或删除),sel 是 0 | -N | id 前缀 | changed,缺省 -1。 | --help | 已定 |
| 转换与校验 | |||
| 13 | geml <file> [--to json|html|md|geml] [--from geml|md|json]:默认输出文档模型 JSON;--to md 有损(标题 id 和属性丢掉,会加 note);--to html 自包含,--fragment 只给正文标记;--to geml 规范重排;--from md 把 Markdown 读成 GEML,扩展名能推断则不用写。 | --help · 实测 | 已定 |
| 14 | check [--root d] [--json]:只校验,诊断加退出码;--json 给诊断数组。这是本图解系列每一页右侧输出的来源。 | --help | 已定 |
| 15 | profile 自带动词:style check(exit 0/1/2)、history save|get|restore|verify、codemap build|verify|render|serve|refresh|find;另有 mcp --root(11 个工具,每个写操作落盘前校验)、skill install。核心动词永不带 profile 名。 | --help · profiles README | 实测 |
| 16 | 地址形式:#id、'## Heading'(整节)、L27-58(位置)、#fy[2]["Q1"] / #meta["key"](GEP-0011 坐标,本分支已实现)、#form#field(GEP-0008 草案,未实现)。 | --help · GEP-0011 · GEP-0008 | GEP 草案 |
一份七行的小文档。左边是它,右边是每一步的真实输出。
=== meta title = "cli probe" === # Intro {#intro} Hello world paragraph. === code {#c lang=sh} echo hi === ## Next {#next} See [[#c]].
$ printf '=== code {#c lang=sh}\nunterminated\n' | geml set cli.geml '#c' --in - error: replacement would break the document: unterminated `code` block (no matching === or `=== #c`) (line 6); not written exit 1 · cli.geml 一字未动
$ geml list cli.geml === meta meta anon L1-3 #intro heading h1 L4-11 Intro #intro-before-c prose L5-5 ← 派生地址:#intro 之内、#c 之前 #c code L6-8 #next heading h2 L9-11 Next $ geml find "echo" cli.geml cli.geml #c exit 0 $ geml find "nothing-here" cli.geml exit 1 $ geml get cli.geml 'L6' 位置 → 最小包含块 === code {#c lang=sh} echo hi === $ geml get cli.geml '#intro' --head # Intro {#intro} $ geml get cli.geml '#intro' --body 标题下全部,含子节 #next Hello world paragraph. === code {#c lang=sh} echo hi === ## Next {#next} See [[#c]].
get 'L6' 把一个行号变成地址:包含它的最小块。#intro 是 H1,它的节到文件尾,所以 --body 连 ## Next 一起给出。要只改标题行用 --head。三次写入接着一次 check。
$ geml rename cli.geml '#c' '#cmd' wrote cli.geml $ grep -n cmd cli.geml 6:=== code {#cmd lang=sh} 10:See [[#cmd]]. ← 引用一起改了 $ printf '=== note {#n}\nadded\n===\n' | geml add cli.geml --after '#cmd' --in - wrote cli.geml $ geml list cli.geml | tail -4 #intro-before-cmd prose L5-5 #cmd code L6-8 #n note L10-12 #next heading h2 L14-16 Next $ geml delete cli.geml '#cmd' warning: unresolved reference `#cmd` (line 12) — left dangling by delete; run 'geml check' to see it as an error wrote cli.geml exit 0:删除照做 $ geml check cli.geml error: unresolved reference `#cmd` (line 12) 1 error(s), 0 warning(s) exit 1
| 动词 | 对引用的态度 |
|---|---|
| rename | 改 id 和每一处引用,id 边界安全 |
| add | 片段保留自己的 id;与文档已有 id 冲突则拒绝 |
| delete | 留下悬空引用是 warning 不是拒绝:删除是作者的明确意图,check 再把它当 error |
| set | 结果解析不过就不写 |
默认输出是文档模型 JSON;Markdown 是有损的一边。
$ geml cli.geml --to md note: heading id/attributes dropped (Markdown has no attribute syntax) error: unresolved reference `#cmd` (line 12) 诊断照报,输出照给 --- title: cli probe --- # Intro Hello world paragraph. > added ## Next $ printf '# T\n\nsome md\n' | geml - --from md --to geml # T some md
| 标志 | 得到 | 备注 |
|---|---|---|
| --to json | 文档模型 | 默认;§8.4 一致性面的形状 |
| --to html | 自包含 HTML | --fragment 只给正文标记,资产走 pageAssets;本系列右侧标签由此而来 |
| --to md | Markdown | 有损:标题 id、属性丢,note 块变引用块,data 变代码块;会加 note 说明 |
| --to geml | 规范重排 | data 的 json 重排为两空格缩进 |
| --from md | 把 Markdown 读成 GEML | 扩展名是 .md 时自动推断;geml notes.md 直接可用 |
| --from json | 把 --to json 的结果读回 | 往返 |
--from md 自动推断),什么都不转换、不写;这就是 geml 技能里「按块读长 Markdown」的用法。所有动词共用同一套选择器。
| 写法 | 指什么 | 出处 · 状态 |
|---|---|---|
| #id | 声明了 id 的块;标题 id 是整节 | §4 · 已定 |
| '## Heading' | 按标题文字,同样是整节 | --help · 已定 |
| L27 · L27-58 | 包含这些行的最小块 | --help · 已定 |
| #a-between-b · #c-before-n · #c-after-p | 两块之间散文的派生地址,list 会打印 | §4 · 已定 |
| === type · type@hash | 匿名块在 list 里的显示;type@hash 可粘回 get | 实测 |
| #fy[2]["Q1"] · #fy[summary] · #meta["title"] · #cfg["tags"][1] | 表格行/格/列、汇总格、meta 值、data 值树节点 | GEP-0011 · 草案,本分支已实现(第 2、3 页) |
| #vendor#contacts#email | form 内的字段 | GEP-0008 · 草案,未实现(第 6 页) |
| other.geml#id · other.geml#fy[2] | 跨文档,同上任意形式 | §5.2 · GEP-0011 |
cli.geml 在会话 scratchpad 里跑,用仓库内当前构建,1.9.2。
| 命令 | 结果 | 对应 |
|---|---|---|
| list · find(命中 / 未命中)· get L6 · get --head / --body | 如上;find 未命中 exit 1 | 读写、看板 4–6 |
| set 塞入未关闭块 | exit 1 not written,文件不动 | 看板 8 |
| rename · add --after · delete · check | 引用同改;片段插入;悬空 warning 后 check exit 1 | 改、看板 9–11 |
| --to md · - --from md --to geml | 有损 note;stdin Markdown 读成 GEML | 转换 |
| geml --help · geml history · geml codemap | 动词与用法文本 | 看板 1–3、12–15 |