@bendyline/docblocks-cli 2.2.1 → 2.3.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/README.md +9 -1
- package/THIRD_PARTY_NOTICES.txt +16689 -0
- package/dist/bin.js +116 -299
- package/dist/eval/cli.js +3 -3
- package/package.json +11 -10
package/dist/bin.js
CHANGED
|
@@ -1831,47 +1831,6 @@ async function inspectPreparedDocument(_documents, prepared, maximumBlocks = DEF
|
|
|
1831
1831
|
diagnostics: boundDiagnostics(diagnostics, "inspect")
|
|
1832
1832
|
};
|
|
1833
1833
|
}
|
|
1834
|
-
async function validatePreparedDocument(_documents, prepared, targetFormat, signal) {
|
|
1835
|
-
throwIfAborted5(signal);
|
|
1836
|
-
const normalizedTargetFormat = normalizeTargetFormat(targetFormat);
|
|
1837
|
-
const { validateMarkdownSource } = await import("@bendyline/squisq/doc");
|
|
1838
|
-
await yieldForCancellation(signal);
|
|
1839
|
-
const assets = prepared.assets;
|
|
1840
|
-
const validation = validateMarkdownSource(prepared.markdown, {
|
|
1841
|
-
assets: new Set(assets.map((asset) => asset.path))
|
|
1842
|
-
});
|
|
1843
|
-
throwIfAborted5(signal);
|
|
1844
|
-
const diagnostics = [
|
|
1845
|
-
...prepared.diagnostics,
|
|
1846
|
-
...validation.diagnostics.map(
|
|
1847
|
-
(diagnostic) => mapDocDiagnostic(diagnostic, prepared.sourceFormat)
|
|
1848
|
-
),
|
|
1849
|
-
...missingAltTextDiagnostics(
|
|
1850
|
-
prepared.markdownDoc,
|
|
1851
|
-
normalizedTargetFormat ?? prepared.sourceFormat
|
|
1852
|
-
),
|
|
1853
|
-
...await authoringDiagnostics(
|
|
1854
|
-
prepared,
|
|
1855
|
-
"validate",
|
|
1856
|
-
normalizedTargetFormat ?? prepared.sourceFormat,
|
|
1857
|
-
signal
|
|
1858
|
-
),
|
|
1859
|
-
...normalizedTargetFormat ? await targetPreflight(prepared, normalizedTargetFormat, signal) : []
|
|
1860
|
-
];
|
|
1861
|
-
await yieldForCancellation(signal);
|
|
1862
|
-
const unique = boundDiagnostics(diagnostics, "validate");
|
|
1863
|
-
throwIfAborted5(signal);
|
|
1864
|
-
const summary = summarizeDiagnostics(unique);
|
|
1865
|
-
return {
|
|
1866
|
-
version: DOCBLOCKS_MCP_WIRE_VERSION,
|
|
1867
|
-
kind: "validation",
|
|
1868
|
-
sourceFormat: prepared.sourceFormat,
|
|
1869
|
-
targetFormat: normalizedTargetFormat,
|
|
1870
|
-
valid: summary.errorCount === 0,
|
|
1871
|
-
summary,
|
|
1872
|
-
diagnostics: unique
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
1834
|
function annotationSyntaxDiagnostics(markdown, stage, format) {
|
|
1876
1835
|
const diagnostics = [];
|
|
1877
1836
|
const lines = markdown.split(/\r?\n/);
|
|
@@ -1927,7 +1886,7 @@ function malformedAnnotationDiagnostic(stage, format, line, column, message) {
|
|
|
1927
1886
|
format,
|
|
1928
1887
|
count: 1,
|
|
1929
1888
|
message,
|
|
1930
|
-
remediation: 'Repair the annotation so it reads exactly `{[templateId key="value"]}` with no extra braces or brackets
|
|
1889
|
+
remediation: 'Repair the annotation so it reads exactly `{[templateId key="value"]}` with no extra braces or brackets.',
|
|
1931
1890
|
retryable: false,
|
|
1932
1891
|
location: { kind: "source", line, column }
|
|
1933
1892
|
};
|
|
@@ -2013,7 +1972,7 @@ async function authoringDiagnostics(prepared, stage, format, signal) {
|
|
|
2013
1972
|
format,
|
|
2014
1973
|
count: 1,
|
|
2015
1974
|
message: `The complete body of block "${block.title ?? block.id}" is not present in its rendered text layers.`,
|
|
2016
|
-
remediation: "Keep the content template
|
|
1975
|
+
remediation: "Keep the content template or repair its inputs; request a preview only when visual evidence is useful.",
|
|
2017
1976
|
retryable: false,
|
|
2018
1977
|
location: {
|
|
2019
1978
|
kind: "block",
|
|
@@ -2400,7 +2359,7 @@ function mapDocDiagnostic(diagnostic, format) {
|
|
|
2400
2359
|
return {
|
|
2401
2360
|
code: diagnostic.code,
|
|
2402
2361
|
severity: diagnostic.severity,
|
|
2403
|
-
stage: "
|
|
2362
|
+
stage: "inspect",
|
|
2404
2363
|
format,
|
|
2405
2364
|
count: 1,
|
|
2406
2365
|
message: diagnostic.message,
|
|
@@ -2429,7 +2388,7 @@ function missingAltTextDiagnostics(markdownDoc, format) {
|
|
|
2429
2388
|
{
|
|
2430
2389
|
code: "missing-alt-text",
|
|
2431
2390
|
severity: "warning",
|
|
2432
|
-
stage: "
|
|
2391
|
+
stage: "inspect",
|
|
2433
2392
|
format,
|
|
2434
2393
|
count: missingCount,
|
|
2435
2394
|
message: missingCount === 1 ? "One image has no alternative text." : `${missingCount} images have no alternative text.`,
|
|
@@ -2439,130 +2398,6 @@ function missingAltTextDiagnostics(markdownDoc, format) {
|
|
|
2439
2398
|
}
|
|
2440
2399
|
];
|
|
2441
2400
|
}
|
|
2442
|
-
async function targetPreflight(prepared, targetFormat, signal) {
|
|
2443
|
-
throwIfAborted5(signal);
|
|
2444
|
-
const normalized = targetFormat.toLowerCase();
|
|
2445
|
-
const { createCliRegistry: createCliRegistry2 } = await import("@bendyline/squisq-cli/api");
|
|
2446
|
-
throwIfAborted5(signal);
|
|
2447
|
-
const target = createCliRegistry2().get(normalized);
|
|
2448
|
-
if (!target?.exportDoc) {
|
|
2449
|
-
return [
|
|
2450
|
-
{
|
|
2451
|
-
code: target ? "unsupported-output" : "unknown-format",
|
|
2452
|
-
severity: "error",
|
|
2453
|
-
stage: "validate",
|
|
2454
|
-
format: normalized,
|
|
2455
|
-
count: 1,
|
|
2456
|
-
message: target ? `${target.label} cannot be used as a conversion target.` : `Unknown target format "${normalized}".`,
|
|
2457
|
-
remediation: "Choose an export-capable format from list_formats.",
|
|
2458
|
-
retryable: false,
|
|
2459
|
-
location: null
|
|
2460
|
-
}
|
|
2461
|
-
];
|
|
2462
|
-
}
|
|
2463
|
-
const diagnostics = [];
|
|
2464
|
-
const details = summarizeMarkdownNodes(prepared.markdownDoc);
|
|
2465
|
-
const { flattenBlocks } = await import("@bendyline/squisq/doc");
|
|
2466
|
-
const flatBlocks = flattenBlocks(prepared.doc.blocks);
|
|
2467
|
-
await yieldForCancellation(signal);
|
|
2468
|
-
if (["pptx", "pdf", "mp4", "gif"].includes(normalized)) {
|
|
2469
|
-
let denseBlockCount = 0;
|
|
2470
|
-
let firstDenseBlock = null;
|
|
2471
|
-
let analyzedIndex = 0;
|
|
2472
|
-
for (const entry of await analyzeDocumentBlocks(prepared.doc.blocks)) {
|
|
2473
|
-
const checkpoint = cancellationCheckpoint(signal, analyzedIndex);
|
|
2474
|
-
if (checkpoint) await checkpoint;
|
|
2475
|
-
analyzedIndex += 1;
|
|
2476
|
-
if (entry.bodyWordCount <= 120 && entry.plainText.length <= 900) continue;
|
|
2477
|
-
denseBlockCount += 1;
|
|
2478
|
-
firstDenseBlock ?? (firstDenseBlock = entry.block);
|
|
2479
|
-
}
|
|
2480
|
-
if (denseBlockCount > 0) {
|
|
2481
|
-
diagnostics.push({
|
|
2482
|
-
code: "content-density-high",
|
|
2483
|
-
severity: "warning",
|
|
2484
|
-
stage: "validate",
|
|
2485
|
-
format: normalized,
|
|
2486
|
-
count: denseBlockCount,
|
|
2487
|
-
message: denseBlockCount === 1 ? `Block "${firstDenseBlock?.title ?? firstDenseBlock?.id ?? "unknown"}" may be too dense for a single rendered item.` : `${denseBlockCount} blocks may be too dense for a single rendered item.`,
|
|
2488
|
-
remediation: "Split the block, shorten prose, or confirm the result with preview_document overflow diagnostics.",
|
|
2489
|
-
retryable: false,
|
|
2490
|
-
location: denseBlockCount === 1 && firstDenseBlock ? {
|
|
2491
|
-
kind: "block",
|
|
2492
|
-
blockId: toWireIdentifier(firstDenseBlock.id, "block"),
|
|
2493
|
-
nodeType: "block"
|
|
2494
|
-
} : null
|
|
2495
|
-
});
|
|
2496
|
-
}
|
|
2497
|
-
const denseTables = details.tables.filter(
|
|
2498
|
-
(table) => table.columnCount > 8 || table.rowCount > 30
|
|
2499
|
-
);
|
|
2500
|
-
if (denseTables.length > 0) {
|
|
2501
|
-
const firstDenseTable = denseTables[0];
|
|
2502
|
-
diagnostics.push({
|
|
2503
|
-
code: "table-density-high",
|
|
2504
|
-
severity: "warning",
|
|
2505
|
-
stage: "validate",
|
|
2506
|
-
format: normalized,
|
|
2507
|
-
count: denseTables.length,
|
|
2508
|
-
message: denseTables.length === 1 && firstDenseTable ? `Table ${firstDenseTable.index + 1} has ${firstDenseTable.rowCount} rows and ${firstDenseTable.columnCount} columns and may overflow.` : `${denseTables.length} tables may overflow the rendered output.`,
|
|
2509
|
-
remediation: "Split the table or use a spreadsheet target; verify visual clipping with preview_document.",
|
|
2510
|
-
retryable: false,
|
|
2511
|
-
location: null
|
|
2512
|
-
});
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
if (normalized === "pptx" && flatBlocks.some((block) => block.template)) {
|
|
2516
|
-
diagnostics.push(
|
|
2517
|
-
warningDiagnostic(
|
|
2518
|
-
"Editable-native PPTX export preserves semantic content and theme but does not reproduce arbitrary Squisq template geometry.",
|
|
2519
|
-
"validate",
|
|
2520
|
-
normalized
|
|
2521
|
-
)
|
|
2522
|
-
);
|
|
2523
|
-
}
|
|
2524
|
-
if (normalized === "pdf" && await hasImages(prepared.markdownDoc, signal)) {
|
|
2525
|
-
diagnostics.push(
|
|
2526
|
-
warningDiagnostic(
|
|
2527
|
-
"Native PDF export currently represents Markdown images as labeled placeholders.",
|
|
2528
|
-
"validate",
|
|
2529
|
-
normalized
|
|
2530
|
-
)
|
|
2531
|
-
);
|
|
2532
|
-
}
|
|
2533
|
-
if ((normalized === "csv" || normalized === "xlsx") && details.tableCount === 0) {
|
|
2534
|
-
diagnostics.push(
|
|
2535
|
-
warningDiagnostic(
|
|
2536
|
-
`${normalized.toUpperCase()} export is table-oriented; this document contains no Markdown tables.`,
|
|
2537
|
-
"validate",
|
|
2538
|
-
normalized
|
|
2539
|
-
)
|
|
2540
|
-
);
|
|
2541
|
-
}
|
|
2542
|
-
if (normalized === "gif" && (prepared.doc.audio?.segments?.length ?? 0) > 0) {
|
|
2543
|
-
diagnostics.push(
|
|
2544
|
-
warningDiagnostic(
|
|
2545
|
-
"Animated GIF does not support audio; audio will be omitted.",
|
|
2546
|
-
"validate",
|
|
2547
|
-
normalized
|
|
2548
|
-
)
|
|
2549
|
-
);
|
|
2550
|
-
}
|
|
2551
|
-
return diagnostics;
|
|
2552
|
-
}
|
|
2553
|
-
async function hasImages(markdownDoc, signal) {
|
|
2554
|
-
throwIfAborted5(signal);
|
|
2555
|
-
let found = false;
|
|
2556
|
-
const visit = (node) => {
|
|
2557
|
-
if (found || !node || typeof node !== "object") return;
|
|
2558
|
-
const candidate = node;
|
|
2559
|
-
if (candidate.type === "image" || candidate.type === "imageReference") found = true;
|
|
2560
|
-
if (Array.isArray(candidate.children)) candidate.children.forEach(visit);
|
|
2561
|
-
};
|
|
2562
|
-
visit(markdownDoc);
|
|
2563
|
-
await yieldForCancellation(signal);
|
|
2564
|
-
return found;
|
|
2565
|
-
}
|
|
2566
2401
|
function boundDiagnostics(diagnostics, stage) {
|
|
2567
2402
|
const aggregated = aggregateDiagnostics(diagnostics);
|
|
2568
2403
|
if (aggregated.length <= MAX_DIAGNOSTICS) return aggregated;
|
|
@@ -2643,29 +2478,6 @@ function sameLocation(left, right) {
|
|
|
2643
2478
|
function safeDiagnosticCountSum(left, right) {
|
|
2644
2479
|
return Math.min(Number.MAX_SAFE_INTEGER, left + right);
|
|
2645
2480
|
}
|
|
2646
|
-
function summarizeDiagnostics(diagnostics) {
|
|
2647
|
-
let errorCount = 0;
|
|
2648
|
-
let warningCount2 = 0;
|
|
2649
|
-
let infoCount = 0;
|
|
2650
|
-
for (const diagnostic of diagnostics) {
|
|
2651
|
-
if (diagnostic.severity === "error") {
|
|
2652
|
-
errorCount = safeDiagnosticCountSum(errorCount, diagnostic.count);
|
|
2653
|
-
} else if (diagnostic.severity === "warning") {
|
|
2654
|
-
warningCount2 = safeDiagnosticCountSum(warningCount2, diagnostic.count);
|
|
2655
|
-
} else {
|
|
2656
|
-
infoCount = safeDiagnosticCountSum(infoCount, diagnostic.count);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
return { errorCount, warningCount: warningCount2, infoCount };
|
|
2660
|
-
}
|
|
2661
|
-
function normalizeTargetFormat(targetFormat) {
|
|
2662
|
-
if (targetFormat === null) return null;
|
|
2663
|
-
const normalized = targetFormat.toLowerCase();
|
|
2664
|
-
if (!FORMAT_PATTERN.test(normalized)) {
|
|
2665
|
-
throw new Error(`Invalid target format "${targetFormat.slice(0, 64)}"`);
|
|
2666
|
-
}
|
|
2667
|
-
return normalized;
|
|
2668
|
-
}
|
|
2669
2481
|
function words(text) {
|
|
2670
2482
|
return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
2671
2483
|
}
|
|
@@ -4235,8 +4047,7 @@ import {
|
|
|
4235
4047
|
parseDocumentSource,
|
|
4236
4048
|
parseInspectionResult,
|
|
4237
4049
|
parseMaterializationOptions,
|
|
4238
|
-
parsePreviewResult
|
|
4239
|
-
parseValidationResult
|
|
4050
|
+
parsePreviewResult
|
|
4240
4051
|
} from "@bendyline/docblocks/mcp";
|
|
4241
4052
|
import {
|
|
4242
4053
|
artifactUriSchema,
|
|
@@ -4253,7 +4064,7 @@ function registerAgenticTools(server, context) {
|
|
|
4253
4064
|
server.registerTool(
|
|
4254
4065
|
"list_roots",
|
|
4255
4066
|
{
|
|
4256
|
-
description: "
|
|
4067
|
+
description: "Call first when durable local output is requested. Lists opaque startup-granted roots; if none is write-enabled, save_artifact cannot publish a file and the MCP server must be restarted with --allow-write.",
|
|
4257
4068
|
inputSchema: z.object({}).strict(),
|
|
4258
4069
|
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.list_roots,
|
|
4259
4070
|
annotations: READ_ONLY
|
|
@@ -4261,7 +4072,7 @@ function registerAgenticTools(server, context) {
|
|
|
4261
4072
|
async () => {
|
|
4262
4073
|
try {
|
|
4263
4074
|
const roots = (await context.authority).listRoots();
|
|
4264
|
-
return textAndStructured({ roots });
|
|
4075
|
+
return textAndStructured({ roots }, rootDiscoveryText(roots));
|
|
4265
4076
|
} catch (caught) {
|
|
4266
4077
|
return errorResult(caught, "resolve");
|
|
4267
4078
|
}
|
|
@@ -4288,13 +4099,15 @@ function registerAgenticTools(server, context) {
|
|
|
4288
4099
|
server.registerTool(
|
|
4289
4100
|
"convert_document",
|
|
4290
4101
|
{
|
|
4291
|
-
description: "Convert
|
|
4102
|
+
description: "Convert plain text, Markdown, bundles, or linked-registry inputs directly into one or more immutable artifacts. Plain Markdown needs no preflight or annotations; valid Squisq annotations are optional layout hints. Use save_artifact only for durable filesystem output.",
|
|
4292
4103
|
inputSchema: z.object({
|
|
4293
4104
|
source: documentSourceSchema,
|
|
4294
4105
|
targets: z.array(conversionTargetSchema).min(1).max(12),
|
|
4295
4106
|
themeId: identifierSchema.optional(),
|
|
4296
4107
|
transformId: identifierSchema.optional(),
|
|
4297
|
-
autoTemplates: z.boolean().optional()
|
|
4108
|
+
autoTemplates: z.boolean().optional().describe(
|
|
4109
|
+
"Enable content-aware automatic template selection. Defaults to true; explicit annotations still take precedence."
|
|
4110
|
+
),
|
|
4298
4111
|
title: z.string().max(4e3).optional()
|
|
4299
4112
|
}).strict(),
|
|
4300
4113
|
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.convert_document,
|
|
@@ -4314,7 +4127,7 @@ function registerAgenticTools(server, context) {
|
|
|
4314
4127
|
targets: targets.map(toConversionTarget),
|
|
4315
4128
|
themeId,
|
|
4316
4129
|
transformId,
|
|
4317
|
-
autoTemplates,
|
|
4130
|
+
autoTemplates: autoTemplates ?? true,
|
|
4318
4131
|
title
|
|
4319
4132
|
},
|
|
4320
4133
|
operationSignal,
|
|
@@ -4338,7 +4151,7 @@ function registerAgenticTools(server, context) {
|
|
|
4338
4151
|
server.registerTool(
|
|
4339
4152
|
"create_document_bundle",
|
|
4340
4153
|
{
|
|
4341
|
-
description: "
|
|
4154
|
+
description: "Stage Markdown plus authority-scoped assets as a reusable immutable DBK artifact when assets must travel with the document or the same draft will be passed to two or more inspect, preview, or convert calls. Pass its artifact URI instead of repeating the Markdown.",
|
|
4342
4155
|
inputSchema: z.object({ source: bundleDocumentSourceSchema }).strict(),
|
|
4343
4156
|
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.create_document_bundle,
|
|
4344
4157
|
annotations: ARTIFACT_CREATING
|
|
@@ -4412,7 +4225,7 @@ function registerAgenticTools(server, context) {
|
|
|
4412
4225
|
server.registerTool(
|
|
4413
4226
|
"inspect_document",
|
|
4414
4227
|
{
|
|
4415
|
-
description: "Inspect
|
|
4228
|
+
description: "Inspect bounded semantic structure, block provenance, assets, metadata, theme, and diagnostics when the user asks for document analysis. Inspection is never required before conversion.",
|
|
4416
4229
|
inputSchema: z.object({
|
|
4417
4230
|
source: documentSourceSchema,
|
|
4418
4231
|
maxBlocks: z.number().int().min(1).max(2e3).optional(),
|
|
@@ -4441,37 +4254,6 @@ function registerAgenticTools(server, context) {
|
|
|
4441
4254
|
}
|
|
4442
4255
|
}
|
|
4443
4256
|
);
|
|
4444
|
-
server.registerTool(
|
|
4445
|
-
"validate_document",
|
|
4446
|
-
{
|
|
4447
|
-
description: "Validate document structure, templates, annotations, assets, accessibility, and optional target-format fidelity before export.",
|
|
4448
|
-
inputSchema: z.object({
|
|
4449
|
-
source: documentSourceSchema,
|
|
4450
|
-
targetFormat: formatInputSchema.nullable().optional()
|
|
4451
|
-
}).strict(),
|
|
4452
|
-
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.validate_document,
|
|
4453
|
-
annotations: READ_ONLY
|
|
4454
|
-
},
|
|
4455
|
-
async ({ source, targetFormat }, extra) => {
|
|
4456
|
-
try {
|
|
4457
|
-
return await context.runOperation(extra.signal, async (operationSignal) => {
|
|
4458
|
-
const documents = new DocumentService(await context.authority, context.artifacts);
|
|
4459
|
-
const prepared = await documents.prepare(requireDocumentSource(source), operationSignal);
|
|
4460
|
-
const normalizedTargetFormat = targetFormat?.toLowerCase() ?? null;
|
|
4461
|
-
const result = await validatePreparedDocument(
|
|
4462
|
-
documents,
|
|
4463
|
-
prepared,
|
|
4464
|
-
normalizedTargetFormat,
|
|
4465
|
-
operationSignal
|
|
4466
|
-
);
|
|
4467
|
-
requireWire(parseValidationResult(result), "validation");
|
|
4468
|
-
return textAndStructured(result);
|
|
4469
|
-
});
|
|
4470
|
-
} catch (caught) {
|
|
4471
|
-
return errorResult(caught, "validate", null, extra.signal);
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
|
-
);
|
|
4475
4257
|
server.registerTool(
|
|
4476
4258
|
"compare_documents",
|
|
4477
4259
|
{
|
|
@@ -4617,7 +4399,7 @@ function registerAuthoringGuideResource(server) {
|
|
|
4617
4399
|
"authoring-guide",
|
|
4618
4400
|
"docblocks://authoring-guide",
|
|
4619
4401
|
{
|
|
4620
|
-
description: "
|
|
4402
|
+
description: "Complete linked-Squisq authoring catalog with exact template inputs, fidelity modes, themes, and transform styles. Read only when the focused authoring context is insufficient.",
|
|
4621
4403
|
mimeType: "application/json",
|
|
4622
4404
|
annotations: { audience: ["assistant"], priority: 1 }
|
|
4623
4405
|
},
|
|
@@ -4628,18 +4410,14 @@ function registerAuthoringGuideResource(server) {
|
|
|
4628
4410
|
import("@bendyline/squisq/transform")
|
|
4629
4411
|
]);
|
|
4630
4412
|
const guide = {
|
|
4631
|
-
version:
|
|
4413
|
+
version: 8,
|
|
4632
4414
|
workflow: [
|
|
4633
|
-
"
|
|
4634
|
-
"
|
|
4635
|
-
"
|
|
4636
|
-
"
|
|
4637
|
-
"
|
|
4638
|
-
"
|
|
4639
|
-
"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",
|
|
4640
|
-
"for visual polish, replace selected content blocks with compatible visual templates while preserving required content",
|
|
4641
|
-
"convert_document creates one or more immutable artifacts from the authoritative complete source",
|
|
4642
|
-
"save_artifact only when a durable file is required"
|
|
4415
|
+
"plain text and ordinary Markdown can be passed directly to convert_document without a preflight or template annotations",
|
|
4416
|
+
"for deliberate PPTX slide boundaries, use one level-one Markdown heading per slide; unstructured text is still accepted",
|
|
4417
|
+
"convert_document chooses compatible templates automatically; Squisq annotations on headings are optional layout hints that take precedence",
|
|
4418
|
+
"use a bundle source when assets must travel with the document, or create_document_bundle when one draft will be reused by two or more inspect, preview, or convert calls",
|
|
4419
|
+
"use inspect_document or preview_document only when the user asks for document analysis or visual evidence",
|
|
4420
|
+
"convert_document creates immutable artifacts; save_artifact only when a durable file is required"
|
|
4643
4421
|
],
|
|
4644
4422
|
markdownAnnotation: '# Heading {[templateId key="value"]}',
|
|
4645
4423
|
standaloneAnnotation: '{[templateId key="value"]}',
|
|
@@ -4680,7 +4458,7 @@ function registerTemplateTools(server, context) {
|
|
|
4680
4458
|
server.registerTool(
|
|
4681
4459
|
"get_authoring_context",
|
|
4682
4460
|
{
|
|
4683
|
-
description: "
|
|
4461
|
+
description: "Optionally discover target capabilities, safe defaults, themes, transforms, and exact starter annotation examples. Plain Markdown can be converted without calling this tool.",
|
|
4684
4462
|
inputSchema: z.object({
|
|
4685
4463
|
targetFormat: formatInputSchema.optional(),
|
|
4686
4464
|
goal: z.enum(["content-first", "visual-polish"]).optional(),
|
|
@@ -4709,7 +4487,7 @@ function registerTemplateTools(server, context) {
|
|
|
4709
4487
|
import("@bendyline/squisq/transform")
|
|
4710
4488
|
]);
|
|
4711
4489
|
const annotationHandling = normalizedTarget ? await targetTemplateAnnotationHandling(normalizedTarget) : null;
|
|
4712
|
-
const
|
|
4490
|
+
const candidateTemplates = annotationHandling === "ignored" ? fullTemplates.filter((template) => template.safeForContentFirst) : fullTemplates;
|
|
4713
4491
|
let recommendations = [];
|
|
4714
4492
|
let totalBlocks = null;
|
|
4715
4493
|
let truncated = false;
|
|
@@ -4729,7 +4507,7 @@ function registerTemplateTools(server, context) {
|
|
|
4729
4507
|
const profile = recommendModule.profileBlockContents(block.contents ?? []);
|
|
4730
4508
|
const visual = recommendModule.recommendTemplatesForBlock(
|
|
4731
4509
|
profile,
|
|
4732
|
-
|
|
4510
|
+
candidateTemplates.map((template) => template.id)
|
|
4733
4511
|
).recommended;
|
|
4734
4512
|
const recommendedTemplateIds = goal === "content-first" ? ["content", ...visual.filter((id) => id !== "content")] : visual;
|
|
4735
4513
|
return {
|
|
@@ -4751,31 +4529,26 @@ function registerTemplateTools(server, context) {
|
|
|
4751
4529
|
goal,
|
|
4752
4530
|
targetFormat: normalizedTarget,
|
|
4753
4531
|
defaultTemplateId: "content",
|
|
4754
|
-
defaultFidelity: authoringDefaultFidelity(normalizedTarget
|
|
4532
|
+
defaultFidelity: authoringDefaultFidelity(normalizedTarget),
|
|
4755
4533
|
workflow: [
|
|
4756
|
-
"
|
|
4757
|
-
|
|
4758
|
-
"
|
|
4759
|
-
"
|
|
4760
|
-
"
|
|
4761
|
-
|
|
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.",
|
|
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.",
|
|
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.",
|
|
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.",
|
|
4768
|
-
"Use preview_document when visual evidence is needed, inspect previewBasis, and repair overflow or layout problems in the authoritative Markdown.",
|
|
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.",
|
|
4770
|
-
"Convert to immutable artifacts, inspect conversion reports, and save only final durable outputs."
|
|
4534
|
+
"For durable local output, call list_roots before drafting. If no returned root is write-enabled, stop and explain that the MCP server must restart with --allow-write; do not fall back to a shell or CLI converter. A transient artifact is acceptable only when the user did not require a file.",
|
|
4535
|
+
normalizedTarget === "pptx" ? "Plain text and ordinary Markdown convert directly. Use one level-one Markdown heading (#) for each deliberate slide boundary; unstructured text is still accepted." : "Plain text and ordinary Markdown convert directly without a preflight.",
|
|
4536
|
+
"convert_document chooses compatible templates automatically. Annotations on headings are optional layout hints that take precedence; use the returned exact starter examples when useful.",
|
|
4537
|
+
annotationHandling === "ignored" ? "This target flattens template annotations to semantic content, so annotations are unnecessary for the exported file." : "Explicit valid annotations override automatic layout choices; invalid or unnecessary annotations should be omitted rather than repaired through a separate workflow.",
|
|
4538
|
+
"Pass Markdown directly to convert_document. Use a bundle source for assets, or create_document_bundle only when one draft will be reused by two or more inspect, preview, or convert calls.",
|
|
4539
|
+
"Use inspect_document or preview_document only when the user asks for document analysis or visual evidence. Save only final durable artifacts with save_artifact."
|
|
4771
4540
|
],
|
|
4772
4541
|
syntax: {
|
|
4773
4542
|
headingAnnotation: "# Heading {[content]}",
|
|
4774
4543
|
standaloneAnnotation: "{[content]}",
|
|
4775
4544
|
standaloneWarning: "A standalone annotation creates an additional heading-less block. Bind it to a heading unless that extra block is deliberate."
|
|
4776
4545
|
},
|
|
4777
|
-
formats,
|
|
4778
|
-
templates
|
|
4546
|
+
formats: normalizedTarget ? formats.filter((format) => format.id === normalizedTarget) : formats,
|
|
4547
|
+
templates: focusedAuthoringTemplates(
|
|
4548
|
+
candidateTemplates,
|
|
4549
|
+
recommendations,
|
|
4550
|
+
normalizedTarget
|
|
4551
|
+
),
|
|
4779
4552
|
themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((theme) => ({
|
|
4780
4553
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
4781
4554
|
name: boundWireText(theme.name, MCP_WIRE_LIMITS7.labelCharacters, theme.id),
|
|
@@ -4790,7 +4563,19 @@ function registerTemplateTools(server, context) {
|
|
|
4790
4563
|
totalBlocks,
|
|
4791
4564
|
truncated
|
|
4792
4565
|
};
|
|
4793
|
-
return
|
|
4566
|
+
return {
|
|
4567
|
+
content: [
|
|
4568
|
+
{ type: "text", text: compactAuthoringContextText(payload) },
|
|
4569
|
+
{
|
|
4570
|
+
type: "resource_link",
|
|
4571
|
+
uri: "docblocks://authoring-guide",
|
|
4572
|
+
name: "authoring-guide",
|
|
4573
|
+
description: "Complete linked-Squisq authoring catalog; read only if needed.",
|
|
4574
|
+
mimeType: "application/json"
|
|
4575
|
+
}
|
|
4576
|
+
],
|
|
4577
|
+
structuredContent: asStructuredContent(successResult(payload))
|
|
4578
|
+
};
|
|
4794
4579
|
});
|
|
4795
4580
|
} catch (caught) {
|
|
4796
4581
|
return errorResult(caught, "inspect", null, extra.signal);
|
|
@@ -4832,7 +4617,7 @@ function registerTemplateTools(server, context) {
|
|
|
4832
4617
|
(template2) => template2.id === templateId
|
|
4833
4618
|
);
|
|
4834
4619
|
if (!described) {
|
|
4835
|
-
return errorResult(new Error(`Unknown template "${templateId}"`), "
|
|
4620
|
+
return errorResult(new Error(`Unknown template "${templateId}"`), "inspect");
|
|
4836
4621
|
}
|
|
4837
4622
|
const template = {
|
|
4838
4623
|
id: described.id,
|
|
@@ -4929,7 +4714,7 @@ function registerThemeTools(server, context) {
|
|
|
4929
4714
|
const documentTheme = prepared?.doc.customThemes?.find((theme2) => theme2.id === themeId);
|
|
4930
4715
|
const builtIn = Object.prototype.hasOwnProperty.call(schemaModule.THEMES, themeId);
|
|
4931
4716
|
if (!documentTheme && !builtIn) {
|
|
4932
|
-
return errorResult(new Error(`Unknown theme "${themeId}"`), "
|
|
4717
|
+
return errorResult(new Error(`Unknown theme "${themeId}"`), "inspect");
|
|
4933
4718
|
}
|
|
4934
4719
|
const theme = docModule.resolveThemeForDoc(prepared?.doc, themeId);
|
|
4935
4720
|
return textAndStructured({
|
|
@@ -5206,20 +4991,45 @@ function textAndStructured(payload, text = JSON.stringify(payload, null, 2)) {
|
|
|
5206
4991
|
structuredContent: asStructuredContent(successResult(payload))
|
|
5207
4992
|
};
|
|
5208
4993
|
}
|
|
4994
|
+
function rootDiscoveryText(roots) {
|
|
4995
|
+
if (roots.length === 0) {
|
|
4996
|
+
return "No DocBlocks MCP roots are configured. Durable file output is unavailable: restart the server with --allow-write <directory>. Do not fall back to a shell or CLI converter; keep a transient artifact only when the user did not require a file.";
|
|
4997
|
+
}
|
|
4998
|
+
const catalog = JSON.stringify({ roots }, null, 2);
|
|
4999
|
+
if (roots.some((root) => root.write)) return catalog;
|
|
5000
|
+
return `${catalog}
|
|
5001
|
+
|
|
5002
|
+
No returned root is write-enabled. Durable file output is unavailable: restart the server with --allow-write <directory>. Do not fall back to a shell or CLI converter.`;
|
|
5003
|
+
}
|
|
5004
|
+
function focusedAuthoringTemplates(candidates, recommendations, targetFormat) {
|
|
5005
|
+
const selectedIds = /* @__PURE__ */ new Set(["content"]);
|
|
5006
|
+
if (targetFormat === "pptx") {
|
|
5007
|
+
for (const templateId of ["title", "sectionHeader", "statHighlight", "quote"]) {
|
|
5008
|
+
selectedIds.add(templateId);
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
for (const recommendation of recommendations) {
|
|
5012
|
+
for (const templateId of recommendation.recommendedTemplateIds) {
|
|
5013
|
+
selectedIds.add(templateId);
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
return candidates.filter((template) => selectedIds.has(template.id));
|
|
5017
|
+
}
|
|
5209
5018
|
function compactAuthoringContextText(context) {
|
|
5210
5019
|
const target = context.targetFormat ?? "unspecified target";
|
|
5211
5020
|
const lines = [
|
|
5212
5021
|
`DocBlocks authoring contract: ${target}, ${context.goal}.`,
|
|
5213
5022
|
`Default template: ${context.defaultTemplateId}. Default fidelity: ${context.defaultFidelity ?? "select for target"}.`,
|
|
5214
5023
|
"",
|
|
5215
|
-
"
|
|
5024
|
+
"Optional guidance:",
|
|
5216
5025
|
...context.workflow.map((step, index) => `${index + 1}. ${step}`),
|
|
5217
5026
|
"",
|
|
5218
5027
|
`Heading annotation: ${context.syntax.headingAnnotation}`,
|
|
5219
5028
|
`Warning: ${context.syntax.standaloneWarning}`,
|
|
5220
5029
|
`Templates (${context.templates.length}): ${context.templates.map(({ id }) => id).join(", ")}`,
|
|
5030
|
+
...context.templates.filter(({ id }) => id !== "content").map(({ annotationExample }) => `Optional example: ${annotationExample}`),
|
|
5221
5031
|
`Themes (${context.themes.length}): ${context.themes.map(({ id }) => id).join(", ")}`,
|
|
5222
|
-
`Transform styles (${context.transformStyles.length}): ${context.transformStyles.map(({ id }) => id).join(", ")}
|
|
5032
|
+
`Transform styles (${context.transformStyles.length}): ${context.transformStyles.map(({ id }) => id).join(", ")}.`
|
|
5223
5033
|
];
|
|
5224
5034
|
if (context.recommendations.length > 0) {
|
|
5225
5035
|
lines.push(
|
|
@@ -5230,7 +5040,7 @@ function compactAuthoringContextText(context) {
|
|
|
5230
5040
|
}
|
|
5231
5041
|
lines.push(
|
|
5232
5042
|
"",
|
|
5233
|
-
"
|
|
5043
|
+
"This response is intentionally focused. Use recommend_templates on a complete draft, describe_template for exact selected inputs, or read docblocks://authoring-guide only when the full catalog is required."
|
|
5234
5044
|
);
|
|
5235
5045
|
return lines.join("\n");
|
|
5236
5046
|
}
|
|
@@ -5307,12 +5117,9 @@ async function targetTemplateAnnotationHandling(targetFormat) {
|
|
|
5307
5117
|
}
|
|
5308
5118
|
return null;
|
|
5309
5119
|
}
|
|
5310
|
-
function authoringDefaultFidelity(targetFormat
|
|
5120
|
+
function authoringDefaultFidelity(targetFormat) {
|
|
5311
5121
|
if (!targetFormat) return null;
|
|
5312
5122
|
if (targetFormat === "mp4" || targetFormat === "gif") return "rendered-fidelity";
|
|
5313
|
-
if (goal === "visual-polish" && (targetFormat === "pptx" || targetFormat === "pdf")) {
|
|
5314
|
-
return "rendered-fidelity";
|
|
5315
|
-
}
|
|
5316
5123
|
if (targetFormat === "docx" || targetFormat === "pptx" || targetFormat === "xlsx") {
|
|
5317
5124
|
return "editable-native";
|
|
5318
5125
|
}
|
|
@@ -5638,7 +5445,7 @@ function registerAuthoringPrompts(server) {
|
|
|
5638
5445
|
const presentation = server.registerPrompt(
|
|
5639
5446
|
"create-presentation",
|
|
5640
5447
|
{
|
|
5641
|
-
description: "
|
|
5448
|
+
description: "Convert model-authored Markdown into an artifact-first presentation.",
|
|
5642
5449
|
argsSchema: presentationArgs.shape
|
|
5643
5450
|
},
|
|
5644
5451
|
async (args) => ({
|
|
@@ -5663,7 +5470,7 @@ function registerAuthoringPrompts(server) {
|
|
|
5663
5470
|
const video = server.registerPrompt(
|
|
5664
5471
|
"create-video",
|
|
5665
5472
|
{
|
|
5666
|
-
description: "
|
|
5473
|
+
description: "Convert model-authored Markdown into an artifact-first MP4 video.",
|
|
5667
5474
|
argsSchema: videoArgs.shape
|
|
5668
5475
|
},
|
|
5669
5476
|
async (args) => ({
|
|
@@ -5688,7 +5495,7 @@ function registerAuthoringPrompts(server) {
|
|
|
5688
5495
|
const document2 = server.registerPrompt(
|
|
5689
5496
|
"create-document",
|
|
5690
5497
|
{
|
|
5691
|
-
description: "
|
|
5498
|
+
description: "Convert model-authored Markdown into an artifact-first document.",
|
|
5692
5499
|
argsSchema: documentArgs.shape
|
|
5693
5500
|
},
|
|
5694
5501
|
async (args) => ({
|
|
@@ -5706,36 +5513,39 @@ function registerAuthoringPrompts(server) {
|
|
|
5706
5513
|
document2.argsSchema = documentArgs;
|
|
5707
5514
|
}
|
|
5708
5515
|
function presentationPrompt(topic, style, theme, template) {
|
|
5516
|
+
const templateHint = template ? ` Use the requested \`${template}\` annotation where appropriate.` : "";
|
|
5517
|
+
const themeHint = theme ? ` with themeId \`${theme}\`` : "";
|
|
5518
|
+
const styleHint = style ? ` and transformId \`${style}\`` : "";
|
|
5709
5519
|
return `Create a presentation about: ${topic}
|
|
5710
5520
|
|
|
5711
|
-
1.
|
|
5712
|
-
2.
|
|
5713
|
-
3.
|
|
5714
|
-
4.
|
|
5715
|
-
5.
|
|
5716
|
-
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.
|
|
5717
|
-
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.
|
|
5718
|
-
8. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5521
|
+
1. For durable output, call list_roots before drafting. If no returned root is write-enabled, stop and explain that the MCP server must restart with --allow-write; do not use a shell or CLI converter.
|
|
5522
|
+
2. Author plain Markdown. One level-one heading (\`#\`) creates each deliberate slide boundary; unstructured text is also accepted. Squisq annotations are optional layout hints.${templateHint} Call get_authoring_context only when exact annotation examples or theme details would help.
|
|
5523
|
+
3. Pass the Markdown directly to convert_document with a pptx target${themeHint}${styleHint}. Use a bundle source only when assets must travel with the document. No validation, inspection, or preview is required.
|
|
5524
|
+
4. Use inspect_document or preview_document only when the user explicitly asks for document analysis or visual evidence.
|
|
5525
|
+
5. Call save_artifact only when a durable file is required.`;
|
|
5719
5526
|
}
|
|
5720
5527
|
function videoPrompt(topic, orientation, theme, template) {
|
|
5528
|
+
const templateHint = template ? ` Use the requested \`${template}\` annotation where appropriate.` : "";
|
|
5529
|
+
const themeHint = theme ? ` and themeId \`${theme}\`` : "";
|
|
5721
5530
|
return `Create a video about: ${topic}
|
|
5722
5531
|
|
|
5723
|
-
1.
|
|
5724
|
-
2. Author
|
|
5725
|
-
3.
|
|
5726
|
-
4.
|
|
5727
|
-
5.
|
|
5532
|
+
1. For durable output, call list_roots before drafting and stop if no root is write-enabled; do not use a shell or CLI converter.
|
|
5533
|
+
2. Author plain Markdown. Squisq annotations are optional layout hints.${templateHint} Call get_authoring_context only when exact annotation examples or theme details would help.
|
|
5534
|
+
3. Pass the Markdown directly to convert_document with an mp4 target, orientation "${orientation ?? "landscape"}"${themeHint}. Use a bundle source only when assets must travel with the document. No validation, inspection, or preview is required.
|
|
5535
|
+
4. Use preview_document only when the user explicitly asks for visual evidence.
|
|
5536
|
+
5. Call save_artifact only when a durable file is required.`;
|
|
5728
5537
|
}
|
|
5729
5538
|
function documentPrompt(topic, format, theme, template) {
|
|
5730
5539
|
const target = format ?? "pdf";
|
|
5731
|
-
|
|
5540
|
+
const templateHint = template ? ` Use the requested \`${template}\` annotation where appropriate.` : "";
|
|
5541
|
+
const themeHint = theme ? ` and themeId \`${theme}\`` : "";
|
|
5542
|
+
return `Create a document about: ${topic}
|
|
5732
5543
|
|
|
5733
|
-
1.
|
|
5734
|
-
2.
|
|
5735
|
-
3.
|
|
5736
|
-
4.
|
|
5737
|
-
5. Call
|
|
5738
|
-
6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5544
|
+
1. For durable output, call list_roots before drafting and stop if no root is write-enabled; do not use a shell or CLI converter.
|
|
5545
|
+
2. Author plain Markdown. Squisq annotations are optional layout hints.${templateHint} Call get_authoring_context only when exact annotation examples or theme details would help.
|
|
5546
|
+
3. Pass the Markdown directly to convert_document with a ${target} target${themeHint}. Use a bundle source only when assets must travel with the document. No validation, inspection, or preview is required.
|
|
5547
|
+
4. Use inspect_document or preview_document only when the user explicitly asks for document analysis or visual evidence.
|
|
5548
|
+
5. Call save_artifact only when a durable file is required.`;
|
|
5739
5549
|
}
|
|
5740
5550
|
function complete(values, prefix) {
|
|
5741
5551
|
const value = prefix ?? "";
|
|
@@ -5777,7 +5587,14 @@ function createMcpServer(options = {}) {
|
|
|
5777
5587
|
const server = new McpServer(
|
|
5778
5588
|
{ name: "docblocks", version: getPackageVersion() },
|
|
5779
5589
|
{
|
|
5780
|
-
instructions:
|
|
5590
|
+
instructions: [
|
|
5591
|
+
"For durable local output, call list_roots before drafting and use a returned write-enabled root id exactly as given. If none is writable, stop and explain that the server must restart with --allow-write; do not fall back to a shell or CLI converter.",
|
|
5592
|
+
"Pass plain text or ordinary Markdown directly to convert_document; no preflight, inspection, preview, or template annotation is required. For deliberate PPTX slide boundaries, use one level-one heading per slide; unstructured text is still accepted.",
|
|
5593
|
+
"convert_document chooses compatible templates automatically. Squisq annotations on headings are optional layout hints that take precedence. Call get_authoring_context only when exact starter examples, themes, transforms, or target details are useful; read the full authoring-guide or call describe_template only for advanced author control.",
|
|
5594
|
+
"Use a bundle source when assets must travel with the document. Use create_document_bundle only when one complete draft will be reused by two or more inspect, preview, or convert calls.",
|
|
5595
|
+
"Use inspect_document or preview_document only when the user asks for document analysis or visual evidence.",
|
|
5596
|
+
"convert_document returns immutable artifacts and diagnostics. Save only final durable output with save_artifact; never invent root ids or switch conversion to a shell or CLI."
|
|
5597
|
+
].join(" ")
|
|
5781
5598
|
}
|
|
5782
5599
|
);
|
|
5783
5600
|
registerAgenticTools(server, {
|