UNPKG

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
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, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;"); } function escapeAttribute(value) { return escapeHtml(value).replace(/'/g, "&#39;"); } 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 &lt;topic&gt;</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] &lt;filename&gt; &lt;format&gt; protoparser [options] &lt;filename&gt; &lt;format&gt; &lt;output_dir&gt;</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=&lt;name&gt;</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=&lt;mode&gt;</code> and <code>-graphDirection=&lt;dir&gt;</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 "&lt;dir&gt;" 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: &lt;span class="status-pill status-{{state}}"&gt;{{label}}&lt;/span&gt;</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 "&lt;dir&gt;" 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 "&lt;dir&gt;"</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: &lt;div class="decision-card"&gt; &lt;strong&gt;{{title}}&lt;/strong&gt;&lt;br&gt; {{text}} &lt;/div&gt;</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>