@bendyline/docblocks-cli 2.1.1 → 2.2.0

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
@@ -869,6 +869,9 @@ async function withMaterializationLock(target, operation) {
869
869
  if (materializationTails.get(key) === tail) materializationTails.delete(key);
870
870
  }
871
871
  }
872
+ function authorityError(message, hint) {
873
+ return Object.assign(new Error(message), { code: "unauthorized-path", hint });
874
+ }
872
875
  var DEFAULT_MAX_INPUT_FILE_BYTES, MCP_FILE_AUTHORITY_LIMITS, MAX_PATH_CHARACTERS, MATERIALIZATION_WRITE_CHUNK_BYTES, HASH_READ_CHUNK_BYTES, materializationTails, McpFileAuthority;
873
876
  var init_authority = __esm({
874
877
  "src/mcp/authority.ts"() {
@@ -947,7 +950,12 @@ var init_authority = __esm({
947
950
  /** Resolve a root-relative read without ever treating the alias as new authority. */
948
951
  async authorizeRootRead(rootId, workspacePath) {
949
952
  const root = this.readRoots.find((candidate2) => candidate2.id === rootId);
950
- if (!root) throw new Error("Unknown or unreadable MCP root");
953
+ if (!root) {
954
+ throw authorityError(
955
+ "Unknown or unreadable MCP root",
956
+ "Call list_roots and copy a returned read-enabled root id. If no roots are listed, use an inline markdown or bundle source, or restart the server with --allow-read."
957
+ );
958
+ }
951
959
  const candidate = path7.join(root.physical, ...parseRootRelativePath(workspacePath));
952
960
  const physical = await realpath4(candidate).catch(() => null);
953
961
  if (!physical || !isPathInside(root.physical, physical)) {
@@ -958,7 +966,12 @@ var init_authority = __esm({
958
966
  /** Resolve a root-relative write without ever treating the alias as new authority. */
959
967
  async authorizeRootWrite(rootId, workspacePath) {
960
968
  const root = this.writeRoots.find((candidate2) => candidate2.id === rootId);
961
- if (!root) throw new Error("Unknown or unwritable MCP root");
969
+ if (!root) {
970
+ throw authorityError(
971
+ "Unknown or unwritable MCP root",
972
+ "Call list_roots and copy a returned write-enabled root id. If none is writable, keep the result as a session artifact or restart the server with --allow-write."
973
+ );
974
+ }
962
975
  const candidate = path7.join(root.physical, ...parseRootRelativePath(workspacePath));
963
976
  const authorized = await this.authorizeWrite(candidate);
964
977
  const physicalParent = await realpath4(path7.dirname(authorized));
@@ -1537,7 +1550,10 @@ var init_document_service = __esm({
1537
1550
  const { parseMarkdown: parseMarkdown2 } = await import("@bendyline/squisq/markdown");
1538
1551
  const { markdownToDoc: markdownToDoc2 } = await import("@bendyline/squisq/doc");
1539
1552
  const markdownDoc = parseMarkdown2(markdown);
1540
- const doc = markdownToDoc2(markdownDoc);
1553
+ const doc = markdownToDoc2(markdownDoc, {
1554
+ defaultTemplate: "content",
1555
+ autoTemplates: false
1556
+ });
1541
1557
  const assets = await this.assetSummaries(container, signal);
1542
1558
  throwIfAborted5(signal);
1543
1559
  return {
@@ -1776,7 +1792,8 @@ async function inspectPreparedDocument(_documents, prepared, maximumBlocks = DEF
1776
1792
  ...validation.diagnostics.map(
1777
1793
  (diagnostic) => mapDocDiagnostic(diagnostic, prepared.sourceFormat)
1778
1794
  ),
1779
- ...missingAltTextDiagnostics(prepared.markdownDoc, prepared.sourceFormat)
1795
+ ...missingAltTextDiagnostics(prepared.markdownDoc, prepared.sourceFormat),
1796
+ ...await authoringDiagnostics(prepared, "inspect", prepared.sourceFormat, signal)
1780
1797
  ];
1781
1798
  let wordCount = 0;
1782
1799
  for (let index = 0; index < analyzed.length; index += 1) {
@@ -1833,6 +1850,12 @@ async function validatePreparedDocument(_documents, prepared, targetFormat, sign
1833
1850
  prepared.markdownDoc,
1834
1851
  normalizedTargetFormat ?? prepared.sourceFormat
1835
1852
  ),
1853
+ ...await authoringDiagnostics(
1854
+ prepared,
1855
+ "validate",
1856
+ normalizedTargetFormat ?? prepared.sourceFormat,
1857
+ signal
1858
+ ),
1836
1859
  ...normalizedTargetFormat ? await targetPreflight(prepared, normalizedTargetFormat, signal) : []
1837
1860
  ];
1838
1861
  await yieldForCancellation(signal);
@@ -1849,6 +1872,111 @@ async function validatePreparedDocument(_documents, prepared, targetFormat, sign
1849
1872
  diagnostics: unique
1850
1873
  };
1851
1874
  }
1875
+ async function authoringDiagnostics(prepared, stage, format, signal) {
1876
+ throwIfAborted5(signal);
1877
+ const {
1878
+ TEMPLATE_AUTHORING_METADATA,
1879
+ flattenBlocks,
1880
+ getBlockBodyText,
1881
+ materializeBlockLayers,
1882
+ resolveTemplateName,
1883
+ resolveThemeForDoc
1884
+ } = await import("@bendyline/squisq/doc");
1885
+ const blocks = flattenBlocks(prepared.doc.blocks);
1886
+ const analyzedBlocks = blocks.slice(0, MCP_WIRE_LIMITS5.arrayEntries);
1887
+ const theme = resolveThemeForDoc(prepared.doc);
1888
+ const diagnostics = [];
1889
+ for (let index = 0; index < analyzedBlocks.length; index += 1) {
1890
+ const checkpoint = cancellationCheckpoint(signal, index);
1891
+ if (checkpoint) await checkpoint;
1892
+ const block = analyzedBlocks[index];
1893
+ throwIfAborted5(signal);
1894
+ if (block.standaloneAnnotation) {
1895
+ diagnostics.push({
1896
+ code: "standalone-template-block",
1897
+ severity: "warning",
1898
+ stage,
1899
+ format,
1900
+ count: 1,
1901
+ message: `Block "${block.id}" was created by a standalone template annotation and has no heading.`,
1902
+ remediation: "Move the annotation onto the intended heading, for example `# Heading {[content]}`, unless the additional heading-less block is deliberate.",
1903
+ retryable: false,
1904
+ location: {
1905
+ kind: "block",
1906
+ blockId: toWireIdentifier(block.id, "block"),
1907
+ nodeType: "block"
1908
+ }
1909
+ });
1910
+ }
1911
+ if (!block.template) continue;
1912
+ const templateId = resolveTemplateName(block.template);
1913
+ const behavior = TEMPLATE_AUTHORING_METADATA[templateId];
1914
+ if (!behavior) continue;
1915
+ const body = getBlockBodyText(block).trim();
1916
+ if (!body) continue;
1917
+ if (behavior.bodyPolicy === "ignored") {
1918
+ diagnostics.push({
1919
+ code: "template-body-not-rendered",
1920
+ severity: "warning",
1921
+ stage,
1922
+ format,
1923
+ count: 1,
1924
+ message: `Template "${templateId}" does not render the body of block "${block.title ?? block.id}".`,
1925
+ remediation: "Use the content template to preserve the complete body, or move this prose into a separate content block before visual optimization.",
1926
+ retryable: false,
1927
+ location: {
1928
+ kind: "block",
1929
+ blockId: toWireIdentifier(block.id, "block"),
1930
+ nodeType: "block"
1931
+ }
1932
+ });
1933
+ continue;
1934
+ }
1935
+ if (behavior.bodyPolicy !== "complete") continue;
1936
+ const materialized = materializeBlockLayers(block, {
1937
+ theme,
1938
+ customTemplates: prepared.doc.customTemplates,
1939
+ persistentLayers: false,
1940
+ blockIndex: index,
1941
+ totalBlocks: analyzedBlocks.length
1942
+ });
1943
+ const renderedText = materialized.layers.filter((layer) => layer.type === "text").map((layer) => layer.content.text).join("\n");
1944
+ if (!normalizedText(renderedText).includes(normalizedText(body))) {
1945
+ diagnostics.push({
1946
+ code: "rendered-content-omitted",
1947
+ severity: "error",
1948
+ stage,
1949
+ format,
1950
+ count: 1,
1951
+ message: `The complete body of block "${block.title ?? block.id}" is not present in its rendered text layers.`,
1952
+ remediation: "Keep the content template and repair its inputs, then validate and preview again before conversion.",
1953
+ retryable: false,
1954
+ location: {
1955
+ kind: "block",
1956
+ blockId: toWireIdentifier(block.id, "block"),
1957
+ nodeType: "block"
1958
+ }
1959
+ });
1960
+ }
1961
+ }
1962
+ if (analyzedBlocks.length < blocks.length) {
1963
+ diagnostics.push({
1964
+ code: "authoring-analysis-truncated",
1965
+ severity: "info",
1966
+ stage,
1967
+ format,
1968
+ count: blocks.length - analyzedBlocks.length,
1969
+ message: `Authoring diagnostics analyzed the first ${analyzedBlocks.length} blocks.`,
1970
+ remediation: "Split the document before requesting exhaustive per-block authoring diagnostics.",
1971
+ retryable: false,
1972
+ location: null
1973
+ });
1974
+ }
1975
+ return diagnostics;
1976
+ }
1977
+ function normalizedText(value) {
1978
+ return value.normalize("NFKC").replace(/\s+/gu, " ").trim().toLocaleLowerCase("en-US");
1979
+ }
1852
1980
  async function comparePreparedDocuments(documents, left, right, signal) {
1853
1981
  throwIfAborted5(signal);
1854
1982
  await yieldForCancellation(signal);
@@ -2728,6 +2856,7 @@ async function previewPreparedDocument(artifacts, prepared, request, signal, pro
2728
2856
  diagnostics: boundDiagnostics(
2729
2857
  [
2730
2858
  ...prepared.diagnostics,
2859
+ ...await authoringDiagnostics(prepared, "render", prepared.sourceFormat, signal),
2731
2860
  ...rendered.diagnostics ?? [],
2732
2861
  ...previewBasis === "reconstructed-import" ? [reconstructedPreviewDiagnostic(prepared.sourceFormat)] : []
2733
2862
  ],
@@ -3149,10 +3278,13 @@ async function prepareRenderedDocument(prepared, options, signal) {
3149
3278
  }
3150
3279
  };
3151
3280
  registry.register(captureDefinition);
3281
+ const canonicalMarkdown = options.autoTemplates === true ? prepared.markdownDoc : (await import("@bendyline/squisq/doc")).docToMarkdown(prepared.doc, {
3282
+ defaultTemplate: "sectionHeader"
3283
+ });
3152
3284
  const result = await convert(
3153
3285
  {
3154
3286
  kind: "markdown",
3155
- markdown: prepared.markdownDoc,
3287
+ markdown: canonicalMarkdown,
3156
3288
  container: prepared.container,
3157
3289
  baseName: prepared.baseName
3158
3290
  },
@@ -4142,7 +4274,7 @@ function registerAgenticTools(server, context) {
4142
4274
  server.registerTool(
4143
4275
  "create_document_bundle",
4144
4276
  {
4145
- description: "Package Markdown and authority-scoped assets into an immutable DBK artifact with asset metadata.",
4277
+ description: "Optionally stage Markdown plus authority-scoped assets as a reusable immutable DBK artifact when the same bundle will feed several operations. For normal authoring, pass complete Markdown or a bundle source directly to convert_document.",
4146
4278
  inputSchema: z.object({ source: bundleDocumentSourceSchema }).strict(),
4147
4279
  outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.create_document_bundle,
4148
4280
  annotations: ARTIFACT_CREATING
@@ -4422,43 +4554,32 @@ function registerAuthoringGuideResource(server) {
4422
4554
  "docblocks://authoring-guide",
4423
4555
  {
4424
4556
  description: "Compact linked-Squisq authoring vocabulary, artifact workflow, fidelity modes, templates, themes, and transform styles.",
4425
- mimeType: "application/json"
4557
+ mimeType: "application/json",
4558
+ annotations: { audience: ["assistant"], priority: 1 }
4426
4559
  },
4427
4560
  async () => {
4428
- const [docModule, schemaModule, transformModule] = await Promise.all([
4429
- import("@bendyline/squisq/doc"),
4561
+ const [templates, schemaModule, transformModule] = await Promise.all([
4562
+ authoringTemplateCatalog(),
4430
4563
  import("@bendyline/squisq/schemas"),
4431
4564
  import("@bendyline/squisq/transform")
4432
4565
  ]);
4433
- const templates = docModule.getAvailableTemplates().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((id) => ({
4434
- id: requireWireIdentifier(id, "template id"),
4435
- label: boundWireText(
4436
- docModule.TEMPLATE_METADATA[id]?.label ?? id,
4437
- MCP_WIRE_LIMITS7.labelCharacters,
4438
- id
4439
- ),
4440
- description: boundWireText(docModule.TEMPLATE_METADATA[id]?.description ?? ""),
4441
- inputs: (docModule.TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
4442
- key: requireWireIdentifier(input.key, "template input key"),
4443
- required: input.required ?? false,
4444
- values: boundWireTextArray(
4445
- input.values ?? [],
4446
- MCP_WIRE_LIMITS7.arrayEntries,
4447
- MCP_WIRE_LIMITS7.labelCharacters
4448
- ),
4449
- hint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
4450
- }))
4451
- }));
4452
4566
  const guide = {
4453
- version: 1,
4567
+ version: 6,
4454
4568
  workflow: [
4455
- "list_roots and select an authority-scoped source",
4456
- "inspect_document and validate_document",
4457
- "convert_document to one or more immutable artifacts",
4458
- "preview_document and inspect previewBasis before treating images as source renders, reconstructed imports, or native extraction",
4569
+ "treat supplied facts as a closed evidence set; label calculations, assumptions, hypotheses, recommendations, and examples; do not present causal links, rhetorical performance labels, superlatives, sole causes, targets, capabilities, owners, dates, channels, or operational details as established facts unless supplied",
4570
+ "when the requested genre needs unsupplied roles, gates, timelines, channels, or procedures, add one explicit proposed operating model scope note that applies to those details, then complete the requested element",
4571
+ "author complete Squisq-compatible Markdown with annotations bound to headings; ordinary headings default to the loss-averse content template",
4572
+ "for PPTX, 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",
4573
+ "by default, pass the complete Markdown\u2014or a bundle source when assets are needed\u2014directly to convert_document; revise by editing the complete Markdown and converting again",
4574
+ "use create_document_bundle only when a reusable staged DBK materially avoids repeating a large Markdown-and-asset bundle across several operations",
4575
+ "use inspect_document, validate_document, and preview_document when their semantic, diagnostic, or visual evidence is useful; they are review tools rather than required phases",
4576
+ "for visual polish, replace selected content blocks with compatible visual templates while preserving required content",
4577
+ "convert_document creates one or more immutable artifacts from the authoritative complete source",
4459
4578
  "save_artifact only when a durable file is required"
4460
4579
  ],
4461
- markdownAnnotation: '{[templateId key="value"]}',
4580
+ markdownAnnotation: '# Heading {[templateId key="value"]}',
4581
+ standaloneAnnotation: '{[templateId key="value"]}',
4582
+ standaloneWarning: "A standalone annotation creates an additional heading-less block. Bind the annotation to a heading unless that extra block is deliberate.",
4462
4583
  fidelity: {
4463
4584
  semantic: "Prioritize meaning and portability.",
4464
4585
  "editable-native": "Produce editable native structures with target-specific diagnostics.",
@@ -4492,6 +4613,124 @@ function registerAuthoringGuideResource(server) {
4492
4613
  );
4493
4614
  }
4494
4615
  function registerTemplateTools(server, context) {
4616
+ server.registerTool(
4617
+ "get_authoring_context",
4618
+ {
4619
+ description: "Get a compact agent-facing authoring contract plus the complete linked-Squisq catalog in structured content, with optional block recommendations.",
4620
+ inputSchema: z.object({
4621
+ targetFormat: formatInputSchema.optional(),
4622
+ goal: z.enum(["content-first", "visual-polish"]).optional(),
4623
+ source: documentSourceSchema.optional(),
4624
+ maxBlocks: z.number().int().min(1).max(100).optional()
4625
+ }).strict(),
4626
+ outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.get_authoring_context,
4627
+ annotations: READ_ONLY
4628
+ },
4629
+ async ({ targetFormat, goal: requestedGoal, source, maxBlocks }, extra) => {
4630
+ try {
4631
+ return await context.runOperation(extra.signal, async (operationSignal) => {
4632
+ const goal = requestedGoal ?? "content-first";
4633
+ const normalizedTarget = targetFormat?.toLowerCase() ?? null;
4634
+ const formats = boundedFormatCapabilities();
4635
+ if (normalizedTarget) {
4636
+ const target = formats.find((format) => format.id === normalizedTarget);
4637
+ if (!target) throw new Error(`Unknown target format "${normalizedTarget}"`);
4638
+ if (!target.export.supported) {
4639
+ throw new Error(`Format "${normalizedTarget}" is not export-capable`);
4640
+ }
4641
+ }
4642
+ const [templates, schemaModule, transformModule] = await Promise.all([
4643
+ authoringTemplateCatalog(),
4644
+ import("@bendyline/squisq/schemas"),
4645
+ import("@bendyline/squisq/transform")
4646
+ ]);
4647
+ let recommendations = [];
4648
+ let totalBlocks = null;
4649
+ let truncated = false;
4650
+ if (source) {
4651
+ const documents = new DocumentService(await context.authority, context.artifacts);
4652
+ const prepared = await documents.prepare(
4653
+ requireDocumentSource(source),
4654
+ operationSignal
4655
+ );
4656
+ const [{ flattenBlocks }, recommendModule] = await Promise.all([
4657
+ import("@bendyline/squisq/doc"),
4658
+ import("@bendyline/squisq/recommend")
4659
+ ]);
4660
+ const blocks = flattenBlocks(prepared.doc.blocks);
4661
+ const limit = maxBlocks ?? 50;
4662
+ recommendations = blocks.slice(0, limit).map((block) => {
4663
+ const profile = recommendModule.profileBlockContents(block.contents ?? []);
4664
+ const visual = recommendModule.recommendTemplatesForBlock(
4665
+ profile,
4666
+ templates.map((template) => template.id)
4667
+ ).recommended;
4668
+ const recommendedTemplateIds = goal === "content-first" ? ["content", ...visual.filter((id) => id !== "content")] : visual;
4669
+ return {
4670
+ blockId: toWireIdentifier(block.id, "block"),
4671
+ title: boundNullableWireText(block.title, MCP_WIRE_LIMITS7.labelCharacters),
4672
+ profile: {
4673
+ ...profile,
4674
+ hasAsciiDiagram: profile.hasAsciiDiagram ?? false,
4675
+ hasTimeline: profile.hasTimeline ?? false,
4676
+ hasTree: profile.hasTree ?? false
4677
+ },
4678
+ recommendedTemplateIds: recommendedTemplateIds.slice(0, 256).map((id) => requireWireIdentifier(id, "recommended template id"))
4679
+ };
4680
+ });
4681
+ totalBlocks = blocks.length;
4682
+ truncated = recommendations.length < blocks.length;
4683
+ }
4684
+ const payload = {
4685
+ goal,
4686
+ targetFormat: normalizedTarget,
4687
+ defaultTemplateId: "content",
4688
+ defaultFidelity: authoringDefaultFidelity(normalizedTarget, goal),
4689
+ workflow: [
4690
+ "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.",
4693
+ "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
+ "Author complete Squisq-compatible content with heading-bound annotations; ordinary headings default to the loss-averse content template.",
4695
+ 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.",
4698
+ "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.",
4701
+ "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
+ "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.",
4704
+ "Convert to immutable artifacts, inspect conversion reports, and save only final durable outputs."
4705
+ ],
4706
+ syntax: {
4707
+ headingAnnotation: "# Heading {[content]}",
4708
+ standaloneAnnotation: "{[content]}",
4709
+ standaloneWarning: "A standalone annotation creates an additional heading-less block. Bind it to a heading unless that extra block is deliberate."
4710
+ },
4711
+ formats,
4712
+ templates,
4713
+ themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((theme) => ({
4714
+ id: requireWireIdentifier(theme.id, "theme id"),
4715
+ name: boundWireText(theme.name, MCP_WIRE_LIMITS7.labelCharacters, theme.id),
4716
+ description: boundWireText(theme.description ?? "")
4717
+ })),
4718
+ transformStyles: transformModule.getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((style) => ({
4719
+ id: requireWireIdentifier(style.id, "transform style id"),
4720
+ name: boundWireText(style.name, MCP_WIRE_LIMITS7.labelCharacters, style.id),
4721
+ description: boundWireText(style.description)
4722
+ })),
4723
+ recommendations,
4724
+ totalBlocks,
4725
+ truncated
4726
+ };
4727
+ return textAndStructured(payload, compactAuthoringContextText(payload));
4728
+ });
4729
+ } catch (caught) {
4730
+ return errorResult(caught, "inspect", null, extra.signal);
4731
+ }
4732
+ }
4733
+ );
4495
4734
  server.registerTool(
4496
4735
  "list_templates",
4497
4736
  {
@@ -4502,15 +4741,10 @@ function registerTemplateTools(server, context) {
4502
4741
  },
4503
4742
  async () => {
4504
4743
  try {
4505
- const { getAvailableTemplates, TEMPLATE_METADATA } = await import("@bendyline/squisq/doc");
4506
- const templates = getAvailableTemplates().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((id) => ({
4507
- id: requireWireIdentifier(id, "template id"),
4508
- label: boundWireText(
4509
- TEMPLATE_METADATA[id]?.label ?? id,
4510
- MCP_WIRE_LIMITS7.labelCharacters,
4511
- id
4512
- ),
4513
- description: boundWireText(TEMPLATE_METADATA[id]?.description ?? "")
4744
+ const templates = (await authoringTemplateCatalog()).map(({ id, label, description }) => ({
4745
+ id,
4746
+ label,
4747
+ description
4514
4748
  }));
4515
4749
  return textAndStructured({ templates });
4516
4750
  } catch (caught) {
@@ -4528,41 +4762,21 @@ function registerTemplateTools(server, context) {
4528
4762
  },
4529
4763
  async ({ templateId }) => {
4530
4764
  try {
4531
- const { getAvailableTemplates, TEMPLATE_INPUT_DESCRIPTORS, TEMPLATE_METADATA } = await import("@bendyline/squisq/doc");
4532
- if (!getAvailableTemplates().includes(templateId)) {
4765
+ const described = (await authoringTemplateCatalog()).find(
4766
+ (template2) => template2.id === templateId
4767
+ );
4768
+ if (!described) {
4533
4769
  return errorResult(new Error(`Unknown template "${templateId}"`), "validate");
4534
4770
  }
4535
- const inputs = (TEMPLATE_INPUT_DESCRIPTORS[templateId] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
4536
- key: requireWireIdentifier(input.key, "template input key"),
4537
- description: boundWireText(input.description),
4538
- type: boundWireText(
4539
- input.coerce ?? "string",
4540
- MCP_WIRE_LIMITS7.labelCharacters,
4541
- "string"
4542
- ),
4543
- required: input.required ?? false,
4544
- values: boundWireTextArray(
4545
- input.values ?? [],
4546
- MCP_WIRE_LIMITS7.arrayEntries,
4547
- MCP_WIRE_LIMITS7.labelCharacters
4548
- ),
4549
- valueHint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
4550
- }));
4551
- const metadata = TEMPLATE_METADATA[templateId];
4552
4771
  const template = {
4553
- id: requireWireIdentifier(templateId, "template id"),
4554
- label: boundWireText(
4555
- metadata?.label ?? templateId,
4556
- MCP_WIRE_LIMITS7.labelCharacters,
4557
- templateId
4558
- ),
4559
- description: boundWireText(metadata?.description ?? ""),
4560
- inputs
4772
+ id: described.id,
4773
+ label: described.label,
4774
+ description: described.description,
4775
+ inputs: described.inputs
4561
4776
  };
4562
- const required = inputs.filter((input) => input.required).map((input) => `${input.key}="\u2026"`).join(" ");
4563
4777
  return textAndStructured({
4564
4778
  template,
4565
- annotationExample: boundWireText(`{[${templateId}${required ? ` ${required}` : ""}]}`)
4779
+ annotationExample: described.annotationExample
4566
4780
  });
4567
4781
  } catch (caught) {
4568
4782
  return errorResult(caught, "inspect");
@@ -4920,12 +5134,40 @@ function artifactLink(artifact) {
4920
5134
  async function sendProgress(extra, progress, total, message) {
4921
5135
  await reportMcpProgress(extra, progress, total, message);
4922
5136
  }
4923
- function textAndStructured(payload) {
5137
+ function textAndStructured(payload, text = JSON.stringify(payload, null, 2)) {
4924
5138
  return {
4925
- content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
5139
+ content: [{ type: "text", text }],
4926
5140
  structuredContent: asStructuredContent(successResult(payload))
4927
5141
  };
4928
5142
  }
5143
+ function compactAuthoringContextText(context) {
5144
+ const target = context.targetFormat ?? "unspecified target";
5145
+ const lines = [
5146
+ `DocBlocks authoring contract: ${target}, ${context.goal}.`,
5147
+ `Default template: ${context.defaultTemplateId}. Default fidelity: ${context.defaultFidelity ?? "select for target"}.`,
5148
+ "",
5149
+ "Required workflow:",
5150
+ ...context.workflow.map((step, index) => `${index + 1}. ${step}`),
5151
+ "",
5152
+ `Heading annotation: ${context.syntax.headingAnnotation}`,
5153
+ `Warning: ${context.syntax.standaloneWarning}`,
5154
+ `Templates (${context.templates.length}): ${context.templates.map(({ id }) => id).join(", ")}`,
5155
+ `Themes (${context.themes.length}): ${context.themes.map(({ id }) => id).join(", ")}`,
5156
+ `Transform styles (${context.transformStyles.length}): ${context.transformStyles.map(({ id }) => id).join(", ")}`
5157
+ ];
5158
+ if (context.recommendations.length > 0) {
5159
+ lines.push(
5160
+ `Recommended blocks: ${context.recommendations.map(
5161
+ ({ blockId, recommendedTemplateIds }) => `${blockId} -> ${recommendedTemplateIds.join("/")}`
5162
+ ).join("; ")}`
5163
+ );
5164
+ }
5165
+ lines.push(
5166
+ "",
5167
+ "The complete exact format, template-input, theme, transform, and recommendation catalog is in structuredContent. Use describe_template for focused follow-up when structured content is unavailable."
5168
+ );
5169
+ return lines.join("\n");
5170
+ }
4929
5171
  function asStructuredContent(payload) {
4930
5172
  return payload;
4931
5173
  }
@@ -4955,6 +5197,52 @@ function summarizeTheme(theme) {
4955
5197
  titleFont: boundWireText(JSON.stringify(theme.typography.titleFont) ?? "")
4956
5198
  };
4957
5199
  }
5200
+ async function authoringTemplateCatalog() {
5201
+ const {
5202
+ getAvailableTemplates,
5203
+ TEMPLATE_AUTHORING_METADATA,
5204
+ TEMPLATE_INPUT_DESCRIPTORS,
5205
+ TEMPLATE_METADATA
5206
+ } = await import("@bendyline/squisq/doc");
5207
+ const authoring = TEMPLATE_AUTHORING_METADATA;
5208
+ return getAvailableTemplates().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((id) => {
5209
+ const metadata = TEMPLATE_METADATA[id];
5210
+ const behavior = authoring[id];
5211
+ if (!behavior) throw new Error(`Template "${id}" has no authoring metadata`);
5212
+ const inputs = (TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
5213
+ key: requireWireIdentifier(input.key, "template input key"),
5214
+ description: boundWireText(input.description),
5215
+ type: boundWireText(input.coerce ?? "string", MCP_WIRE_LIMITS7.labelCharacters, "string"),
5216
+ required: input.required ?? false,
5217
+ values: boundWireTextArray(
5218
+ input.values ?? [],
5219
+ MCP_WIRE_LIMITS7.arrayEntries,
5220
+ MCP_WIRE_LIMITS7.labelCharacters
5221
+ ),
5222
+ valueHint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
5223
+ }));
5224
+ const required = inputs.filter((input) => input.required).map((input) => `${input.key}="\u2026"`).join(" ");
5225
+ return {
5226
+ id: requireWireIdentifier(id, "template id"),
5227
+ label: boundWireText(metadata?.label ?? id, MCP_WIRE_LIMITS7.labelCharacters, id),
5228
+ description: boundWireText(metadata?.description ?? ""),
5229
+ inputs,
5230
+ ...behavior,
5231
+ annotationExample: boundWireText(`# Heading {[${id}${required ? ` ${required}` : ""}]}`)
5232
+ };
5233
+ });
5234
+ }
5235
+ function authoringDefaultFidelity(targetFormat, goal) {
5236
+ if (!targetFormat) return null;
5237
+ if (targetFormat === "mp4" || targetFormat === "gif") return "rendered-fidelity";
5238
+ if (goal === "visual-polish" && (targetFormat === "pptx" || targetFormat === "pdf")) {
5239
+ return "rendered-fidelity";
5240
+ }
5241
+ if (targetFormat === "docx" || targetFormat === "pptx" || targetFormat === "xlsx") {
5242
+ return "editable-native";
5243
+ }
5244
+ return "semantic";
5245
+ }
4958
5246
  function boundedFormatCapabilities() {
4959
5247
  return MCP_FORMAT_CAPABILITIES.slice(0, 64).map((format) => {
4960
5248
  const id = requireWireIdentifier(format.id, "format id");
@@ -5345,32 +5633,33 @@ function registerAuthoringPrompts(server) {
5345
5633
  function presentationPrompt(topic, style, theme, template) {
5346
5634
  return `Create a presentation about: ${topic}
5347
5635
 
5348
- 1. Use list_formats, list_themes, list_transform_styles, and list_templates for current linked-Squisq capabilities.
5349
- 2. Author focused Markdown sections with accessible alt text. Prefer style "${style ?? "choose from list_transform_styles"}", theme "${theme ?? "choose from list_themes"}", and template "${template ?? "choose per block"}".
5350
- 3. Call validate_document with targetFormat "pptx" and repair actionable diagnostics.
5351
- 4. Call convert_document with a pptx target. Choose editable-native for editable Office structures, rendered-fidelity for exact Squisq visuals, or hybrid for rendered visuals plus semantic retention.
5352
- 5. Call preview_document and inspect the bounded slide images.
5353
- 6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
5636
+ 1. Call get_authoring_context with targetFormat "pptx" and goal "content-first" for the complete linked-Squisq vocabulary and workflow in one response.
5637
+ 2. Treat the topic facts as a closed evidence set. Preserve them exactly, use temporal or correlational wording unless causality is supplied, and label calculations, assumptions, hypotheses, recommendations, and examples. Causal links, rhetorical performance labels, superlatives, sole causes, 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 the requested content needs unsupplied operating details, add one proposed operating model scope note that applies to them.
5638
+ 3. 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; use lists, tables, or bold labels within a slide. Target at most 80 words per slide. Make slide one a supported point-of-view thesis, not a generic title or unsupported flourish. For requested choices, state concrete opportunity costs grounded in supplied alternatives and one clearly labeled proposed accountable role per choice. Label any unsupplied capacity or outcome assumption as \`Assumption:\` or \`Potential tradeoff:\`. Author complete Markdown sections with accessible alt text. Put annotations on headings, for example \`# Heading {[content]}\`; a standalone \`{[template]}\` creates an extra block. Prefer style "${style ?? "choose from the returned transform styles"}", theme "${theme ?? "choose from the returned themes"}", and template "${template ?? "content until visual optimization"}".
5639
+ 4. Keep the complete Markdown as the authoritative draft. Pass it\u2014or a bundle source when assets are needed\u2014directly to convert_document. Use create_document_bundle only when the same materially large Markdown-and-asset bundle will feed several operations. Do not write a temporary local Markdown file or invent a root id.
5640
+ 5. Before conversion, count slide sections, verify every requested element, and rewrite or label every unsupported claim. Label any new review cadence or measurement procedure as proposed. Use validate_document when syntax, accessibility, retention, or target diagnostics need review; use preview_document when visual evidence such as overflow needs review. Repair findings in the complete Markdown.
5641
+ 6. Only after content coverage is acceptable, replace selected \`content\` blocks with compatible visual templates such as \`definitionCard\`, \`dataTable\`, \`comparisonBar\`, or \`list\` when their semantic inputs fit. Preserve required content.
5642
+ 7. Call convert_document with the complete source and a pptx target. Choose editable-native for editable Office structures, rendered-fidelity for exact Squisq visuals, or hybrid for rendered visuals plus semantic retention. Revise by editing the complete Markdown and converting again.
5643
+ 8. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
5354
5644
  }
5355
5645
  function videoPrompt(topic, orientation, theme, template) {
5356
5646
  return `Create a video about: ${topic}
5357
5647
 
5358
- 1. Use list_formats, list_themes, list_transform_styles, and list_templates for current linked-Squisq capabilities.
5359
- 2. Author concise Markdown for a ${orientation ?? "landscape"} animated sequence using theme "${theme ?? "choose from list_themes"}" and template "${template ?? "choose per block"}".
5360
- 3. Call validate_document with targetFormat "mp4" and repair actionable diagnostics.
5361
- 4. Call preview_document to review representative frames.
5362
- 5. Call convert_document with an mp4 target and orientation "${orientation ?? "landscape"}"; monitor progress and honor cancellation.
5363
- 6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
5648
+ 1. Call get_authoring_context with targetFormat "mp4" for the complete linked-Squisq vocabulary and workflow.
5649
+ 2. Author concise Markdown for a ${orientation ?? "landscape"} animated sequence using theme "${theme ?? "choose from the returned themes"}" and template "${template ?? "content until visual optimization"}". Bind annotations to headings.
5650
+ 3. Keep the complete Markdown as the authoritative draft and pass it\u2014or a bundle source when assets are needed\u2014directly to convert_document. Use validate_document for uncertain target constraints and preview_document when representative-frame evidence is useful; repair findings in the complete Markdown.
5651
+ 4. Call convert_document with the complete source, an mp4 target, and orientation "${orientation ?? "landscape"}"; monitor progress and honor cancellation. Revise by editing the complete Markdown and converting again.
5652
+ 5. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
5364
5653
  }
5365
5654
  function documentPrompt(topic, format, theme, template) {
5366
5655
  const target = format ?? "pdf";
5367
5656
  return `Create a professional document about: ${topic}
5368
5657
 
5369
- 1. Use list_formats, list_themes, list_transform_styles, and list_templates for current linked-Squisq capabilities.
5370
- 2. Author structured Markdown using theme "${theme ?? "choose from list_themes"}" and template "${template ?? "choose per block"}".
5371
- 3. Call validate_document with targetFormat "${target}" and repair actionable diagnostics.
5372
- 4. Call convert_document with a ${target} target to create an immutable artifact.
5373
- 5. Call preview_document and review the bounded page images.
5658
+ 1. Call get_authoring_context with targetFormat "${target}" for the complete linked-Squisq vocabulary and workflow.
5659
+ 2. Treat the topic facts as a closed evidence set. Preserve them exactly, use temporal or correlational wording unless causality is supplied, and label calculations, assumptions, hypotheses, recommendations, and examples. Causal links, superlatives, sole causes, capabilities, owners, dates, channels, and operational details must be supplied or explicitly framed. When the requested genre needs unsupplied roles, gates, timelines, channels, or procedures, add one proposed operating model scope note that applies to those details.
5660
+ 3. Honor the requested word range and document genre. Author structured Markdown using theme "${theme ?? "choose from the returned themes"}" and template "${template ?? "content until visual optimization"}". Bind annotations to headings. Use connected memo prose for executive decisions and native headings, tables, and checklists for operational documents; retain complete-body content when a visual template would discard detail.
5661
+ 4. Before conversion, count document words, verify every requested element, and rewrite or label every unsupported claim. Keep the complete Markdown as the authoritative draft and pass it\u2014or a bundle source when assets are needed\u2014directly to convert_document. Use validate_document when syntax, accessibility, retention, or target diagnostics need review, and preview_document when page-layout evidence is useful; repair findings in the complete Markdown.
5662
+ 5. Call convert_document with the complete source and a ${target} target to create an immutable artifact. Revise by editing the complete Markdown and converting again.
5374
5663
  6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
5375
5664
  }
5376
5665
  function complete(values, prefix) {
@@ -5410,7 +5699,12 @@ function createMcpServer(options = {}) {
5410
5699
  options.shutdownDrainTimeoutMs ?? DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS
5411
5700
  );
5412
5701
  const artifacts = new ArtifactStore(options);
5413
- const server = new McpServer({ name: "docblocks", version: getPackageVersion() });
5702
+ const server = new McpServer(
5703
+ { name: "docblocks", version: getPackageVersion() },
5704
+ {
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."
5706
+ }
5707
+ );
5414
5708
  registerAgenticTools(server, {
5415
5709
  authority,
5416
5710
  artifacts,