protoml-parser
Version:
ProtoML is a lightweight, declarative markup language designed for writing and structuring meeting protocols, notes and task lists in a human-readable and machine-parseable format.
977 lines (918 loc) • 115 kB
JavaScript
const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");
const { parseMacroDefinition } = require("../core/macroDefinition");
const repoRoot = path.resolve(__dirname, "..", "..");
const docsDir = path.join(repoRoot, "docs");
const chmDir = path.join(docsDir, "chm");
const htmlDocsDir = path.join(chmDir, "html_docs");
const projectFile = path.join(chmDir, "protoml-help.hhp");
const tocFile = path.join(chmDir, "TOC.hhc");
const indexFile = path.join(chmDir, "Index.hhk");
const tocHtmlFile = path.join(chmDir, "toc.html");
const embeddedTocHtmlFile = path.join(chmDir, "toc.embedded.html");
const helpViewerFile = path.join(chmDir, "help-viewer.html");
const stylesFile = path.join(htmlDocsDir, "help.css");
const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
const protoVersion = packageJson.version || "unknown";
const compilerCandidates = [
process.env.HHC_EXE,
"C:\\Program Files (x86)\\HTML Help Workshop\\hhc.exe",
"C:\\Program Files\\HTML Help Workshop\\hhc.exe",
].filter(Boolean);
function ensureDir(dir) {
fs.mkdirSync(dir, { recursive: true });
}
function writeFile(filePath, content) {
fs.writeFileSync(filePath, content, "utf8");
}
function escapeHtml(value) {
return String(value || "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """);
}
function escapeAttribute(value) {
return escapeHtml(value).replace(/'/g, "'");
}
function slugify(value) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "_")
.replace(/^_+|_+$/g, "") || "topic";
}
function titleCaseFromName(value) {
return String(value || "")
.replace(/[_-]+/g, " ")
.replace(/\s+/g, " ")
.trim()
.replace(/\b\w/g, (char) => char.toUpperCase());
}
function extractSection(raw, sectionName) {
const trimmed = String(raw || "").trimStart();
if (trimmed.startsWith("@help")) {
return extractKnownSection(raw, sectionName, ["name", "docs", "examples"]);
}
if (trimmed.startsWith("@new_macro")) {
return extractKnownSection(raw, sectionName, ["name", "docs", "template"]);
}
const escaped = sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const header = new RegExp(`^=${escaped}:`, "m").exec(raw);
if (!header || header.index == null) return "";
const contentStart = header.index + header[0].length;
const rest = raw.slice(contentStart);
const nextSectionMatch = /^=[a-zA-Z0-9_-]+:/m.exec(rest);
const contentEnd = nextSectionMatch ? contentStart + nextSectionMatch.index : raw.length;
return raw.slice(contentStart, contentEnd).trim();
}
function extractKnownSection(raw, sectionName, orderedSectionNames) {
const escaped = sectionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const header = new RegExp(`^=${escaped}:`, "m").exec(raw);
if (!header || header.index == null) return "";
const contentStart = header.index + header[0].length;
const currentIndex = orderedSectionNames.indexOf(sectionName);
const nextSectionNames = currentIndex >= 0
? orderedSectionNames.slice(currentIndex + 1)
: [];
if (!nextSectionNames.length) {
return raw.slice(contentStart).trim();
}
const nextSectionPattern = nextSectionNames
.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("|");
const rest = raw.slice(contentStart);
const nextSectionMatch = new RegExp(`^=(?:${nextSectionPattern}):`, "m").exec(rest);
const contentEnd = nextSectionMatch ? contentStart + nextSectionMatch.index : raw.length;
return raw.slice(contentStart, contentEnd).trim();
}
function inlineFormat(text) {
return escapeHtml(text).replace(/`([^`]+)`/g, "<code>$1</code>");
}
function renderMarkdownish(text, options = {}) {
const lines = String(text || "").replace(/\r/g, "").split("\n");
const html = [];
let paragraph = [];
let listItems = [];
let codeFence = null;
let codeLines = [];
const macroCache = options.macroCache || {};
function flushParagraph() {
if (!paragraph.length) return;
html.push(`<p>${inlineFormat(paragraph.join(" "))}</p>`);
paragraph = [];
}
function flushList() {
if (!listItems.length) return;
html.push("<ul>");
for (const item of listItems) {
html.push(`<li>${inlineFormat(item)}</li>`);
}
html.push("</ul>");
listItems = [];
}
function flushCode() {
if (!codeFence) return;
const langClass = codeFence ? ` class="language-${escapeAttribute(codeFence)}"` : "";
html.push(`<pre><code${langClass}>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
codeFence = null;
codeLines = [];
}
function renderMacroLine(line) {
const match = String(line || "").trim().match(/^@@macro=([\w-]+):(.*)$/);
if (!match) return null;
const [, macroName, rawParams] = match;
const template = macroCache[macroName];
if (!template) {
return `<pre><code>${escapeHtml(line)}</code></pre>`;
}
const params = {};
for (const entry of rawParams.split(";")) {
const [key, ...rest] = entry.split("=");
if (!key) continue;
params[key.trim()] = rest.join("=").trim();
}
return String(template).replace(/\{\{(.*?)\}\}/g, (_, key) => {
const normalizedKey = String(key).trim();
return params[normalizedKey] ?? "";
});
}
for (const line of lines) {
const trimmed = line.trim();
const fenceMatch = trimmed.match(/^```([a-zA-Z0-9_-]+)?$/);
if (fenceMatch) {
if (codeFence != null) {
flushCode();
} else {
flushParagraph();
flushList();
codeFence = fenceMatch[1] || "";
}
continue;
}
if (codeFence != null) {
codeLines.push(line);
continue;
}
if (!trimmed) {
flushParagraph();
flushList();
continue;
}
const renderedMacro = renderMacroLine(trimmed);
if (renderedMacro) {
flushParagraph();
flushList();
html.push(renderedMacro);
continue;
}
if (trimmed.startsWith("- ")) {
flushParagraph();
listItems.push(trimmed.slice(2).trim());
continue;
}
paragraph.push(trimmed);
}
flushParagraph();
flushList();
flushCode();
return html.join("\n");
}
function loadBundledMacroCache() {
const macrosDir = path.join(repoRoot, "macros");
const cache = {};
function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(fullPath);
continue;
}
if (!entry.isFile() || !entry.name.endsWith(".pml")) {
continue;
}
const parsed = parseMacroDefinition(fs.readFileSync(fullPath, "utf8"));
if (parsed?.name && parsed.template) {
cache[parsed.name] = parsed.template;
}
}
}
if (fs.existsSync(macrosDir)) {
walk(macrosDir);
}
return cache;
}
function htmlTemplate(title, body) {
return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${escapeHtml(title)} - ProtoML ${escapeHtml(protoVersion)}</title>
<link rel="stylesheet" href="help.css">
</head>
<body>
<nav class="topnav">
<a href="../toc.html">Contents</a>
<span class="version">ProtoML ${escapeHtml(protoVersion)}</span>
</nav>
<main class="page">
${body}
</main>
</body>
</html>
`;
}
function buildGuidePages() {
return [
{
file: "00_overview.html",
title: "Documentation Index",
group: "Guides",
keywords: ["overview", "documentation", "index", "protoml", "intro", "what is protoml"],
body: `
<h1>ProtoML Documentation</h1>
<p>ProtoML is a lightweight document language for meetings, protocols, task lists, reusable content, shared participants, shared tags, and structured exports.</p>
<p>The repository includes the parser, renderers, CLI tools, bundled macros, the Electron viewer, and Windows CHM help generation.</p>
<h2>Start here</h2>
<ul>
<li><a href="01_installation.html">Installation And Release Use</a></li>
<li><a href="02_quick_start.html">Quick Start</a></li>
<li><a href="06_concepts.html">ProtoML Concepts</a></li>
<li><a href="07_authoring_guide.html">Authoring Guide</a></li>
<li><a href="08a_reuse_and_imports.html">Reuse And Imports Guide</a></li>
<li><a href="08_macro_registry_guide.html">Own Macro Registry Guide</a></li>
<li><a href="13_macro_security_trust_model.html">Macro Security And Trust Model</a></li>
<li><a href="14_validation_and_analysis_workflows.html">Validation And Analysis Workflows</a></li>
<li><a href="15_governance_documents.html">Governance Documents</a></li>
<li><a href="16_release_and_packaging.html">Release And Packaging</a></li>
<li><a href="11_examples_cookbook.html">Examples Cookbook</a></li>
</ul>
<h2>By role</h2>
<ul>
<li>Authors: Quick Start, Authoring Guide, Reuse And Imports Guide, Outputs And Rendering</li>
<li>Macro users: Macros Guide, Reuse And Imports Guide, Own Macro Registry Guide, Macro Security And Trust Model, Examples Cookbook</li>
<li>CLI users: CLI Reference, Outputs And Rendering, Validation And Analysis Workflows, Release And Packaging</li>
<li>Viewer users: Viewer Guide</li>
<li>Governance-focused users: Governance Documents, Validation And Analysis Workflows</li>
<li>Users exploring the language surface: Reference Map</li>
</ul>
<h2>What lives where</h2>
<ul>
<li><code>README.md</code> for project overview, installation, quick start, and release structure</li>
<li><code>docs/*.pml</code> for built-in CLI help topics via <code>protoparser --docs <topic></code></li>
<li><code>docs/chm/</code> for generated Windows CHM help project files and compiled output</li>
<li><code>examples/</code> for runnable feature and macro-registry examples</li>
</ul>
`,
},
{
file: "01_installation.html",
title: "Installation And Release Use",
group: "Guides",
keywords: ["install", "npm", "release", "dist", "chm", "executables"],
body: `
<h1>Installation And Release Use</h1>
<h2>Install from npm</h2>
<pre><code>npm install -g protoml-parser</code></pre>
<h2>Install from source</h2>
<pre><code>git clone https://github.com/Ente/protoml-parser.git
cd protoml-parser
npm install -g .</code></pre>
<h2>Release structure</h2>
<ul>
<li>Source code for development and npm publishing</li>
<li>Windows and Linux executables in <code>dist/</code></li>
<li>Native Windows CHM help in <code>docs/chm/</code> and copied to <code>dist/</code> during release preparation</li>
<li><code>SHA256SUMS.txt</code> for release checksums</li>
</ul>
<h2>Version check</h2>
<pre><code>protoparser --version</code></pre>
<h2>Continue with</h2>
<p>After installation, move on to <a href="02_quick_start.html">Quick Start</a> for the first end-to-end document workflow.</p>
`,
},
{
file: "02_quick_start.html",
title: "Quick Start",
group: "Guides",
keywords: ["quick start", "first file", "render", "viewer"],
body: `
<h1>Quick Start</h1>
<p>This is the shortest useful end-to-end ProtoML workflow.</p>
<h2>1. Install</h2>
<pre><code>npm install -g protoml-parser</code></pre>
<h2>2. Create <code>test.pml</code></h2>
<pre><code>@tags_import "_tags.pml"
@protocol "Weekly Sync - {{date}}"
@date:17.04.2026
@participants
=lead:Jane Doe,jdoe,jdoe@example.com
=ops:Max Mustermann,mmustermann,max@example.com
@subjects
=0:Release status
=1:Next steps
@tasks
-[ ] Prepare package notes @ptp=lead =0 @tag=important
-[ ] Validate release build @ptp=ops =1 @tag=review
@meeting "Minutes"
# Weekly Sync
## Participants
@@e=lead, @@e=ops
## Topics
@@e=0
@@e=1</code></pre>
<h2>3. Create <code>_tags.pml</code></h2>
<pre><code>@title "Shared Workflow Tags"
@tags
=important:High priority
=review:Needs review</code></pre>
<p>This first split already demonstrates a useful ProtoML habit: keep reusable classification in a shared tags file and keep meeting-specific content in the meeting file.</p>
<h2>4. Render HTML</h2>
<pre><code>protoparser "test.pml" html</code></pre>
<p>HTML is the best first output because it shows most of the structure, styling, tags, and macro behavior in the richest form.</p>
<h2>5. Open it in the viewer</h2>
<pre><code>protoviewer "test.pml"</code></pre>
<p>Use the viewer when you want a local review loop while editing instead of thinking in terms of generated export files.</p>
<h2>6. Add a first macro</h2>
<pre><code>@macro badge "{{macro_dir}}/badge.pml"
@@macro=badge:text=review</code></pre>
<p>This is a good first macro because it adds presentation reuse without changing the document model itself.</p>
<h2>7. Explore the feature suite</h2>
<pre><code>protoparser "examples/feature-suite/main_demo.pml" html
protoparser analyze "examples/feature-suite/main_demo.pml" statistics
protoparser tags "examples/feature-suite/_workflow_tags.pml" statistics</code></pre>
<p>The feature suite is where you should look once the first file makes sense. It shows imports, tag merging, macros, analysis, and richer cross-file behavior.</p>
<h2>Path recommendation</h2>
<p>Wrap file paths in double quotes whenever possible, especially for paths with spaces or <code>{{macro_dir}}</code>.</p>
<h2>What you learned in this first pass</h2>
<ul>
<li>ProtoML separates structured data from the reader-facing narrative</li>
<li>Shared tags are a practical way to keep task language consistent</li>
<li>HTML and the viewer are the easiest first ways to inspect a document</li>
<li>Macros add reusable rendering patterns on top of a stable document structure</li>
</ul>
<h2>Next reading</h2>
<ul>
<li><a href="06_concepts.html">ProtoML Concepts</a></li>
<li><a href="07_authoring_guide.html">Authoring Guide</a></li>
<li><a href="04_macros_guide.html">Macros Guide</a></li>
<li><a href="08a_reuse_and_imports.html">Reuse And Imports Guide</a></li>
<li><a href="08_macro_registry_guide.html">Own Macro Registry Guide</a></li>
</ul>
`,
},
{
file: "03_cli_workflows.html",
title: "CLI Reference",
group: "Guides",
keywords: ["cli", "workflow", "reference", "validate", "analyze", "register", "bundle"],
body: `
<h1>CLI Reference</h1>
<p>ProtoML currently exposes these main binaries: <code>protoml-parser</code>, <code>protoparser</code>, <code>protoml-viewer</code>, and <code>protoviewer</code>.</p>
<h2>Main syntax</h2>
<pre><code>protoparser [options] <filename> <format>
protoparser [options] <filename> <format> <output_dir></code></pre>
<h2>Main render formats</h2>
<pre><code>protoparser "test.pml" html
protoparser "test.pml" pdf
protoparser "test.pml" json
protoparser "test.pml" markdown
protoparser "test.pml" text</code></pre>
<p>Most users should think of these as three classes of outputs: reader-facing rich output, archival/static output, and machine-readable output.</p>
<h2>Analysis and validation</h2>
<pre><code>protoparser validate "test.pml"
protoparser tags "_tags.pml" validate
protoparser tags "_tags.pml" statistics
protoparser macros "test.pml"
protoparser trust "test.pml"
protoparser analyze "test.pml" statistics
protoparser analyze "test.pml" graph
protoparser register "meetings" statistics
protoparser bundle "test.pml"</code></pre>
<p>These commands are where ProtoML stops being just a renderer and starts acting like a document system. They let you inspect structure, governance metadata, tag usage, macro usage, import relationships, and document trust state.</p>
<p><code>register</code> here means a governance report over document folders. It is separate from macro registries and the <code>macro_install ..._registry</code> commands used for external macro packs.</p>
<h2>Project and packaging commands</h2>
<pre><code>protoparser scaffold meeting "./demo"
protoparser init "./project"</code></pre>
<p>Use these when you want a repeatable starting structure instead of hand-creating the first files.</p>
<h2>Macro commands</h2>
<pre><code>protoparser --listMacros "{{macro_dir}}"
protoparser --macroHelp "{{macro_dir}}/finance/f_entry.pml"
protoparser --listMacrosJson "{{macro_dir}}"</code></pre>
<p>These are especially useful when a team wants to discover what is already bundled before inventing new macros.</p>
<h2>Documentation commands</h2>
<pre><code>protoparser --listDocs
protoparser --docs meeting
protoparser chm
protoparser chm path
protoparser chm download</code></pre>
<p>The built-in docs help with precise topic lookup, while the CHM guides are better for learning and orientation.</p>
<h2>Trust and signing commands</h2>
<pre><code>protoparser trust "test.pml"
protoparser sign macro "./macros/warn_box.pml" "./keys/alice-private.pem" "Alice" alice-main
protoparser sign pml "./governance/release-approval.pml" "./keys/alice-private.pem" "Alice" alice-main
protoparser verify macro "./macros/warn_box.pml" -trustRegistry="./my-registry"
protoparser verify pml "./governance/release-approval.pml" -trustRegistry="./authors-registry"</code></pre>
<p>These commands provide the lightweight trust workflow: static risk checks, detached signatures, and author trust lookup via registry sources. The same detached signature model works for normal governance-style <code>.pml</code> files as well as macros.</p>
<h2>Useful options</h2>
<ul>
<li><code>-v</code>, <code>-vv</code>, <code>-vvv</code> for verbosity on text-style reports</li>
<li><code>-theme=<name></code> for HTML and PDF themes</li>
<li><code>-strict</code> for stricter validation behavior</li>
<li><code>-trust=off|warn|strict</code> and repeatable <code>-trustRegistry=...</code> flags for trust enforcement and author lookup</li>
<li><code>-graphView=<mode></code> and <code>-graphDirection=<dir></code> for graph rendering</li>
</ul>
<h2>Common workflow patterns</h2>
<ul>
<li>Authoring loop: <code>protoviewer</code> or <code>protoparser "...pml" html</code></li>
<li>Quality loop: <code>validate</code>, <code>analyze</code>, and <code>macros</code></li>
<li>Governance loop: <code>register "<dir>" statistics</code></li>
<li>Packaging loop: <code>bundle</code> for a single-file archive form</li>
</ul>
<h2>Verbosity</h2>
<ul>
<li><code>-v</code> adds a compact structural overview</li>
<li><code>-vv</code> adds deeper lists and nested detail</li>
<li><code>-vvv</code> adds the most verbose diagnostics available for the command</li>
</ul>
<h2>Related guides</h2>
<ul>
<li><a href="10_outputs_and_rendering.html">Outputs And Rendering</a></li>
<li><a href="09_viewer_guide.html">Viewer Guide</a></li>
<li><a href="05_windows_help_and_dev.html">Windows Help And Development</a></li>
<li><a href="15a_signed_governance_workflow.html">Signed Governance Workflow Tutorial</a></li>
</ul>
`,
},
{
file: "04_macros_guide.html",
title: "Macros Guide",
group: "Guides",
keywords: ["macros", "external macros", "macro install", "registry", "macro packs", "macros import"],
body: `
<h1>Macros Guide</h1>
<p>ProtoML macros provide reusable rendering templates, mainly for HTML-oriented output.</p>
<h2>What macros are good for</h2>
<p>Macros are best when the same visual or structured output pattern appears repeatedly across documents. They help you standardize presentation without turning your documents into copy-pasted HTML fragments.</p>
<h2>Using bundled macros</h2>
<pre><code>@macro badge "{{macro_dir}}/badge.pml"
@@macro=badge:text=review</code></pre>
<p>Prefer quotes around paths in CLI usage.</p>
<pre><code>protoparser --listMacros "{{macro_dir}}"
protoparser --macroHelp "{{macro_dir}}/finance/f_entry.pml"</code></pre>
<h2>Bundled macro areas</h2>
<ul>
<li>standalone: <code>alert</code>, <code>badge</code>, <code>calendar_event</code>, <code>clicktoreveal</code>, <code>codeblock_copy</code>, <code>image</code>, <code>progress_bar</code>, <code>quote</code>, <code>spoiler</code>, <code>tts</code>, <code>warn_box</code></li>
<li>grouped sets: <code>actions</code>, <code>decisions</code>, <code>finance</code>, <code>highlight</code>, <code>summary</code>, <code>taskflow</code>, <code>timeline</code></li>
</ul>
<h2>Writing custom macros</h2>
<pre><code>@new_macro
=name:statusPill
=template:
<span class="status-pill status-{{state}}">{{label}}</span></code></pre>
<p>When writing a macro, keep the input parameters obvious and stable. The best custom macros are easy to understand from the call site alone.</p>
<h2>When to prefer imports, macros, or themes</h2>
<ul>
<li>Use imports when the repeated thing is mostly content</li>
<li>Use macros when you need a reusable component with its own structure and presentation, such as alerts, badges, timelines, summaries, or cards</li>
<li>Use themes when the desired effect is document-wide styling such as colors, typography, spacing, or general page chrome</li>
<li>Use tags when the repeated thing is really classification, not rendering</li>
</ul>
<h2>External macro pack workflow</h2>
<pre><code>protoparser macro_install init
protoparser macro_install init_registry "./my-registry"
protoparser macro_install init_pack "legal-pack" "./my-registry"
protoparser macro_install add_registry "./my-registry"
protoparser macro_install install "legal-pack"</code></pre>
<h2>Use installed packs in a document</h2>
<pre><code>@macros_import ".protoml/macro-packs/macros.index.pml"</code></pre>
<p>This is useful when multiple teams or projects should consume the same curated macro set instead of copying files around manually.</p>
<h2>Practical advice</h2>
<ul>
<li>Use imports for content reuse and macros for rendering reuse</li>
<li>Prefer themes over macros when you only want to change the overall visual look of the document</li>
<li>Start with bundled macros before creating a shared pack workflow</li>
<li>Treat untrusted macro files as unsafe, especially when they include JavaScript</li>
<li>Keep macro names and parameter names predictable across a macro family</li>
</ul>
<h2>Related guides</h2>
<ul>
<li><a href="06_concepts.html">ProtoML Concepts</a></li>
<li><a href="08a_reuse_and_imports.html">Reuse And Imports Guide</a></li>
<li><a href="08_macro_registry_guide.html">Own Macro Registry Guide</a></li>
<li><a href="11_examples_cookbook.html">Examples Cookbook</a></li>
<li><a href="03_cli_workflows.html">CLI Reference</a></li>
</ul>
`,
},
{
file: "05_windows_help_and_dev.html",
title: "Windows Help And Development",
group: "Guides",
keywords: ["windows help", "chm", "development", "build", "html help workshop"],
body: `
<h1>Windows Help And Development</h1>
<h2>Open the local CHM help</h2>
<pre><code>protoparser chm
protoparser chm path
protoparser chm download</code></pre>
<h2>Install HTML Help Workshop first</h2>
<p>If you want to compile the CHM on your own Windows machine, install Microsoft HTML Help Workshop first.</p>
<p>Archived installer:</p>
<pre><code>https://web.archive.org/web/20160201063255/http://download.microsoft.com/download/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe</code></pre>
<h2>Build the CHM project files</h2>
<pre><code>npm run build:chm:project</code></pre>
<h2>Compile the CHM</h2>
<pre><code>npm run build:chm</code></pre>
<h2>Other useful development commands</h2>
<pre><code>npm run build:web
npm run dev
npm run build:exe
npm run install:local</code></pre>
<h2>Related guides</h2>
<ul>
<li><a href="03_cli_workflows.html">CLI Reference</a></li>
<li><a href="00_overview.html">Documentation Index</a></li>
</ul>
`,
},
{
file: "06_concepts.html",
title: "ProtoML Concepts",
group: "Guides",
keywords: ["concepts", "document model", "imports", "macros", "tags"],
body: `
<h1>ProtoML Concepts</h1>
<p>ProtoML is not just Markdown with a few extra commands. It behaves more like a small document system for structured protocols, reusable building blocks, and machine-readable exports.</p>
<h2>Document shape</h2>
<p>A typical ProtoML file combines metadata, structured blocks such as <code>@participants</code> or <code>@tasks</code>, and a freeform <code>@meeting</code> block for the readable narrative.</p>
<p>The structured blocks act as the source of truth. The meeting block is where that structured data is turned into a readable document.</p>
<h2>IDs and references</h2>
<ul>
<li><code>=lead:Jane Doe,...</code> defines a participant ID</li>
<li><code>=0:Release status</code> defines a subject ID</li>
<li><code>@@e=lead</code> echoes a known entry into the meeting text</li>
<li><code>@@ref=participants:lead:email</code> fetches a specific field</li>
</ul>
<p>This is what makes ProtoML maintainable: values are defined once and then referenced consistently.</p>
<h2>Imports vs. macros</h2>
<p>Use <code>@import</code> when you want to reuse content. Use <code>@macro</code> when you want reusable rendering templates.</p>
<ul>
<li>Imports help you split large documents into maintainable pieces</li>
<li>Macros help you standardize repeated render patterns</li>
<li>They can be combined, but they solve different problems</li>
</ul>
<h2>Tags as shared classification</h2>
<p>Tags are ProtoML's reusable classification layer for tasks. Define them locally with <code>@tags</code>, share them with <code>@tags_import</code>, and analyze them with <code>protoparser tags ...</code>. Participant lists can be reused in a similar way with <code>@participants_import</code>.</p>
<p>That means ProtoML can support not just writing, but also reporting on work across a document set.</p>
<h2>When to use what</h2>
<ul>
<li>Use metadata for document-level facts</li>
<li>Use structured blocks when data should be referenceable</li>
<li>Use <code>@meeting</code> for the human-readable story</li>
<li>Use <code>@import</code> to split content across files</li>
<li>Use <code>@macro</code> to standardize repeated rendered patterns</li>
<li>Use shared tags when multiple files should use the same task vocabulary</li>
</ul>
<h2>Related guides</h2>
<ul>
<li><a href="07_authoring_guide.html">Authoring Guide</a></li>
<li><a href="04_macros_guide.html">Macros Guide</a></li>
<li><a href="12_reference_map.html">Reference Map</a></li>
</ul>
`,
},
{
file: "07_authoring_guide.html",
title: "Authoring Guide",
group: "Guides",
keywords: ["authoring", "best practices", "writing", "documents"],
body: `
<h1>Authoring Guide</h1>
<p>This guide focuses on writing maintainable ProtoML documents, not on listing every syntax rule.</p>
<h2>Start with a consistent skeleton</h2>
<ol>
<li>document metadata</li>
<li>participants</li>
<li>subjects</li>
<li>tags or tag imports</li>
<li>tasks</li>
<li>meeting content</li>
<li>optional governance blocks such as signatures, approvals, references, or attachments</li>
</ol>
<p>This order keeps the structural source of truth near the top and the human-readable narrative near the bottom, which makes large files easier to review and diff.</p>
<h2>Name things for reuse</h2>
<p>Prefer stable, meaningful IDs such as <code>lead</code>, <code>pm</code>, <code>security</code>, <code>release</code>, or <code>review</code>.</p>
<p>A good rule is that an ID should still make sense three months later without reading the whole file again.</p>
<h2>Keep structured data in blocks</h2>
<p>If a value should be reused later, define it once in a block instead of duplicating it in prose.</p>
<ul>
<li>Participants belong in <code>@participants</code>, not copied repeatedly in meeting text</li>
<li>Use <code>@participants_import</code> when multiple documents share the same participant roster</li>
<li>Subjects belong in <code>@subjects</code>, then tasks and notes can point back to them</li>
<li>Approval and signature data belong in their dedicated blocks if they are referenced more than once</li>
</ul>
<h2>Use shared tags for teams and projects</h2>
<p>Move stable task vocabularies into shared <code>_tags.pml</code> files when multiple documents share the same workflow or cross-file analysis matters.</p>
<p>Keep local tags only when a label is temporary, one-off, or too project-specific to be worth sharing.</p>
<h2>Split large documents deliberately</h2>
<p>Use <code>@import</code> for appendices, reusable legal or policy text, standard meeting sections, or shared snippets.</p>
<p>The main document should still read like the table of contents and orchestration layer of the whole document, not like a random pile of imports.</p>
<h2>Use macros sparingly but intentionally</h2>
<p>Macros are best for repeated presentation patterns such as alerts, badges, summaries, timelines, and finance cards.</p>
<p>If the repeated thing is really content, choose imports. If you need a reusable rendered component with its own structure and presentation, choose macros. If you only want to change the overall document look, choose a renderer theme instead.</p>
<h2>Governance documents need extra discipline</h2>
<p>Establish conventions for <code>@record_id</code>, <code>@status</code>, <code>@author</code>, <code>@version</code>, <code>@effective_date</code>, <code>@valid_until</code>, and <code>@review_date</code>, then use <code>protoparser register "<dir>" statistics</code>.</p>
<h2>Worked example</h2>
<pre><code>@tags_import "_workflow_tags.pml"
@protocol "Platform Weekly Sync - {{date}}"
@date:17.04.2026
@author:Jane Doe
@status:review
@record_id:PLATFORM-WEEKLY-2026-04-17
@participants
=lead:Jane Doe,jdoe,jdoe@example.com
=ops:Max Mustermann,mmustermann,max@example.com
=sec:Alex Roe,aroe,alex@example.com
@subjects
=release:Release status
=security:Security review
=followup:Next actions
@tasks
-[ ] Finalize release notes @ptp=lead =release @tag=important
-[ ] Recheck deployment window @ptp=ops =release @tag=review
-[ ] Confirm exception handling for audit finding @ptp=sec =security @tag=blocked
@meeting "Weekly Minutes"
# Platform Weekly Sync
## Summary
Current focus: @@e=release
## Participants
@@e=lead, @@e=ops, @@e=sec
## Open points
Audit topic: @@e=security
Next section: @@e=followup</code></pre>
<h2>Why this example is maintainable</h2>
<ul>
<li>Document identity and lifecycle are visible at the top</li>
<li>Participant and subject IDs are stable and descriptive</li>
<li>Tasks point to structured subjects and tags instead of embedding everything in plain text</li>
<li>The meeting block reads clearly while still reusing structured values</li>
<li>The shared tags file keeps workflow classification consistent across multiple documents</li>
</ul>
<h2>Common authoring mistakes</h2>
<ul>
<li>Using throwaway IDs such as <code>1</code> or <code>x</code> for everything</li>
<li>Keeping important document metadata only in prose</li>
<li>Copying repeated names and labels instead of referencing them</li>
<li>Using macros where a plain import or structured block would be simpler</li>
<li>Letting large files grow without extracting reusable imported sections</li>
</ul>
<h2>Related guides</h2>
<ul>
<li><a href="06_concepts.html">ProtoML Concepts</a></li>
<li><a href="08a_reuse_and_imports.html">Reuse And Imports Guide</a></li>
<li><a href="11_examples_cookbook.html">Examples Cookbook</a></li>
<li><a href="12_reference_map.html">Reference Map</a></li>
</ul>
`,
},
{
file: "08a_reuse_and_imports.html",
title: "Reuse And Imports Guide",
group: "Guides",
keywords: ["imports", "reuse", "participants_import", "tags_import", "macros_import", "shared files"],
body: `
<h1>Reuse And Imports Guide</h1>
<p>ProtoML has several reuse mechanisms, and they solve different problems. This guide connects them into one practical workflow so you can choose the right one quickly.</p>
<h2>Choose the right reuse tool</h2>
<ul>
<li><code>@import name "file" pml|html</code> when you want to inject maintained content into the meeting output</li>
<li><code>@participants_import "file.pml"</code> when multiple documents should share the same participant roster</li>
<li><code>@tags_import "file.pml"</code> when multiple documents should share the same task vocabulary</li>
<li><code>@macros_import "file.pml"</code> when one generated or curated macro index should expose many macros at once</li>
<li><code>@macro name "file.pml"</code> when a document should register one concrete macro file directly</li>
</ul>
<h2>Shared participants</h2>
<pre><code>@participants_import "_participants.pml"
@tasks
-[ ] Prepare release notes @ptp=lead
@meeting "Minutes"
Lead: @@e=lead
Lead mail: @@ref=participants:lead:email</code></pre>
<p>Use this when teams, committees, or recurring meeting series keep reusing the same people. The participant file becomes the shared source of truth.</p>
<h2>Shared tags</h2>
<pre><code>@tags_import "_workflow_tags.pml"
@tasks
-[ ] Check deployment window @tag=review
-[ ] Confirm fix plan @tag=blocked</code></pre>
<p>Use shared tags when reporting and workflow consistency matter across many documents. This is the most common cross-file reuse mechanism after plain imports.</p>
<h2>Content imports</h2>
<pre><code>@import appendix "appendix.pml" pml
@import legal "legal_notice.html" html
@meeting "Minutes"
## Appendix
@@output=appendix
## Notice
@@import=legal</code></pre>
<p>Content imports are best for maintained snippets, appendices, reusable sections, or legal text that should live outside the main document.</p>
<h2>Direct macros vs. macro indexes</h2>
<pre><code>@macro badge "{{macro_dir}}/badge.pml"
@@macro=badge:text=review</code></pre>
<p>This direct form is best for a small number of known macros inside one document or repository.</p>
<pre><code>@macros_import ".protoml/macro-packs/macros.index.pml"
@@macro=decisionCard:title=Storage;text=Use the replicated tier</code></pre>
<p>This indexed form is best once you install packs through <code>macro_install</code> and want one shared entry point for many macros.</p>
<h2>Recommended file layout</h2>
<pre><code>meetings/
weekly-sync.pml
shared/
_participants.pml
_workflow_tags.pml
snippets/
appendix.pml
legal_notice.html</code></pre>
<p>This layout keeps shared assets explicit and avoids mixing long-lived vocabularies with one-off meeting text.</p>
<h2>Common mistakes</h2>
<ul>
<li>Using a macro when the repeated thing is really just maintained content</li>
<li>Keeping participant lists local in every meeting even though the same roster repeats weekly</li>
<li>Using local tags everywhere and then wondering why cross-file statistics are inconsistent</li>
<li>Registering many macros one by one when a generated <code>@macros_import</code> index would be cleaner</li>
</ul>
<h2>Rule of thumb</h2>
<ul>
<li>Reuse data with imports to blocks such as participants and tags</li>
<li>Reuse content with <code>@import</code></li>
<li>Reuse rendering with macros</li>
</ul>
<h2>Related guides</h2>
<ul>
<li><a href="07_authoring_guide.html">Authoring Guide</a></li>
<li><a href="04_macros_guide.html">Macros Guide</a></li>
<li><a href="08_macro_registry_guide.html">Own Macro Registry Guide</a></li>
<li><a href="14_validation_and_analysis_workflows.html">Validation And Analysis Workflows</a></li>
</ul>
`,
},
{
file: "08_macro_registry_guide.html",
title: "Own Macro Registry Guide",
group: "Guides",
keywords: ["macro registry", "own registry", "macro packs", "registry workflow", "macros import"],
body: `
<h1>Own Macro Registry Guide</h1>
<p>This guide shows how to build and maintain your own local ProtoML macro registry, publish packs into it, and consume those packs from a project meeting file.</p>
<h2>What this workflow is for</h2>
<p>Use a custom macro registry when you want a reusable, curated macro catalog for one team, one company, or one document domain instead of copying macro files between repositories.</p>
<p>This guide is about macro package registries only. It is not about <code>protoparser register "<dir>"</code>, which creates governance and status reports for document collections.</p>
<h2>Where a company registry can live</h2>
<ul>
<li>a simple internal web server that serves <code>protoml.registry.json</code> and pack files over HTTP or HTTPS</li>
<li>a shared local path such as <code>Z:\\protoml-registry</code> or <code>/mnt/protoml-registry</code></li>
<li>an intranet static host or normal artifact/file server</li>
</ul>
<p>You do not need a special registry backend. A company registry can be just static JSON plus files on a plain web server or network path.</p>
<h2>Choose the right trust path first</h2>
<ul>
<li>Use bundled <code>{{macro_dir}}</code> macros first when the shipped macro set already covers the need</li>
<li>Use a registry when you need reusable custom packs across multiple projects or teams</li>
<li>Use detached signatures plus a registry when your custom macros should resolve to <code>trusted</code></li>
<li>Use unsigned local macros only for exploratory work where <code>unknown</code> is acceptable</li>
</ul>
<h2>Create the registry</h2>
<pre><code>protoparser macro_install init_registry "./my-registry"</code></pre>
<p>This creates the local registry root and the <code>protoml.registry.json</code> index file.</p>
<h2>Create a pack inside the registry</h2>
<pre><code>protoparser macro_install init_pack "meeting-kit" "./my-registry"</code></pre>
<p>This gives you a pack folder with a <code>protoml-pack.json</code> manifest and a place for the pack's macro files.</p>
<h2>Add macros to the pack</h2>
<p>Place your custom macro files into the pack and describe the pack in its manifest. A simple macro could look like this:</p>
<pre><code>@new_macro
=name:decisionCard
=docs:
Renders a highlighted decision summary.
=template:
<div class="decision-card">
<strong>{{title}}</strong><br>
{{text}}
</div></code></pre>
<h2>Add the pack to the registry index</h2>
<pre><code>protoparser macro_install registry_add "./my-registry" "./my-registry/packs/meeting-kit"</code></pre>
<p>If the pack changes later, refresh the registry entry with:</p>
<pre><code>protoparser macro_install registry_update "./my-registry" "./my-registry/packs/meeting-kit"</code></pre>
<h2>Connect a project to your registry</h2>
<pre><code>protoparser macro_install init
protoparser macro_install add_registry "./my-registry"</code></pre>
<p>This prepares the project-local macro configuration and adds your registry as a source.</p>
<h2>Install or sync a pack from the registry</h2>
<pre><code>protoparser macro_install add_package "meeting-kit" 1.0.0
protoparser macro_install sync</code></pre>
<p>Or install in one step:</p>
<pre><code>protoparser macro_install install "meeting-kit" 1.0.0</code></pre>
<p>The project then receives the installed files in <code>.protoml/macro-packs/</code> and a generated <code>macros.index.pml</code>.</p>
<h2>Bind the registry macros into a meeting file</h2>
<pre><code>@macros_import ".protoml/macro-packs/macros.index.pml"
@protocol "Architecture Review - {{date}}"
@participants
=lead:Jane Doe,jdoe,jdoe@example.com
@meeting "Architecture Review"
# Decisions
@@macro=decisionCard:title=Storage;text=Use the replicated storage tier</code></pre>
<p>This is the meeting-side integration step: the generated macro index exposes the installed macros, and the meeting document uses them with <code>@@macro=...</code>.</p>
<h2>Manage installed packs in a project</h2>
<ul>
<li>List installed packs: <code>protoparser macro_install list</code></li>
<li>Inspect a pack: <code>protoparser macro_install info "meeting-kit"</code></li>
<li>Change the requested version: <code>protoparser macro_install update_package "meeting-kit" 1.1.0</code></li>
<li>Remove the package definition: <code>protoparser macro_install remove_package "meeting-kit"</code></li>
<li>Uninstall and remove a pack from the project: <code>protoparser macro_install remove "meeting-kit"</code></li>
</ul>
<h2>Manage the registry itself</h2>
<ul>
<li>Add a pack entry: <code>registry_add</code></li>
<li>Refresh a changed pack entry: <code>registry_update</code></li>
<li>Remove a pack entry: <code>protoparser macro_install registry_remove "./my-registry" "meeting-kit"</code></li>
</ul>
<p>Removing a pack from the registry index stops future resolution through that registry entry, but existing project installs may still keep local copies until updated or removed.</p>
<h2>Signing workflow for registry macros</h2>
<p>If you want a registry-delivered macro to become <code>trusted</code> in the ProtoML trust workflow, the macro file itself must be signed and the signing author must be listed in the registry <code>authors</code> section.</p>
<pre><code>protoparser sign macro "./my-registry/packs/meeting-kit/macros/meeting_kit_sample.pml" "./keys/alice-private.pem" "Alice" alice-main</code></pre>
<p>This creates a detached <code>*.sig.json</code> file next to the macro. The registry then needs a matching trusted author entry:</p>
<pre><code>{
"version": 1,
"name": "my-registry",
"authors": [
{
"name": "Alice",
"trust": "trusted",
"keys": [
{
"id": "alice-main",
"public_key": "-----BEGIN PUBLIC KEY----- ..."
}
]
}
],
"packages": []
}</code></pre>
<p>After that, consumers can run <code>verify</code> or <code>trust</code> with <code>-trustRegistry=...</code> and the macro can resolve to <code>trusted</code> if it has no hard risk flags such as JavaScript or external URLs.</p>
<h2>Registries can be split or combined</h2>
<p>A ProtoML registry does not have to do everything at once. A registry may publish:</p>
<ul>
<li>package entries in <code>packages</code> for install, sync, and search workflows</li>
<li>author trust entries in <code>authors</code> for trust, verify, and validate workflows</li>
<li>or both in one combined registry</li>
</ul>
<p>That means teams can keep macro delivery in one registry and trusted authors in another if that better matches their release and security process.</p>
<h2>Recommended trust-aware workflow</h2>
<ol>
<li>Start with bundled macros when possible</li>
<li>Create a custom pack only for the gaps</li>
<li>Sign the pack macros before treating them as production-ready</li>
<li>Publish the signing authors in the registry <code>authors</code> list</li>
<li>Install the pack into the project and check it with <code>trust</code>, <code>verify</code>, or <code>validate -trust=...</code></li>
<li>Review JavaScript and external URLs explicitly, even for signed registry macros</li>
</ol>
<h2>What about built-in macros?</h2>
<p>Bundled built-in macros can resolve to <code>trusted</code> without detached sidecars when they match the shipped built-in hash manifest and do not trigger hard risk flags.</p>
<p>If built-in macros should participate in the exact same author-signature workflow as external macros, they still need detached signatures and a matching trusted author entry in a documented trust registry. Extra or modified files in the built-in macro directory are not automatically trusted.</p>
<h2>Detached sidecar workflow without a registry</h2>
<p>Not every signed macro has to live inside a registry.</p>
<p>Author side:</p>
<ul>
<li>sign the standalone macro file with <code>protoparser sign macro ...</code></li>
<li>ship the macro together with its <code>*.sig.json</code> sidecar</li>
<li>ship the public key through a documented channel</li>
</ul>
<p>User side:</p>
<ul>
<li>keep the macro and sidecar together</li>
<li>run <code>protoparser verify macro ...</code></li>
<li>treat the result as cryptographic verification, not as automatic registry trust</li>
</ul>
<h2>Remote registries</h2>
<p>ProtoML can already use remote registry URLs for discovery, trust lookup, and explicit search. The remote workflow is intentionally simple: the registry is just a JSON document plus reachable pack files behind normal HTTP or HTTPS URLs.</p>
<h2>What the registry admin must do</h2>
<ol>
<li>Host a <code>protoml.registry.json</code> file on a stable HTTP or HTTPS URL</li>
<li>Publish each pack at a stable remote location and make sure the registry <code>source</code> and <code>manifest</code> paths point to reachable files</li>
<li>Keep package versions immutable once published whenever possible</li>
<li>Optionally publish an <code>authors</code> list with trusted or untrusted authors and public keys for trust verification</li>
<li>Document whether the registry is public, internal, reviewed, or experimental</li>
</ol>
<p>A minimal remote registry usually looks like a static website, GitHub Pages site, internal web server, or artifact host that serves JSON and pack files without any special server logic.</p>
<h2>What the user must do</h2>
<ol>
<li>Add the remote registry to the project with <code>protoparser macro_install add_registry "https://example.org/protoml.registry.json"</code> if it should be part of the project config</li>
<li>Search it explicitly with <code>protoparser macro_install search "meeting" "https://example.org/protoml.registry.json"</code> if it should only be queried ad hoc</li>
<li>Use repeatable <code>-trustRegistry=...</code> flags with <code>trust</code>, <code>verify</code>, or <code>validate -trust=...</code> when one or more registries should act as author trust sources</li>
<li>Review the registry owner and pack maintainers before treating the registry as trusted</li>
</ol>
<h2>Local company registry variant</h2>
<p>Some teams do not want HTTP hosting at all. In that case, the same registry can live in a shared directory or mounted network path.</p>
<pre><code>protoparser macro_install add_registry "Z:\\protoml-registry"
protoparser macro_install add_registry "/mnt/protoml-registry"
protoparser validate "./governance/release-checklist.pml" -trust=strict -trustRegistry="Z:\\protoml-registry"</code></pre>
<p>This works well for internal-only environments where a reviewed file share or NFS path is easier to operate than a hosted web endpoint.</p>
<h2>Operational notes</h2>