@bendyline/docblocks-cli 2.2.0 → 2.2.1

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/bin.js CHANGED
@@ -1872,6 +1872,66 @@ async function validatePreparedDocument(_documents, prepared, targetFormat, sign
1872
1872
  diagnostics: unique
1873
1873
  };
1874
1874
  }
1875
+ function annotationSyntaxDiagnostics(markdown, stage, format) {
1876
+ const diagnostics = [];
1877
+ const lines = markdown.split(/\r?\n/);
1878
+ let inFence = false;
1879
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
1880
+ if (diagnostics.length >= MCP_WIRE_LIMITS5.arrayEntries) break;
1881
+ const rawLine = lines[lineIndex];
1882
+ if (/^\s*(?:```|~~~)/u.test(rawLine)) {
1883
+ inFence = !inFence;
1884
+ continue;
1885
+ }
1886
+ if (inFence) continue;
1887
+ const line = rawLine.replace(/`[^`]*`/gu, "``");
1888
+ let search = 0;
1889
+ while (search < line.length) {
1890
+ const open6 = line.indexOf("{[", search);
1891
+ if (open6 === -1) break;
1892
+ const close = line.indexOf("]}", open6 + 2);
1893
+ if (close === -1) {
1894
+ diagnostics.push(
1895
+ malformedAnnotationDiagnostic(
1896
+ stage,
1897
+ format,
1898
+ lineIndex + 1,
1899
+ open6 + 1,
1900
+ 'An annotation opened with "{[" has no matching "]}" on the same line.'
1901
+ )
1902
+ );
1903
+ break;
1904
+ }
1905
+ const inner = line.slice(open6 + 2, close).replace(/"[^"]*"/gu, '""');
1906
+ if (/[{}[\]]/u.test(inner)) {
1907
+ diagnostics.push(
1908
+ malformedAnnotationDiagnostic(
1909
+ stage,
1910
+ format,
1911
+ lineIndex + 1,
1912
+ open6 + 1,
1913
+ "An annotation contains a stray brace or bracket outside quoted values."
1914
+ )
1915
+ );
1916
+ }
1917
+ search = close + 2;
1918
+ }
1919
+ }
1920
+ return diagnostics;
1921
+ }
1922
+ function malformedAnnotationDiagnostic(stage, format, line, column, message) {
1923
+ return {
1924
+ code: "malformed-template-annotation",
1925
+ severity: "warning",
1926
+ stage,
1927
+ format,
1928
+ count: 1,
1929
+ message,
1930
+ remediation: 'Repair the annotation so it reads exactly `{[templateId key="value"]}` with no extra braces or brackets, then validate again.',
1931
+ retryable: false,
1932
+ location: { kind: "source", line, column }
1933
+ };
1934
+ }
1875
1935
  async function authoringDiagnostics(prepared, stage, format, signal) {
1876
1936
  throwIfAborted5(signal);
1877
1937
  const {
@@ -1885,7 +1945,11 @@ async function authoringDiagnostics(prepared, stage, format, signal) {
1885
1945
  const blocks = flattenBlocks(prepared.doc.blocks);
1886
1946
  const analyzedBlocks = blocks.slice(0, MCP_WIRE_LIMITS5.arrayEntries);
1887
1947
  const theme = resolveThemeForDoc(prepared.doc);
1888
- const diagnostics = [];
1948
+ const diagnostics = annotationSyntaxDiagnostics(
1949
+ prepared.markdown,
1950
+ stage,
1951
+ format
1952
+ );
1889
1953
  for (let index = 0; index < analyzedBlocks.length; index += 1) {
1890
1954
  const checkpoint = cancellationCheckpoint(signal, index);
1891
1955
  if (checkpoint) await checkpoint;
@@ -4639,11 +4703,13 @@ function registerTemplateTools(server, context) {
4639
4703
  throw new Error(`Format "${normalizedTarget}" is not export-capable`);
4640
4704
  }
4641
4705
  }
4642
- const [templates, schemaModule, transformModule] = await Promise.all([
4706
+ const [fullTemplates, schemaModule, transformModule] = await Promise.all([
4643
4707
  authoringTemplateCatalog(),
4644
4708
  import("@bendyline/squisq/schemas"),
4645
4709
  import("@bendyline/squisq/transform")
4646
4710
  ]);
4711
+ const annotationHandling = normalizedTarget ? await targetTemplateAnnotationHandling(normalizedTarget) : null;
4712
+ const templates = annotationHandling === "ignored" ? fullTemplates.filter((template) => template.safeForContentFirst) : fullTemplates;
4647
4713
  let recommendations = [];
4648
4714
  let totalBlocks = null;
4649
4715
  let truncated = false;
@@ -4688,19 +4754,19 @@ function registerTemplateTools(server, context) {
4688
4754
  defaultFidelity: authoringDefaultFidelity(normalizedTarget, goal),
4689
4755
  workflow: [
4690
4756
  "Treat supplied facts as a closed evidence set. Use temporal or correlational wording unless causality is supplied. Label calculations, assumptions, hypotheses, recommendations, and illustrative examples. Do not infer end-to-end feasibility from one supplied phase duration.",
4691
- "Audit claims before conversion: causal links, rhetorical labels such as compounding, engine, healthy, strong, or constraint, superlatives, sole-cause claims, targets, capabilities, owners, dates, channels, and operational details must be supplied or explicitly framed as a hypothesis, recommendation, example, or proposal. Do not connect separate metrics, audiences, segments, or workflows unless the relationship is supplied. Prefer observed, coincided with, intended to, or proposed over drove, addresses, protects, or proves. Do not call choices a sequence or capacity allocation unless order, timing, or resource constraints were supplied.",
4692
- "Complete every requested element without presenting invented policy as established fact. When a requested policy, playbook, or training guide needs unsupplied roles, gates, timelines, channels, or procedures, add one explicit proposed operating model scope note placed before the first such detail so it governs all of them.",
4757
+ "Audit claims before conversion: causal links, rhetorical labels such as compounding, engine, healthy, strong, or constraint, superlatives, sole-cause claims, targets, capabilities, owners, dates, channels, and operational details must be supplied or explicitly framed. Do not connect separate metrics, audiences, segments, or workflows unless the relationship is supplied. Prefer observed, coincided with, intended to, or proposed over drove, addresses, protects, or proves. Do not call choices a sequence or capacity allocation unless order, timing, or resources were supplied.",
4758
+ "Complete every requested element without presenting invented policy as established fact. When a requested policy, playbook, or training guide needs unsupplied roles, gates, timelines, channels, or procedures, add one explicit proposed operating model scope note placed before the first such detail so it governs all of them. Prefix each unsupplied operational rule with Proposed: on its slide or section.",
4693
4759
  "Generic option traits\u2014control, flexibility, vendor support, freed capacity, switching costs\u2014are capability claims: state one only when supplied or labeled Assumption or Judgment, even in table cells and tradeoffs.",
4694
4760
  "Author complete Squisq-compatible content with heading-bound annotations; ordinary headings default to the loss-averse content template.",
4695
4761
  normalizedTarget === "pptx" ? "Match every explicitly requested slide count exactly. Use exactly one level-one Markdown heading (#) per slide and no level-two through level-six headings because every heading becomes a slide by default. Target at most 80 words per slide, and use lists, tables, or bold labels for within-slide structure." : "Honor the requested word range and document genre. Prefer connected memo prose for executive decisions and native headings, tables, and checklists for operational documents.",
4696
- normalizedTarget === "pptx" ? "Make slide one a supported point-of-view thesis, not a generic title or unsupported flourish. For requested choices, state a concrete opportunity cost grounded in the supplied alternatives and name one clearly labeled proposed accountable role per choice. If a tradeoff requires an unsupplied capacity or outcome assumption, label it Assumption or Potential tradeoff rather than stating it as fact." : "Tie each decision implication or comparative judgment to a supplied fact or calculation, or label it Judgment. When accountability is requested but not supplied, name one clearly labeled proposed accountable role per decision or workstream.",
4697
- "Add decision value without inventing causes: interpret supplied comparisons to baselines, goals, targets, and rankings; use transparent calculations; label any new review cadence or measurement procedure as proposed.",
4762
+ normalizedTarget === "pptx" ? "Make slide one a supported point-of-view thesis, not a generic title. For requested choices, state a concrete opportunity cost from supplied alternatives and one proposed accountable role per choice. Label any unsupplied capacity or outcome assumption Assumption or Potential tradeoff." : "Tie each decision implication or comparative judgment to a supplied fact or calculation, or label it Judgment. When accountability is requested but not supplied, name one clearly labeled proposed accountable role per decision or workstream.",
4763
+ "Add decision value without inventing causes: interpret supplied comparisons to baselines, goals, and targets; use transparent calculations; label new review cadences or measurement procedures as proposed.",
4698
4764
  "Before validation and conversion, run a content preflight: count slide sections or document words, verify every requested element, and rewrite or label every unsupported claim.",
4699
- "Use the complete Markdown as the authoritative source. Pass it\u2014or a bundle source when assets are needed\u2014directly to convert_document, and revise by editing the complete Markdown and converting again.",
4700
- "Use create_document_bundle only when reusing a large Markdown-and-asset bundle across several review or conversion operations would materially reduce retransmission.",
4765
+ "Use the complete Markdown as the authoritative source. Pass it\u2014or a bundle source when assets are needed\u2014directly to convert_document; revise by editing it and converting again.",
4766
+ "When a draft will feed two or more validate, preview, or convert calls, stage it once with create_document_bundle and pass the returned artifact URI as each source; after edits, stage the revised draft again.",
4701
4767
  "Use inspect_document or validate_document when semantic structure, content retention, accessibility, or target diagnostics need review; repair actionable findings in the authoritative Markdown.",
4702
4768
  "Use preview_document when visual evidence is needed, inspect previewBasis, and repair overflow or layout problems in the authoritative Markdown.",
4703
- normalizedTarget === "pptx" ? "For visual polish, replace selected content blocks with compatible recommended templates; use patterns such as definitionCard for a definition, dataTable for a true table, comparisonBar for supported numeric comparisons, and list for concise steps when the returned catalog supports them. Preserve all required content and preview again." : "For visual polish, use native headings, tables, and checklists where they improve scanning; keep complete-body content when a visual template would discard detail, then preview again.",
4769
+ normalizedTarget === "pptx" ? "For visual polish, replace selected content blocks with compatible recommended templates; use patterns such as definitionCard for a definition, dataTable for a true table, comparisonBar for supported numeric comparisons, and list for concise steps when the returned catalog supports them. Preserve all required content and preview again." : annotationHandling === "ignored" ? "This target flattens template annotations to semantic content, so visual templates have no effect on the exported file: rely on native headings, tables, and checklists for structure and scanning." : "For visual polish, use native headings, tables, and checklists where they improve scanning; keep complete-body content when a visual template would discard detail, then preview again.",
4704
4770
  "Convert to immutable artifacts, inspect conversion reports, and save only final durable outputs."
4705
4771
  ],
4706
4772
  syntax: {
@@ -5153,7 +5219,7 @@ function compactAuthoringContextText(context) {
5153
5219
  `Warning: ${context.syntax.standaloneWarning}`,
5154
5220
  `Templates (${context.templates.length}): ${context.templates.map(({ id }) => id).join(", ")}`,
5155
5221
  `Themes (${context.themes.length}): ${context.themes.map(({ id }) => id).join(", ")}`,
5156
- `Transform styles (${context.transformStyles.length}): ${context.transformStyles.map(({ id }) => id).join(", ")}`
5222
+ `Transform styles (${context.transformStyles.length}): ${context.transformStyles.map(({ id }) => id).join(", ")} \u2014 transforms re-compose blocks and can change section count; avoid on exact-count briefs.`
5157
5223
  ];
5158
5224
  if (context.recommendations.length > 0) {
5159
5225
  lines.push(
@@ -5232,6 +5298,15 @@ async function authoringTemplateCatalog() {
5232
5298
  };
5233
5299
  });
5234
5300
  }
5301
+ async function targetTemplateAnnotationHandling(targetFormat) {
5302
+ const { createCliRegistry: createCliRegistry2 } = await import("@bendyline/squisq-cli/api");
5303
+ const definition = createCliRegistry2().get(targetFormat);
5304
+ if (definition && typeof definition === "object" && "templateAnnotationHandling" in definition) {
5305
+ const value = definition.templateAnnotationHandling;
5306
+ if (value === "rendered" || value === "preserved" || value === "ignored") return value;
5307
+ }
5308
+ return null;
5309
+ }
5235
5310
  function authoringDefaultFidelity(targetFormat, goal) {
5236
5311
  if (!targetFormat) return null;
5237
5312
  if (targetFormat === "mp4" || targetFormat === "gif") return "rendered-fidelity";
@@ -5702,7 +5777,7 @@ function createMcpServer(options = {}) {
5702
5777
  const server = new McpServer(
5703
5778
  { name: "docblocks", version: getPackageVersion() },
5704
5779
  {
5705
- instructions: "Treat provided facts as a closed evidence set: preserve them exactly; use temporal or correlational wording unless causality is supplied; label calculations, assumptions, hypotheses, recommendations, and examples. Causal links, rhetorical performance labels, superlatives, sole causes, targets, capabilities, owners, dates, channels, and operational details must be supplied or explicitly framed. Do not connect separate metrics, audiences, segments, or workflows unless the relationship is supplied. Prefer observed, coincided with, intended to, or proposed over drove, addresses, protects, or proves. Add decision value through supplied baselines, goals, targets, rankings, and transparent calculations. Do not call choices a sequence or capacity allocation unless order, timing, or resources were supplied. When a requested policy, playbook, or training guide needs unsupplied operating details, add one proposed operating model scope note, then complete every requested element. Match explicit slide/page counts and word ranges exactly; target at most 80 words per presentation section. Make slide one a supported point-of-view thesis. For requested choices, state concrete opportunity costs grounded in supplied alternatives and one proposed accountable role per choice; label unsupplied capacity, outcome, or review-cadence assumptions. Before conversion, count sections or words, verify required elements, and audit unsupported claims. Start with get_authoring_context for the compact linked-Squisq contract and structured catalog. Author complete Squisq-compatible Markdown with template annotations on headings, for example `# Heading {[content]}`; standalone `{[template]}` creates an extra heading-less block. Ordinary headings default to the loss-averse `content` template. The canonical path is complete Markdown\u2014or a bundle source when assets are needed\u2014directly into convert_document; revise by editing the complete Markdown and converting again. create_document_bundle is optional for materially large, reusable Markdown-and-asset bundles. Use inspect_document, validate_document, and preview_document when their evidence is useful rather than as required phases. Save only the final durable artifact. Never invent root ids: call list_roots and use an id exactly as returned."
5780
+ instructions: "Treat provided facts as a closed evidence set: preserve them exactly; use temporal or correlational wording unless causality is supplied; label calculations, assumptions, hypotheses, recommendations, and examples. Causal links, rhetorical performance labels, superlatives, sole causes, targets, capabilities, owners, dates, channels, and operational details must be supplied or explicitly framed. Do not connect separate metrics, audiences, segments, or workflows unless the relationship is supplied. Prefer observed, coincided with, intended to, or proposed over drove, addresses, protects, or proves. Add decision value through supplied baselines, goals, targets, rankings, and transparent calculations. Do not call choices a sequence or capacity allocation unless order, timing, or resources were supplied. When a requested policy, playbook, or training guide needs unsupplied operating details, add one proposed operating model scope note, then complete every requested element. Match explicit slide/page counts and word ranges exactly; target at most 80 words per presentation section. Make slide one a supported point-of-view thesis. For requested choices, state concrete opportunity costs grounded in supplied alternatives and one proposed accountable role per choice; label unsupplied capacity, outcome, or review-cadence assumptions. Before conversion, count sections or words, verify required elements, and audit unsupported claims. Start with get_authoring_context for the compact linked-Squisq contract and structured catalog. Author complete Squisq-compatible Markdown with template annotations on headings, for example `# Heading {[content]}`; standalone `{[template]}` creates an extra heading-less block. Ordinary headings default to the loss-averse `content` template. The canonical path is complete Markdown\u2014or a bundle source when assets are needed\u2014directly into convert_document; revise by editing the complete Markdown and converting again. When one draft will feed two or more validate, preview, or convert calls, stage it once with create_document_bundle and pass the returned artifact URI as each source instead of re-sending the Markdown. Use inspect_document, validate_document, and preview_document when their evidence is useful rather than as required phases. Save only the final durable artifact. Never invent root ids: call list_roots and use an id exactly as returned."
5706
5781
  }
5707
5782
  );
5708
5783
  registerAgenticTools(server, {
package/dist/eval/cli.js CHANGED
@@ -704,7 +704,8 @@ function missingArtifact(caught) {
704
704
  }
705
705
 
706
706
  // src/eval/codex-host.ts
707
- import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
707
+ import { mkdtemp, readFile as readFile3, rm, writeFile as writeFile2 } from "fs/promises";
708
+ import { tmpdir } from "os";
708
709
  import path3 from "path";
709
710
  import { z } from "zod";
710
711
 
@@ -908,17 +909,30 @@ async function runGeneration(options, testCase, caseDirectory) {
908
909
  const prompt = generationPrompt(testCase, options.profile);
909
910
  await writeFile2(path3.join(caseDirectory, "generation-prompt.txt"), prompt);
910
911
  const resolved = await resolveCodexCommand(options.codexCommand);
912
+ const agentCwd = await mkdtemp(path3.join(tmpdir(), "docblocks-eval-agent-"));
911
913
  const args = [
912
914
  ...resolved.prefixArgs,
913
- ...buildCodexGenerationArgs(options, testCase, caseDirectory, outputSchemaPath, finalPath)
915
+ ...buildCodexGenerationArgs(
916
+ options,
917
+ testCase,
918
+ caseDirectory,
919
+ agentCwd,
920
+ outputSchemaPath,
921
+ finalPath
922
+ )
914
923
  ];
915
- const capture = await runCapturedProcess({
916
- command: resolved.command,
917
- args,
918
- cwd: options.repoRoot,
919
- stdin: prompt,
920
- timeoutMs: 15 * 60 * 1e3
921
- });
924
+ let capture;
925
+ try {
926
+ capture = await runCapturedProcess({
927
+ command: resolved.command,
928
+ args,
929
+ cwd: agentCwd,
930
+ stdin: prompt,
931
+ timeoutMs: 15 * 60 * 1e3
932
+ });
933
+ } finally {
934
+ await rm(agentCwd, { recursive: true, force: true }).catch(() => void 0);
935
+ }
922
936
  await Promise.all([
923
937
  writeFile2(path3.join(caseDirectory, "generation-trace.jsonl"), capture.stdout),
924
938
  writeFile2(path3.join(caseDirectory, "generation-stderr.log"), capture.stderr)
@@ -951,6 +965,7 @@ async function runLlmJudge(options, testCase, markdown, caseDirectory) {
951
965
  const prompt = judgePrompt(testCase, markdown);
952
966
  await writeFile2(path3.join(caseDirectory, "judge-prompt.txt"), prompt);
953
967
  const resolved = await resolveCodexCommand(options.codexCommand);
968
+ const judgeCwd = await mkdtemp(path3.join(tmpdir(), "docblocks-eval-judge-"));
954
969
  const args = [
955
970
  ...resolved.prefixArgs,
956
971
  "exec",
@@ -964,7 +979,7 @@ async function runLlmJudge(options, testCase, markdown, caseDirectory) {
964
979
  "--color",
965
980
  "never",
966
981
  "--cd",
967
- caseDirectory,
982
+ judgeCwd,
968
983
  "--output-schema",
969
984
  outputSchemaPath,
970
985
  "--output-last-message",
@@ -972,13 +987,18 @@ async function runLlmJudge(options, testCase, markdown, caseDirectory) {
972
987
  ...options.model ? ["--model", options.model] : [],
973
988
  "-"
974
989
  ];
975
- const capture = await runCapturedProcess({
976
- command: resolved.command,
977
- args,
978
- cwd: options.repoRoot,
979
- stdin: prompt,
980
- timeoutMs: 10 * 60 * 1e3
981
- });
990
+ let capture;
991
+ try {
992
+ capture = await runCapturedProcess({
993
+ command: resolved.command,
994
+ args,
995
+ cwd: judgeCwd,
996
+ stdin: prompt,
997
+ timeoutMs: 10 * 60 * 1e3
998
+ });
999
+ } finally {
1000
+ await rm(judgeCwd, { recursive: true, force: true }).catch(() => void 0);
1001
+ }
982
1002
  await Promise.all([
983
1003
  writeFile2(path3.join(caseDirectory, "judge-trace.jsonl"), capture.stdout),
984
1004
  writeFile2(path3.join(caseDirectory, "judge-stderr.log"), capture.stderr)
@@ -990,7 +1010,7 @@ async function runLlmJudge(options, testCase, markdown, caseDirectory) {
990
1010
  usage: extractTokenUsage(capture.stdout)
991
1011
  };
992
1012
  }
993
- function buildCodexGenerationArgs(options, testCase, caseDirectory, outputSchemaPath, finalPath) {
1013
+ function buildCodexGenerationArgs(options, testCase, caseDirectory, agentCwd, outputSchemaPath, finalPath) {
994
1014
  const mcpArgs = [
995
1015
  options.cliEntry,
996
1016
  "mcp",
@@ -1013,7 +1033,7 @@ function buildCodexGenerationArgs(options, testCase, caseDirectory, outputSchema
1013
1033
  "--color",
1014
1034
  "never",
1015
1035
  "--cd",
1016
- caseDirectory,
1036
+ agentCwd,
1017
1037
  "-c",
1018
1038
  `mcp_servers.docblocks.command=${tomlString(process.execPath)}`,
1019
1039
  "-c",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/docblocks-cli",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Build, preview, convert, render, inspect, and automate documents",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -46,17 +46,17 @@
46
46
  "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "dependencies": {
49
- "@bendyline/docblocks": "2.2.0",
50
- "@bendyline/squisq": "2.3.2",
51
- "@bendyline/squisq-cli": "2.3.2",
52
- "@bendyline/squisq-formats": "2.3.2",
53
- "@bendyline/squisq-react": "2.3.2",
54
- "@bendyline/squisq-video": "2.2.2",
49
+ "@bendyline/docblocks": "2.2.1",
50
+ "@bendyline/squisq": "2.3.3",
51
+ "@bendyline/squisq-cli": "2.3.3",
52
+ "@bendyline/squisq-formats": "2.3.3",
53
+ "@bendyline/squisq-react": "2.3.3",
54
+ "@bendyline/squisq-video": "2.2.3",
55
55
  "@modelcontextprotocol/sdk": "1.29.0",
56
56
  "commander": "13.1.0",
57
57
  "jszip": "3.10.1",
58
- "playwright-core": "1.58.2",
59
58
  "pdf-lib": "1.17.1",
59
+ "playwright-core": "1.58.2",
60
60
  "zod": "3.25.76"
61
61
  }
62
62
  }