@geml/geml 1.4.2 → 1.4.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/dist/history.d.ts CHANGED
@@ -70,6 +70,16 @@ export declare function listRevisions(historyPath: string): RevisionInfo[];
70
70
  /** Resolve a revision selector to its id + reconstructed full text. Selectors:
71
71
  * `-N` (N revisions back from current; `-0` is the tip), `latest`/`current`, or
72
72
  * an unambiguous id prefix/suffix (the same forms `restore` accepts). */
73
+ /** Resolve a revision selector to its id, for EVERY command that takes one.
74
+ *
75
+ * There is exactly one selector grammar — `0` (the tip), `-N` (N revisions
76
+ * back), or an unambiguous revision id (prefix, suffix, or exact) — and it is
77
+ * the grammar `history log` prints in its first column, so its output is
78
+ * copy-pasteable into `revert --rev`, `history show`, and `history restore`
79
+ * alike. Keeping this in one function is what makes that true: it used to be
80
+ * written twice, and the copy in `restore` never grew the `0`/`-N` arm, so the
81
+ * selectors `history log` advertised were rejected by `history show`. */
82
+ export declare function resolveRevision(h: History, selector: string): string;
73
83
  export declare function resolveContent(historyPath: string, selector: string): {
74
84
  id: string;
75
85
  text: string;
package/dist/history.js CHANGED
@@ -591,12 +591,7 @@ export function verify(historyPath, gemlPath) {
591
591
  }
592
592
  export function restore(o) {
593
593
  const h = parseHistory(o.historyPath);
594
- // accept an unambiguous id prefix
595
- const ids = [...h.revisions.keys()];
596
- const matches = ids.filter((x) => x === o.revision || x.startsWith(o.revision) || x.endsWith(o.revision));
597
- if (matches.length !== 1)
598
- throw new Error(`history: revision selector "${o.revision}" matched ${matches.length} revisions`);
599
- const target = matches[0];
594
+ const target = resolveRevision(h, o.revision); // `0` | `-N` | id — see resolveRevision
600
595
  const content = reconstruct(h, target);
601
596
  if (o.write) {
602
597
  if (existsSync(o.gemlPath)) {
@@ -648,27 +643,33 @@ export function listRevisions(historyPath) {
648
643
  /** Resolve a revision selector to its id + reconstructed full text. Selectors:
649
644
  * `-N` (N revisions back from current; `-0` is the tip), `latest`/`current`, or
650
645
  * an unambiguous id prefix/suffix (the same forms `restore` accepts). */
651
- export function resolveContent(historyPath, selector) {
652
- const h = parseHistory(historyPath);
653
- const chain = chainFrom(h); // chain[0] = current tip
654
- let id;
655
- const off = /^-(\d+)$/.exec(selector);
646
+ /** Resolve a revision selector to its id, for EVERY command that takes one.
647
+ *
648
+ * There is exactly one selector grammar — `0` (the tip), `-N` (N revisions
649
+ * back), or an unambiguous revision id (prefix, suffix, or exact) — and it is
650
+ * the grammar `history log` prints in its first column, so its output is
651
+ * copy-pasteable into `revert --rev`, `history show`, and `history restore`
652
+ * alike. Keeping this in one function is what makes that true: it used to be
653
+ * written twice, and the copy in `restore` never grew the `0`/`-N` arm, so the
654
+ * selectors `history log` advertised were rejected by `history show`. */
655
+ export function resolveRevision(h, selector) {
656
+ const off = /^(0|-\d+)$/.exec(selector);
656
657
  if (off) {
657
- const n = Number(off[1]);
658
+ const chain = chainFrom(h); // chain[0] = current tip
659
+ const n = Math.abs(Number(selector)); // "0" -> 0 (tip), "-1" -> 1 back, …
658
660
  if (n >= chain.length)
659
- throw new Error(`history: offset -${n} is out of range (only ${chain.length} revision(s))`);
660
- id = chain[n].id;
661
- }
662
- else if (selector === "latest" || selector === "current") {
663
- id = chain[0].id;
664
- }
665
- else {
666
- const ids = [...h.revisions.keys()];
667
- const matches = ids.filter((x) => x === selector || x.startsWith(selector) || x.endsWith(selector));
668
- if (matches.length !== 1)
669
- throw new Error(`history: revision selector "${selector}" matched ${matches.length} revisions`);
670
- id = matches[0];
661
+ throw new Error(`history: offset ${selector} is out of range (only ${chain.length} revision(s))`);
662
+ return chain[n].id;
671
663
  }
664
+ const ids = [...h.revisions.keys()];
665
+ const matches = ids.filter((x) => x === selector || x.startsWith(selector) || x.endsWith(selector));
666
+ if (matches.length !== 1)
667
+ throw new Error(`history: revision selector "${selector}" matched ${matches.length} revisions`);
668
+ return matches[0];
669
+ }
670
+ export function resolveContent(historyPath, selector) {
671
+ const h = parseHistory(historyPath);
672
+ const id = resolveRevision(h, selector);
672
673
  return { id, text: reconstruct(h, id) };
673
674
  }
674
675
  /** Walk the chain newest→oldest; return the first revision whose block (as
package/dist/inline.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // GEML reference parser — Milestone 2: inline content (§5).
2
2
  //
3
- // Parses the inline grammar of flow blocks (paragraphs, headings, list items):
4
- // escapes, code spans, inline math, images, links, auto-references, footnote
5
- // references, then emphasis/strong/strike — in the §5.3 priority order. Every
6
- // internal/cross-document reference is reported to a `RefSink` so the document
7
- // layer can resolve and validate it at build time (§8).
3
+ // Parses the inline grammar of unfenced blocks (paragraphs, headings, list
4
+ // items): escapes, code spans, inline math, images, links, auto-references,
5
+ // footnote references, then emphasis/strong/strike — in the §5.3 priority order.
6
+ // Every internal/cross-document reference is reported to a `RefSink` so the
7
+ // document layer can resolve and validate it at build time (§8).
8
8
  import { parseAttrs } from "./attrs.js";
9
9
  const MAX_INLINE_NESTING = 100; // cap parseInline<->scanAtoms recursion (R2-7 DoS)
10
10
  // §4: the source pattern of a `{{key}}` metadata reference. Owned here as the
@@ -455,8 +455,8 @@ export function parseInline(s, line, sink, depth = 0) {
455
455
  // call stack (R2-7). Degrade the over-deep content to text — emphasis only,
456
456
  // no further link recursion — and flag it; never throw RangeError.
457
457
  const diags = sink.diags;
458
- if (Array.isArray(diags) && !diags.some((d) => d.message.startsWith("inline nesting too deep")))
459
- diags.push({ severity: "error", message: `inline nesting too deep (max ${MAX_INLINE_NESTING})`, line });
458
+ if (Array.isArray(diags) && !diags.some((d) => d.code === "inline-nesting-too-deep"))
459
+ diags.push({ severity: "error", code: "inline-nesting-too-deep", message: `inline nesting too deep (max ${MAX_INLINE_NESTING})`, line });
460
460
  return mergeText(emphasize(s));
461
461
  }
462
462
  const atoms = scanAtoms(s, line, sink, depth);
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ export interface McpOptions {
3
+ root: string;
4
+ history: boolean;
5
+ }
6
+ /** Configure the server. Exported so the suite can point it at a temp dir. */
7
+ export declare function configure(o: Partial<McpOptions>): McpOptions;
8
+ export declare function resolveInRoot(file: string): string;
9
+ export interface Tool {
10
+ name: string;
11
+ description: string;
12
+ inputSchema: unknown;
13
+ run: (args: Record<string, any>) => unknown;
14
+ }
15
+ export declare const TOOLS: Tool[];
16
+ export declare function handleLine(line: string, write?: (s: string) => void): void;
17
+ export declare const MCP_USAGE = "usage: geml mcp --root <dir> [--no-history]\n\n Serve GEML document CRUD over the MCP stdio transport (JSON-RPC 2.0).\n\n --root <dir> REQUIRED. Root directory holding the .geml documents.\n Relative paths resolve against the server process's CWD,\n which the CLIENT chooses \u2014 pass an absolute path.\n Every path a client names is confined to this directory;\n a client cannot widen or override it.\n --no-history Do not auto-commit a .gemlhistory revision before each\n write. Default is to commit, so geml_revert_block always\n has a revision to undo to.\n\n Register with a client:\n claude mcp add geml -- geml mcp --root /abs/path/to/docs";
18
+ export declare function parseArgs(args: string[]): McpOptions;