@bendyline/docblocks-cli 2.1.2 → 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 +194 -360
- package/dist/eval/cli.d.ts +2 -0
- package/dist/eval/cli.js +1604 -0
- package/package.json +8 -8
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;
|
|
@@ -4161,258 +4225,13 @@ var init_progress = __esm({
|
|
|
4161
4225
|
}
|
|
4162
4226
|
});
|
|
4163
4227
|
|
|
4164
|
-
// src/mcp/revision-service.ts
|
|
4165
|
-
import { createHash as createHash7 } from "crypto";
|
|
4166
|
-
import {
|
|
4167
|
-
DOCBLOCKS_MCP_WIRE_VERSION as DOCBLOCKS_MCP_WIRE_VERSION5,
|
|
4168
|
-
MCP_WIRE_LIMITS as MCP_WIRE_LIMITS7,
|
|
4169
|
-
parseDocumentRevisionResult
|
|
4170
|
-
} from "@bendyline/docblocks/mcp";
|
|
4171
|
-
async function reviseDocumentArtifact(artifacts, documents, request, signal) {
|
|
4172
|
-
throwIfAborted5(signal);
|
|
4173
|
-
const parentArtifact = await artifacts.get(request.artifactUri, signal);
|
|
4174
|
-
if (parentArtifact.sha256 !== request.expectedSha256) {
|
|
4175
|
-
throw revisionError(
|
|
4176
|
-
"conflict",
|
|
4177
|
-
`Revision precondition failed: expected ${request.expectedSha256}, but the parent artifact is ${parentArtifact.sha256}.`,
|
|
4178
|
-
`Use expectedSha256 "${parentArtifact.sha256}" only if this is still the intended parent artifact.`
|
|
4179
|
-
);
|
|
4180
|
-
}
|
|
4181
|
-
if (parentArtifact.format !== "dbk") {
|
|
4182
|
-
throw revisionError(
|
|
4183
|
-
"invalid-revision-source",
|
|
4184
|
-
`Artifact-native block revision requires a DBK parent, not ${parentArtifact.format}.`,
|
|
4185
|
-
"Stage Markdown and assets with create_document_bundle, or convert the source to DBK first."
|
|
4186
|
-
);
|
|
4187
|
-
}
|
|
4188
|
-
if (new Set(request.edits.map((edit) => edit.blockId)).size !== request.edits.length) {
|
|
4189
|
-
throw revisionError(
|
|
4190
|
-
"duplicate-block-edit",
|
|
4191
|
-
"Each block id may be revised at most once per request.",
|
|
4192
|
-
"Combine changes to the same block into one replacement fragment."
|
|
4193
|
-
);
|
|
4194
|
-
}
|
|
4195
|
-
const totalReplacementCharacters = request.edits.reduce(
|
|
4196
|
-
(sum, edit) => sum + edit.markdown.length,
|
|
4197
|
-
0
|
|
4198
|
-
);
|
|
4199
|
-
if (totalReplacementCharacters > MCP_WIRE_LIMITS7.documentCharacters) {
|
|
4200
|
-
throw revisionError(
|
|
4201
|
-
"revision-too-large",
|
|
4202
|
-
"Combined replacement Markdown exceeds the MCP document character limit.",
|
|
4203
|
-
"Use fewer or smaller block replacements."
|
|
4204
|
-
);
|
|
4205
|
-
}
|
|
4206
|
-
const prepared = await documents.prepare({ kind: "artifact", uri: parentArtifact.uri }, signal);
|
|
4207
|
-
const { flattenBlocks } = await import("@bendyline/squisq/doc");
|
|
4208
|
-
const blocks = flattenBlocks(prepared.doc.blocks);
|
|
4209
|
-
const ranges = await computeBlockSourceRanges(blocks, prepared.markdown.length, signal);
|
|
4210
|
-
const blockRecords = indexEditableBlocks(blocks, ranges);
|
|
4211
|
-
const replacements = [];
|
|
4212
|
-
for (let index = 0; index < request.edits.length; index += 1) {
|
|
4213
|
-
throwIfAborted5(signal);
|
|
4214
|
-
const edit = request.edits[index];
|
|
4215
|
-
const record = blockRecords.get(edit.blockId);
|
|
4216
|
-
if (!record) {
|
|
4217
|
-
throw revisionError(
|
|
4218
|
-
"block-not-found",
|
|
4219
|
-
`Block "${edit.blockId}" is not an editable heading block in the parent artifact.`,
|
|
4220
|
-
"Call inspect_document on the exact parent artifact and use a returned block id with a non-null sourceRange."
|
|
4221
|
-
);
|
|
4222
|
-
}
|
|
4223
|
-
const replacementMarkdown = await canonicalReplacement(
|
|
4224
|
-
edit.markdown,
|
|
4225
|
-
record.block,
|
|
4226
|
-
edit.blockId
|
|
4227
|
-
);
|
|
4228
|
-
const before = prepared.markdown.slice(record.start, record.end);
|
|
4229
|
-
if (replacementMarkdown === before) {
|
|
4230
|
-
throw revisionError(
|
|
4231
|
-
"no-op-block-edit",
|
|
4232
|
-
`Replacement for block "${edit.blockId}" does not change its heading or body.`,
|
|
4233
|
-
"Omit no-op block edits from the revision request."
|
|
4234
|
-
);
|
|
4235
|
-
}
|
|
4236
|
-
replacements.push({
|
|
4237
|
-
blockId: edit.blockId,
|
|
4238
|
-
start: record.start,
|
|
4239
|
-
end: record.end,
|
|
4240
|
-
markdown: replacementMarkdown,
|
|
4241
|
-
applied: {
|
|
4242
|
-
kind: "replace_block",
|
|
4243
|
-
blockId: edit.blockId,
|
|
4244
|
-
beforeSha256: sha256(Buffer.from(before, "utf8")),
|
|
4245
|
-
afterSha256: sha256(Buffer.from(replacementMarkdown, "utf8"))
|
|
4246
|
-
}
|
|
4247
|
-
});
|
|
4248
|
-
if ((index + 1) % 16 === 0) await yieldForCancellation2(signal);
|
|
4249
|
-
}
|
|
4250
|
-
const revisedMarkdown = applyReplacements(prepared.markdown, replacements);
|
|
4251
|
-
if (revisedMarkdown.length > MCP_WIRE_LIMITS7.documentCharacters) {
|
|
4252
|
-
throw revisionError(
|
|
4253
|
-
"revision-too-large",
|
|
4254
|
-
"The revised Markdown exceeds the MCP document character limit.",
|
|
4255
|
-
"Use smaller block replacements or split the document before revising it."
|
|
4256
|
-
);
|
|
4257
|
-
}
|
|
4258
|
-
if (revisedMarkdown === prepared.markdown) {
|
|
4259
|
-
throw revisionError(
|
|
4260
|
-
"no-op-revision",
|
|
4261
|
-
"The requested block replacements do not change the document.",
|
|
4262
|
-
"Submit only replacements that change heading or body Markdown."
|
|
4263
|
-
);
|
|
4264
|
-
}
|
|
4265
|
-
const [{ parseMarkdown: parseMarkdown2 }, { markdownToDoc: markdownToDoc2, flattenBlocks: flattenRevisedBlocks }] = await Promise.all([import("@bendyline/squisq/markdown"), import("@bendyline/squisq/doc")]);
|
|
4266
|
-
const markdownDoc = parseMarkdown2(revisedMarkdown);
|
|
4267
|
-
const doc = markdownToDoc2(markdownDoc, { defaultTemplate: "content", autoTemplates: false });
|
|
4268
|
-
const revisedIds = new Set(
|
|
4269
|
-
flattenRevisedBlocks(doc.blocks).map((block) => toWireIdentifier(block.id, "block"))
|
|
4270
|
-
);
|
|
4271
|
-
for (const replacement of replacements) {
|
|
4272
|
-
if (!revisedIds.has(replacement.blockId)) {
|
|
4273
|
-
throw revisionError(
|
|
4274
|
-
"unstable-block-id",
|
|
4275
|
-
`Replacement for block "${replacement.blockId}" did not preserve its stable id.`,
|
|
4276
|
-
"Keep the replacement heading id unchanged and retry from the parent artifact."
|
|
4277
|
-
);
|
|
4278
|
-
}
|
|
4279
|
-
}
|
|
4280
|
-
throwIfAborted5(signal);
|
|
4281
|
-
await prepared.container.writeDocument(revisedMarkdown);
|
|
4282
|
-
const revisionSourceSha256 = createHash7("sha256").update("docblocks-artifact-revision-v1\0", "utf8").update(parentArtifact.sha256, "utf8").update("\0", "utf8").update(revisedMarkdown, "utf8").digest("hex");
|
|
4283
|
-
const [conversion] = await convertPreparedDocument(
|
|
4284
|
-
artifacts,
|
|
4285
|
-
{
|
|
4286
|
-
...prepared,
|
|
4287
|
-
markdown: revisedMarkdown,
|
|
4288
|
-
markdownDoc,
|
|
4289
|
-
doc,
|
|
4290
|
-
sourceSha256: revisionSourceSha256
|
|
4291
|
-
},
|
|
4292
|
-
{ targets: [{ format: "dbk", fidelity: "semantic" }] },
|
|
4293
|
-
signal
|
|
4294
|
-
);
|
|
4295
|
-
if (!conversion) throw new Error("Document revision produced no DBK artifact");
|
|
4296
|
-
const result = {
|
|
4297
|
-
version: DOCBLOCKS_MCP_WIRE_VERSION5,
|
|
4298
|
-
kind: "revision",
|
|
4299
|
-
parentArtifact,
|
|
4300
|
-
artifact: conversion.artifact,
|
|
4301
|
-
edits: replacements.map((replacement) => replacement.applied),
|
|
4302
|
-
diagnostics: conversion.diagnostics
|
|
4303
|
-
};
|
|
4304
|
-
const parsed = parseDocumentRevisionResult(result);
|
|
4305
|
-
if (!parsed) throw new Error("Document revision produced an invalid MCP result");
|
|
4306
|
-
return parsed;
|
|
4307
|
-
}
|
|
4308
|
-
function indexEditableBlocks(blocks, ranges) {
|
|
4309
|
-
const indexed = /* @__PURE__ */ new Map();
|
|
4310
|
-
const ambiguous = /* @__PURE__ */ new Set();
|
|
4311
|
-
for (let index = 0; index < blocks.length; index += 1) {
|
|
4312
|
-
const block = blocks[index];
|
|
4313
|
-
const range = ranges[index];
|
|
4314
|
-
if (!block || !range || !block.sourceHeading) continue;
|
|
4315
|
-
const wireId = toWireIdentifier(block.id, "block");
|
|
4316
|
-
if (indexed.has(wireId)) {
|
|
4317
|
-
indexed.delete(wireId);
|
|
4318
|
-
ambiguous.add(wireId);
|
|
4319
|
-
} else if (!ambiguous.has(wireId)) {
|
|
4320
|
-
indexed.set(wireId, { block, start: range.start, end: range.end });
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
return indexed;
|
|
4324
|
-
}
|
|
4325
|
-
async function canonicalReplacement(markdown, target, wireBlockId) {
|
|
4326
|
-
const { parseMarkdown: parseMarkdown2, stringifyMarkdown } = await import("@bendyline/squisq/markdown");
|
|
4327
|
-
const fragment = parseMarkdown2(markdown);
|
|
4328
|
-
if (fragment.frontmatter && Object.keys(fragment.frontmatter).length > 0) {
|
|
4329
|
-
throw revisionError(
|
|
4330
|
-
"invalid-block-replacement",
|
|
4331
|
-
`Replacement for block "${wireBlockId}" cannot contain frontmatter.`,
|
|
4332
|
-
"Provide one heading and its direct body only."
|
|
4333
|
-
);
|
|
4334
|
-
}
|
|
4335
|
-
const headings = fragment.children.filter(
|
|
4336
|
-
(node) => node.type === "heading"
|
|
4337
|
-
);
|
|
4338
|
-
const heading = headings[0];
|
|
4339
|
-
if (!heading || fragment.children[0] !== heading || headings.length !== 1) {
|
|
4340
|
-
throw revisionError(
|
|
4341
|
-
"invalid-block-replacement",
|
|
4342
|
-
`Replacement for block "${wireBlockId}" must begin with exactly one heading and cannot contain nested headings.`,
|
|
4343
|
-
"Use a complete fragment such as `# Heading {[content]}\n\nReplacement body`."
|
|
4344
|
-
);
|
|
4345
|
-
}
|
|
4346
|
-
const sourceHeading = target.sourceHeading;
|
|
4347
|
-
if (!sourceHeading) throw new Error("Editable replacement target has no source heading");
|
|
4348
|
-
if (heading.depth !== sourceHeading.depth) {
|
|
4349
|
-
throw revisionError(
|
|
4350
|
-
"invalid-block-replacement",
|
|
4351
|
-
`Replacement for block "${wireBlockId}" must keep heading depth ${sourceHeading.depth}.`,
|
|
4352
|
-
`Begin the replacement with ${"#".repeat(sourceHeading.depth)} followed by the heading text.`
|
|
4353
|
-
);
|
|
4354
|
-
}
|
|
4355
|
-
if (heading.attributes?.id && heading.attributes.id !== target.id) {
|
|
4356
|
-
throw revisionError(
|
|
4357
|
-
"block-id-change-not-allowed",
|
|
4358
|
-
`Replacement for block "${wireBlockId}" cannot change its id to "${heading.attributes.id}".`,
|
|
4359
|
-
`Omit the explicit id or keep {#${target.id}}.`
|
|
4360
|
-
);
|
|
4361
|
-
}
|
|
4362
|
-
const sourceAttributes = sourceHeading.attributes;
|
|
4363
|
-
const replacementAttributes = heading.attributes;
|
|
4364
|
-
const attributes = {
|
|
4365
|
-
...sourceAttributes,
|
|
4366
|
-
...replacementAttributes,
|
|
4367
|
-
id: target.id,
|
|
4368
|
-
...replacementAttributes?.classes === void 0 && sourceAttributes?.classes ? { classes: sourceAttributes.classes } : {},
|
|
4369
|
-
...replacementAttributes?.params === void 0 && sourceAttributes?.params ? { params: sourceAttributes.params } : {},
|
|
4370
|
-
...replacementAttributes?.blockMeta === void 0 && sourceAttributes?.blockMeta ? { blockMeta: sourceAttributes.blockMeta } : {},
|
|
4371
|
-
...replacementAttributes?.metadata === void 0 && sourceAttributes?.metadata ? { metadata: sourceAttributes.metadata } : {}
|
|
4372
|
-
};
|
|
4373
|
-
const pinnedHeading = {
|
|
4374
|
-
...heading,
|
|
4375
|
-
attributes,
|
|
4376
|
-
templateAnnotation: heading.templateAnnotation ?? sourceHeading.templateAnnotation
|
|
4377
|
-
};
|
|
4378
|
-
return stringifyMarkdown({
|
|
4379
|
-
type: "document",
|
|
4380
|
-
children: [pinnedHeading, ...fragment.children.slice(1)]
|
|
4381
|
-
});
|
|
4382
|
-
}
|
|
4383
|
-
function applyReplacements(source, replacements) {
|
|
4384
|
-
let revised = source;
|
|
4385
|
-
const descending = [...replacements].sort((left, right) => right.start - left.start);
|
|
4386
|
-
for (const replacement of descending) {
|
|
4387
|
-
revised = revised.slice(0, replacement.start) + replacement.markdown + revised.slice(replacement.end);
|
|
4388
|
-
}
|
|
4389
|
-
return revised;
|
|
4390
|
-
}
|
|
4391
|
-
function revisionError(code, message, hint) {
|
|
4392
|
-
return Object.assign(new Error(message), { code, hint, retryable: false });
|
|
4393
|
-
}
|
|
4394
|
-
async function yieldForCancellation2(signal) {
|
|
4395
|
-
await new Promise((resolve4) => setImmediate(resolve4));
|
|
4396
|
-
throwIfAborted5(signal);
|
|
4397
|
-
}
|
|
4398
|
-
var init_revision_service = __esm({
|
|
4399
|
-
"src/mcp/revision-service.ts"() {
|
|
4400
|
-
"use strict";
|
|
4401
|
-
init_conversion_service();
|
|
4402
|
-
init_document_service();
|
|
4403
|
-
init_intelligence();
|
|
4404
|
-
init_output_bounds();
|
|
4405
|
-
}
|
|
4406
|
-
});
|
|
4407
|
-
|
|
4408
4228
|
// src/mcp/agentic-tools.ts
|
|
4409
4229
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4410
4230
|
import { z } from "zod";
|
|
4411
4231
|
import {
|
|
4412
|
-
MCP_WIRE_LIMITS as
|
|
4232
|
+
MCP_WIRE_LIMITS as MCP_WIRE_LIMITS7,
|
|
4413
4233
|
parseComparisonResult,
|
|
4414
4234
|
parseConversionResult as parseConversionResult2,
|
|
4415
|
-
parseDocumentRevisionResult as parseDocumentRevisionResult2,
|
|
4416
4235
|
parseDocumentSource,
|
|
4417
4236
|
parseInspectionResult,
|
|
4418
4237
|
parseMaterializationOptions,
|
|
@@ -4424,7 +4243,6 @@ import {
|
|
|
4424
4243
|
bundleDocumentSourceSchema,
|
|
4425
4244
|
DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS,
|
|
4426
4245
|
documentSourceSchema,
|
|
4427
|
-
documentRevisionRequestSchema,
|
|
4428
4246
|
formatInputSchema,
|
|
4429
4247
|
materializationOptionsSchema
|
|
4430
4248
|
} from "@bendyline/docblocks/mcp/zod";
|
|
@@ -4520,7 +4338,7 @@ function registerAgenticTools(server, context) {
|
|
|
4520
4338
|
server.registerTool(
|
|
4521
4339
|
"create_document_bundle",
|
|
4522
4340
|
{
|
|
4523
|
-
description: "
|
|
4341
|
+
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.",
|
|
4524
4342
|
inputSchema: z.object({ source: bundleDocumentSourceSchema }).strict(),
|
|
4525
4343
|
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.create_document_bundle,
|
|
4526
4344
|
annotations: ARTIFACT_CREATING
|
|
@@ -4552,38 +4370,6 @@ function registerAgenticTools(server, context) {
|
|
|
4552
4370
|
}
|
|
4553
4371
|
}
|
|
4554
4372
|
);
|
|
4555
|
-
server.registerTool(
|
|
4556
|
-
"revise_document",
|
|
4557
|
-
{
|
|
4558
|
-
description: "Replace selected heading blocks in an immutable DBK artifact without resending the full document. Requires the parent artifact SHA-256, preserves block ids and child blocks, and returns a new DBK artifact without mutating the parent.",
|
|
4559
|
-
inputSchema: documentRevisionRequestSchema,
|
|
4560
|
-
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.revise_document,
|
|
4561
|
-
annotations: ARTIFACT_CREATING
|
|
4562
|
-
},
|
|
4563
|
-
async (request, extra) => {
|
|
4564
|
-
try {
|
|
4565
|
-
return await context.runOperation(extra.signal, async (operationSignal) => {
|
|
4566
|
-
const documents = new DocumentService(await context.authority, context.artifacts);
|
|
4567
|
-
const result = await reviseDocumentArtifact(
|
|
4568
|
-
context.artifacts,
|
|
4569
|
-
documents,
|
|
4570
|
-
request,
|
|
4571
|
-
operationSignal
|
|
4572
|
-
);
|
|
4573
|
-
requireWire(parseDocumentRevisionResult2(result), "document revision");
|
|
4574
|
-
return {
|
|
4575
|
-
content: [
|
|
4576
|
-
{ type: "text", text: JSON.stringify(result) },
|
|
4577
|
-
artifactLink(result.artifact)
|
|
4578
|
-
],
|
|
4579
|
-
structuredContent: asStructuredContent(successResult(result))
|
|
4580
|
-
};
|
|
4581
|
-
});
|
|
4582
|
-
} catch (caught) {
|
|
4583
|
-
return errorResult(caught, "transform", "dbk", extra.signal);
|
|
4584
|
-
}
|
|
4585
|
-
}
|
|
4586
|
-
);
|
|
4587
4373
|
server.registerTool(
|
|
4588
4374
|
"save_artifact",
|
|
4589
4375
|
{
|
|
@@ -4842,15 +4628,17 @@ function registerAuthoringGuideResource(server) {
|
|
|
4842
4628
|
import("@bendyline/squisq/transform")
|
|
4843
4629
|
]);
|
|
4844
4630
|
const guide = {
|
|
4845
|
-
version:
|
|
4631
|
+
version: 6,
|
|
4846
4632
|
workflow: [
|
|
4847
|
-
"
|
|
4848
|
-
"
|
|
4849
|
-
"
|
|
4850
|
-
"for
|
|
4851
|
-
"
|
|
4852
|
-
"
|
|
4853
|
-
"
|
|
4633
|
+
"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",
|
|
4634
|
+
"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",
|
|
4635
|
+
"author complete Squisq-compatible Markdown with annotations bound to headings; ordinary headings default to the loss-averse content template",
|
|
4636
|
+
"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",
|
|
4637
|
+
"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",
|
|
4638
|
+
"use create_document_bundle only when a reusable staged DBK materially avoids repeating a large Markdown-and-asset bundle across several operations",
|
|
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",
|
|
4854
4642
|
"save_artifact only when a durable file is required"
|
|
4855
4643
|
],
|
|
4856
4644
|
markdownAnnotation: '# Heading {[templateId key="value"]}',
|
|
@@ -4865,14 +4653,14 @@ function registerAuthoringGuideResource(server) {
|
|
|
4865
4653
|
fidelityByFormat: MCP_FORMAT_FIDELITIES,
|
|
4866
4654
|
formats: boundedFormatCapabilities(),
|
|
4867
4655
|
templates,
|
|
4868
|
-
themes: schemaModule.getThemeSummaries().slice(0,
|
|
4656
|
+
themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((theme) => ({
|
|
4869
4657
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
4870
|
-
name: boundWireText(theme.name,
|
|
4658
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS7.labelCharacters, theme.id),
|
|
4871
4659
|
description: boundWireText(theme.description ?? "")
|
|
4872
4660
|
})),
|
|
4873
|
-
transformStyles: transformModule.getTransformStyleSummaries().slice(0,
|
|
4661
|
+
transformStyles: transformModule.getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((style) => ({
|
|
4874
4662
|
id: requireWireIdentifier(style.id, "transform style id"),
|
|
4875
|
-
name: boundWireText(style.name,
|
|
4663
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS7.labelCharacters, style.id),
|
|
4876
4664
|
description: boundWireText(style.description)
|
|
4877
4665
|
}))
|
|
4878
4666
|
};
|
|
@@ -4892,7 +4680,7 @@ function registerTemplateTools(server, context) {
|
|
|
4892
4680
|
server.registerTool(
|
|
4893
4681
|
"get_authoring_context",
|
|
4894
4682
|
{
|
|
4895
|
-
description: "Get
|
|
4683
|
+
description: "Get a compact agent-facing authoring contract plus the complete linked-Squisq catalog in structured content, with optional block recommendations.",
|
|
4896
4684
|
inputSchema: z.object({
|
|
4897
4685
|
targetFormat: formatInputSchema.optional(),
|
|
4898
4686
|
goal: z.enum(["content-first", "visual-polish"]).optional(),
|
|
@@ -4915,11 +4703,13 @@ function registerTemplateTools(server, context) {
|
|
|
4915
4703
|
throw new Error(`Format "${normalizedTarget}" is not export-capable`);
|
|
4916
4704
|
}
|
|
4917
4705
|
}
|
|
4918
|
-
const [
|
|
4706
|
+
const [fullTemplates, schemaModule, transformModule] = await Promise.all([
|
|
4919
4707
|
authoringTemplateCatalog(),
|
|
4920
4708
|
import("@bendyline/squisq/schemas"),
|
|
4921
4709
|
import("@bendyline/squisq/transform")
|
|
4922
4710
|
]);
|
|
4711
|
+
const annotationHandling = normalizedTarget ? await targetTemplateAnnotationHandling(normalizedTarget) : null;
|
|
4712
|
+
const templates = annotationHandling === "ignored" ? fullTemplates.filter((template) => template.safeForContentFirst) : fullTemplates;
|
|
4923
4713
|
let recommendations = [];
|
|
4924
4714
|
let totalBlocks = null;
|
|
4925
4715
|
let truncated = false;
|
|
@@ -4944,7 +4734,7 @@ function registerTemplateTools(server, context) {
|
|
|
4944
4734
|
const recommendedTemplateIds = goal === "content-first" ? ["content", ...visual.filter((id) => id !== "content")] : visual;
|
|
4945
4735
|
return {
|
|
4946
4736
|
blockId: toWireIdentifier(block.id, "block"),
|
|
4947
|
-
title: boundNullableWireText(block.title,
|
|
4737
|
+
title: boundNullableWireText(block.title, MCP_WIRE_LIMITS7.labelCharacters),
|
|
4948
4738
|
profile: {
|
|
4949
4739
|
...profile,
|
|
4950
4740
|
hasAsciiDiagram: profile.hasAsciiDiagram ?? false,
|
|
@@ -4957,18 +4747,26 @@ function registerTemplateTools(server, context) {
|
|
|
4957
4747
|
totalBlocks = blocks.length;
|
|
4958
4748
|
truncated = recommendations.length < blocks.length;
|
|
4959
4749
|
}
|
|
4960
|
-
|
|
4750
|
+
const payload = {
|
|
4961
4751
|
goal,
|
|
4962
4752
|
targetFormat: normalizedTarget,
|
|
4963
4753
|
defaultTemplateId: "content",
|
|
4964
4754
|
defaultFidelity: authoringDefaultFidelity(normalizedTarget, goal),
|
|
4965
4755
|
workflow: [
|
|
4966
|
-
"
|
|
4967
|
-
"
|
|
4968
|
-
"
|
|
4969
|
-
"
|
|
4970
|
-
"
|
|
4971
|
-
"
|
|
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.",
|
|
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.",
|
|
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.",
|
|
4760
|
+
"Author complete Squisq-compatible content with heading-bound annotations; ordinary headings default to the loss-averse content template.",
|
|
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.",
|
|
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.",
|
|
4972
4770
|
"Convert to immutable artifacts, inspect conversion reports, and save only final durable outputs."
|
|
4973
4771
|
],
|
|
4974
4772
|
syntax: {
|
|
@@ -4978,20 +4776,21 @@ function registerTemplateTools(server, context) {
|
|
|
4978
4776
|
},
|
|
4979
4777
|
formats,
|
|
4980
4778
|
templates,
|
|
4981
|
-
themes: schemaModule.getThemeSummaries().slice(0,
|
|
4779
|
+
themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((theme) => ({
|
|
4982
4780
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
4983
|
-
name: boundWireText(theme.name,
|
|
4781
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS7.labelCharacters, theme.id),
|
|
4984
4782
|
description: boundWireText(theme.description ?? "")
|
|
4985
4783
|
})),
|
|
4986
|
-
transformStyles: transformModule.getTransformStyleSummaries().slice(0,
|
|
4784
|
+
transformStyles: transformModule.getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((style) => ({
|
|
4987
4785
|
id: requireWireIdentifier(style.id, "transform style id"),
|
|
4988
|
-
name: boundWireText(style.name,
|
|
4786
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS7.labelCharacters, style.id),
|
|
4989
4787
|
description: boundWireText(style.description)
|
|
4990
4788
|
})),
|
|
4991
4789
|
recommendations,
|
|
4992
4790
|
totalBlocks,
|
|
4993
4791
|
truncated
|
|
4994
|
-
}
|
|
4792
|
+
};
|
|
4793
|
+
return textAndStructured(payload, compactAuthoringContextText(payload));
|
|
4995
4794
|
});
|
|
4996
4795
|
} catch (caught) {
|
|
4997
4796
|
return errorResult(caught, "inspect", null, extra.signal);
|
|
@@ -5081,7 +4880,7 @@ function registerTemplateTools(server, context) {
|
|
|
5081
4880
|
const profile = recommendModule.profileBlockContents(block.contents ?? []);
|
|
5082
4881
|
return {
|
|
5083
4882
|
blockId: toWireIdentifier(block.id, "block"),
|
|
5084
|
-
title: boundNullableWireText(block.title,
|
|
4883
|
+
title: boundNullableWireText(block.title, MCP_WIRE_LIMITS7.labelCharacters),
|
|
5085
4884
|
profile: {
|
|
5086
4885
|
...profile,
|
|
5087
4886
|
hasAsciiDiagram: profile.hasAsciiDiagram ?? false,
|
|
@@ -5180,12 +4979,12 @@ function registerThemeTools(server, context) {
|
|
|
5180
4979
|
layouts: (inferred.layouts ?? []).slice(0, 1e3).map((layout, index) => ({
|
|
5181
4980
|
id: boundWireText(
|
|
5182
4981
|
layout.name,
|
|
5183
|
-
|
|
4982
|
+
MCP_WIRE_LIMITS7.labelCharacters,
|
|
5184
4983
|
`layout-${index + 1}`
|
|
5185
4984
|
),
|
|
5186
4985
|
name: boundWireText(
|
|
5187
4986
|
layout.label,
|
|
5188
|
-
|
|
4987
|
+
MCP_WIRE_LIMITS7.labelCharacters,
|
|
5189
4988
|
`Layout ${index + 1}`
|
|
5190
4989
|
),
|
|
5191
4990
|
description: boundWireText(layout.description ?? "")
|
|
@@ -5230,21 +5029,21 @@ function registerThemeTools(server, context) {
|
|
|
5230
5029
|
layouts: result.layouts.slice(0, 2048).map((layout, index) => ({
|
|
5231
5030
|
layoutPath: boundWireText(
|
|
5232
5031
|
layout.layoutPath,
|
|
5233
|
-
|
|
5032
|
+
MCP_WIRE_LIMITS7.pathCharacters,
|
|
5234
5033
|
`ppt/slideLayouts/slideLayout${index + 1}.xml`
|
|
5235
5034
|
),
|
|
5236
5035
|
name: boundWireText(
|
|
5237
5036
|
layout.name,
|
|
5238
|
-
|
|
5037
|
+
MCP_WIRE_LIMITS7.labelCharacters,
|
|
5239
5038
|
`Layout ${index + 1}`
|
|
5240
5039
|
),
|
|
5241
|
-
masterName: boundNullableWireText(layout.masterName,
|
|
5242
|
-
type: boundNullableWireText(layout.typeAttr,
|
|
5040
|
+
masterName: boundNullableWireText(layout.masterName, MCP_WIRE_LIMITS7.labelCharacters),
|
|
5041
|
+
type: boundNullableWireText(layout.typeAttr, MCP_WIRE_LIMITS7.labelCharacters),
|
|
5243
5042
|
slideCount: layout.slideCount,
|
|
5244
5043
|
verdict: layout.verdict,
|
|
5245
5044
|
templateId: boundNullableWireText(
|
|
5246
5045
|
layout.builtinTemplate ?? layout.customTemplate?.name,
|
|
5247
|
-
|
|
5046
|
+
MCP_WIRE_LIMITS7.labelCharacters
|
|
5248
5047
|
),
|
|
5249
5048
|
notes: boundWireTextArray(layout.notes ?? [], 1e3)
|
|
5250
5049
|
}))
|
|
@@ -5344,7 +5143,7 @@ function registerThemeTools(server, context) {
|
|
|
5344
5143
|
const payload = {
|
|
5345
5144
|
result,
|
|
5346
5145
|
theme: summarizeTheme(inferred.theme),
|
|
5347
|
-
layoutIds: (inferred.layouts ?? []).slice(0, 1e3).map((layout) => boundWireText(layout.name,
|
|
5146
|
+
layoutIds: (inferred.layouts ?? []).slice(0, 1e3).map((layout) => boundWireText(layout.name, MCP_WIRE_LIMITS7.labelCharacters)),
|
|
5348
5147
|
warnings: boundWireWarnings(inferred.warnings)
|
|
5349
5148
|
};
|
|
5350
5149
|
return {
|
|
@@ -5401,12 +5200,40 @@ function artifactLink(artifact) {
|
|
|
5401
5200
|
async function sendProgress(extra, progress, total, message) {
|
|
5402
5201
|
await reportMcpProgress(extra, progress, total, message);
|
|
5403
5202
|
}
|
|
5404
|
-
function textAndStructured(payload) {
|
|
5203
|
+
function textAndStructured(payload, text = JSON.stringify(payload, null, 2)) {
|
|
5405
5204
|
return {
|
|
5406
|
-
content: [{ type: "text", text
|
|
5205
|
+
content: [{ type: "text", text }],
|
|
5407
5206
|
structuredContent: asStructuredContent(successResult(payload))
|
|
5408
5207
|
};
|
|
5409
5208
|
}
|
|
5209
|
+
function compactAuthoringContextText(context) {
|
|
5210
|
+
const target = context.targetFormat ?? "unspecified target";
|
|
5211
|
+
const lines = [
|
|
5212
|
+
`DocBlocks authoring contract: ${target}, ${context.goal}.`,
|
|
5213
|
+
`Default template: ${context.defaultTemplateId}. Default fidelity: ${context.defaultFidelity ?? "select for target"}.`,
|
|
5214
|
+
"",
|
|
5215
|
+
"Required workflow:",
|
|
5216
|
+
...context.workflow.map((step, index) => `${index + 1}. ${step}`),
|
|
5217
|
+
"",
|
|
5218
|
+
`Heading annotation: ${context.syntax.headingAnnotation}`,
|
|
5219
|
+
`Warning: ${context.syntax.standaloneWarning}`,
|
|
5220
|
+
`Templates (${context.templates.length}): ${context.templates.map(({ id }) => id).join(", ")}`,
|
|
5221
|
+
`Themes (${context.themes.length}): ${context.themes.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.`
|
|
5223
|
+
];
|
|
5224
|
+
if (context.recommendations.length > 0) {
|
|
5225
|
+
lines.push(
|
|
5226
|
+
`Recommended blocks: ${context.recommendations.map(
|
|
5227
|
+
({ blockId, recommendedTemplateIds }) => `${blockId} -> ${recommendedTemplateIds.join("/")}`
|
|
5228
|
+
).join("; ")}`
|
|
5229
|
+
);
|
|
5230
|
+
}
|
|
5231
|
+
lines.push(
|
|
5232
|
+
"",
|
|
5233
|
+
"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."
|
|
5234
|
+
);
|
|
5235
|
+
return lines.join("\n");
|
|
5236
|
+
}
|
|
5410
5237
|
function asStructuredContent(payload) {
|
|
5411
5238
|
return payload;
|
|
5412
5239
|
}
|
|
@@ -5419,18 +5246,18 @@ function summarizeTheme(theme) {
|
|
|
5419
5246
|
const id = requireWireIdentifier(theme.id, "theme id");
|
|
5420
5247
|
return {
|
|
5421
5248
|
id,
|
|
5422
|
-
name: boundWireText(theme.name,
|
|
5249
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS7.labelCharacters, id),
|
|
5423
5250
|
description: boundWireText(theme.description ?? ""),
|
|
5424
5251
|
colors: {
|
|
5425
|
-
primary: boundWireText(theme.colors.primary,
|
|
5426
|
-
secondary: boundWireText(theme.colors.secondary,
|
|
5252
|
+
primary: boundWireText(theme.colors.primary, MCP_WIRE_LIMITS7.labelCharacters, "#000000"),
|
|
5253
|
+
secondary: boundWireText(theme.colors.secondary, MCP_WIRE_LIMITS7.labelCharacters, "#000000"),
|
|
5427
5254
|
background: boundWireText(
|
|
5428
5255
|
theme.colors.background,
|
|
5429
|
-
|
|
5256
|
+
MCP_WIRE_LIMITS7.labelCharacters,
|
|
5430
5257
|
"#ffffff"
|
|
5431
5258
|
),
|
|
5432
|
-
text: boundWireText(theme.colors.text,
|
|
5433
|
-
highlight: boundWireText(theme.colors.highlight,
|
|
5259
|
+
text: boundWireText(theme.colors.text, MCP_WIRE_LIMITS7.labelCharacters, "#000000"),
|
|
5260
|
+
highlight: boundWireText(theme.colors.highlight, MCP_WIRE_LIMITS7.labelCharacters, "#000000")
|
|
5434
5261
|
},
|
|
5435
5262
|
bodyFont: boundWireText(JSON.stringify(theme.typography.bodyFont) ?? ""),
|
|
5436
5263
|
titleFont: boundWireText(JSON.stringify(theme.typography.titleFont) ?? "")
|
|
@@ -5444,26 +5271,26 @@ async function authoringTemplateCatalog() {
|
|
|
5444
5271
|
TEMPLATE_METADATA
|
|
5445
5272
|
} = await import("@bendyline/squisq/doc");
|
|
5446
5273
|
const authoring = TEMPLATE_AUTHORING_METADATA;
|
|
5447
|
-
return getAvailableTemplates().slice(0,
|
|
5274
|
+
return getAvailableTemplates().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((id) => {
|
|
5448
5275
|
const metadata = TEMPLATE_METADATA[id];
|
|
5449
5276
|
const behavior = authoring[id];
|
|
5450
5277
|
if (!behavior) throw new Error(`Template "${id}" has no authoring metadata`);
|
|
5451
|
-
const inputs = (TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0,
|
|
5278
|
+
const inputs = (TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
|
|
5452
5279
|
key: requireWireIdentifier(input.key, "template input key"),
|
|
5453
5280
|
description: boundWireText(input.description),
|
|
5454
|
-
type: boundWireText(input.coerce ?? "string",
|
|
5281
|
+
type: boundWireText(input.coerce ?? "string", MCP_WIRE_LIMITS7.labelCharacters, "string"),
|
|
5455
5282
|
required: input.required ?? false,
|
|
5456
5283
|
values: boundWireTextArray(
|
|
5457
5284
|
input.values ?? [],
|
|
5458
|
-
|
|
5459
|
-
|
|
5285
|
+
MCP_WIRE_LIMITS7.arrayEntries,
|
|
5286
|
+
MCP_WIRE_LIMITS7.labelCharacters
|
|
5460
5287
|
),
|
|
5461
|
-
valueHint: boundNullableWireText(input.valueHint,
|
|
5288
|
+
valueHint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
|
|
5462
5289
|
}));
|
|
5463
5290
|
const required = inputs.filter((input) => input.required).map((input) => `${input.key}="\u2026"`).join(" ");
|
|
5464
5291
|
return {
|
|
5465
5292
|
id: requireWireIdentifier(id, "template id"),
|
|
5466
|
-
label: boundWireText(metadata?.label ?? id,
|
|
5293
|
+
label: boundWireText(metadata?.label ?? id, MCP_WIRE_LIMITS7.labelCharacters, id),
|
|
5467
5294
|
description: boundWireText(metadata?.description ?? ""),
|
|
5468
5295
|
inputs,
|
|
5469
5296
|
...behavior,
|
|
@@ -5471,6 +5298,15 @@ async function authoringTemplateCatalog() {
|
|
|
5471
5298
|
};
|
|
5472
5299
|
});
|
|
5473
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
|
+
}
|
|
5474
5310
|
function authoringDefaultFidelity(targetFormat, goal) {
|
|
5475
5311
|
if (!targetFormat) return null;
|
|
5476
5312
|
if (targetFormat === "mp4" || targetFormat === "gif") return "rendered-fidelity";
|
|
@@ -5485,14 +5321,14 @@ function authoringDefaultFidelity(targetFormat, goal) {
|
|
|
5485
5321
|
function boundedFormatCapabilities() {
|
|
5486
5322
|
return MCP_FORMAT_CAPABILITIES.slice(0, 64).map((format) => {
|
|
5487
5323
|
const id = requireWireIdentifier(format.id, "format id");
|
|
5488
|
-
if (id.length >
|
|
5324
|
+
if (id.length > MCP_WIRE_LIMITS7.formatCharacters || !MCP_OUTPUT_PATTERNS.format.test(id)) {
|
|
5489
5325
|
throw new Error("Linked Squisq returned an invalid MCP format id");
|
|
5490
5326
|
}
|
|
5491
5327
|
return {
|
|
5492
5328
|
id,
|
|
5493
|
-
label: boundWireText(format.label,
|
|
5329
|
+
label: boundWireText(format.label, MCP_WIRE_LIMITS7.labelCharacters, id),
|
|
5494
5330
|
mimeType: format.mimeType,
|
|
5495
|
-
extensions: boundWireTextArray(format.extensions, 64,
|
|
5331
|
+
extensions: boundWireTextArray(format.extensions, 64, MCP_WIRE_LIMITS7.labelCharacters),
|
|
5496
5332
|
import: { ...format.import },
|
|
5497
5333
|
export: { ...format.export }
|
|
5498
5334
|
};
|
|
@@ -5508,7 +5344,6 @@ var init_agentic_tools = __esm({
|
|
|
5508
5344
|
init_preview_service();
|
|
5509
5345
|
init_error_result();
|
|
5510
5346
|
init_progress();
|
|
5511
|
-
init_revision_service();
|
|
5512
5347
|
init_output_bounds();
|
|
5513
5348
|
READ_ONLY = {
|
|
5514
5349
|
readOnlyHint: true,
|
|
@@ -5528,7 +5363,7 @@ var init_agentic_tools = __esm({
|
|
|
5528
5363
|
idempotentHint: false,
|
|
5529
5364
|
openWorldHint: false
|
|
5530
5365
|
};
|
|
5531
|
-
identifierSchema = z.string().min(1).max(
|
|
5366
|
+
identifierSchema = z.string().min(1).max(MCP_WIRE_LIMITS7.identifierCharacters).regex(MCP_OUTPUT_PATTERNS.identifier);
|
|
5532
5367
|
fidelitySchemas = {
|
|
5533
5368
|
editable: z.enum(MCP_FORMAT_FIDELITIES.docx),
|
|
5534
5369
|
pdf: z.enum(MCP_FORMAT_FIDELITIES.pdf),
|
|
@@ -5641,7 +5476,7 @@ var init_agentic_tools = __esm({
|
|
|
5641
5476
|
|
|
5642
5477
|
// src/mcp/discovery-tools.ts
|
|
5643
5478
|
import { z as z2 } from "zod";
|
|
5644
|
-
import { MCP_WIRE_LIMITS as
|
|
5479
|
+
import { MCP_WIRE_LIMITS as MCP_WIRE_LIMITS8 } from "@bendyline/docblocks/mcp";
|
|
5645
5480
|
import { DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS as DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS2 } from "@bendyline/docblocks/mcp/zod";
|
|
5646
5481
|
function registerDiscoveryTools(server) {
|
|
5647
5482
|
server.registerTool(
|
|
@@ -5676,9 +5511,9 @@ function registerDiscoveryTools(server) {
|
|
|
5676
5511
|
try {
|
|
5677
5512
|
const { getThemeSummaries } = await import("@bendyline/squisq/schemas");
|
|
5678
5513
|
const result = {
|
|
5679
|
-
themes: getThemeSummaries().slice(0,
|
|
5514
|
+
themes: getThemeSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((theme) => ({
|
|
5680
5515
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
5681
|
-
name: boundWireText(theme.name,
|
|
5516
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS8.labelCharacters, theme.id),
|
|
5682
5517
|
description: boundWireText(theme.description ?? "")
|
|
5683
5518
|
}))
|
|
5684
5519
|
};
|
|
@@ -5703,9 +5538,9 @@ function registerDiscoveryTools(server) {
|
|
|
5703
5538
|
try {
|
|
5704
5539
|
const { getTransformStyleSummaries } = await import("@bendyline/squisq/transform");
|
|
5705
5540
|
const result = {
|
|
5706
|
-
styles: getTransformStyleSummaries().slice(0,
|
|
5541
|
+
styles: getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((style) => ({
|
|
5707
5542
|
id: requireWireIdentifier(style.id, "transform style id"),
|
|
5708
|
-
name: boundWireText(style.name,
|
|
5543
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS8.labelCharacters, style.id),
|
|
5709
5544
|
description: boundWireText(style.description)
|
|
5710
5545
|
}))
|
|
5711
5546
|
};
|
|
@@ -5739,16 +5574,16 @@ function registerDiscoveryTools(server) {
|
|
|
5739
5574
|
function serializableCapabilities() {
|
|
5740
5575
|
return MCP_FORMAT_CAPABILITIES.slice(0, 64).map((format) => ({
|
|
5741
5576
|
id: requireWireFormat(format.id),
|
|
5742
|
-
label: boundWireText(format.label,
|
|
5577
|
+
label: boundWireText(format.label, MCP_WIRE_LIMITS8.labelCharacters, format.id),
|
|
5743
5578
|
mimeType: format.mimeType,
|
|
5744
|
-
extensions: boundWireTextArray(format.extensions, 64,
|
|
5579
|
+
extensions: boundWireTextArray(format.extensions, 64, MCP_WIRE_LIMITS8.labelCharacters),
|
|
5745
5580
|
import: { ...format.import },
|
|
5746
5581
|
export: { ...format.export }
|
|
5747
5582
|
}));
|
|
5748
5583
|
}
|
|
5749
5584
|
function requireWireFormat(value) {
|
|
5750
5585
|
requireWireIdentifier(value, "format id");
|
|
5751
|
-
if (value.length >
|
|
5586
|
+
if (value.length > MCP_WIRE_LIMITS8.formatCharacters || !MCP_OUTPUT_PATTERNS.format.test(value)) {
|
|
5752
5587
|
throw new Error("Linked Squisq returned an invalid MCP format id");
|
|
5753
5588
|
}
|
|
5754
5589
|
return value;
|
|
@@ -5874,12 +5709,12 @@ function presentationPrompt(topic, style, theme, template) {
|
|
|
5874
5709
|
return `Create a presentation about: ${topic}
|
|
5875
5710
|
|
|
5876
5711
|
1. Call get_authoring_context with targetFormat "pptx" and goal "content-first" for the complete linked-Squisq vocabulary and workflow in one response.
|
|
5877
|
-
2.
|
|
5878
|
-
3.
|
|
5879
|
-
4.
|
|
5880
|
-
5.
|
|
5881
|
-
6.
|
|
5882
|
-
7. 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.
|
|
5712
|
+
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.
|
|
5713
|
+
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"}".
|
|
5714
|
+
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.
|
|
5715
|
+
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.
|
|
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.
|
|
5883
5718
|
8. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5884
5719
|
}
|
|
5885
5720
|
function videoPrompt(topic, orientation, theme, template) {
|
|
@@ -5887,20 +5722,19 @@ function videoPrompt(topic, orientation, theme, template) {
|
|
|
5887
5722
|
|
|
5888
5723
|
1. Call get_authoring_context with targetFormat "mp4" for the complete linked-Squisq vocabulary and workflow.
|
|
5889
5724
|
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.
|
|
5890
|
-
3.
|
|
5891
|
-
4. Call
|
|
5892
|
-
5.
|
|
5893
|
-
6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5725
|
+
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.
|
|
5726
|
+
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.
|
|
5727
|
+
5. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5894
5728
|
}
|
|
5895
5729
|
function documentPrompt(topic, format, theme, template) {
|
|
5896
5730
|
const target = format ?? "pdf";
|
|
5897
5731
|
return `Create a professional document about: ${topic}
|
|
5898
5732
|
|
|
5899
5733
|
1. Call get_authoring_context with targetFormat "${target}" for the complete linked-Squisq vocabulary and workflow.
|
|
5900
|
-
2.
|
|
5901
|
-
3.
|
|
5902
|
-
4.
|
|
5903
|
-
5. Call
|
|
5734
|
+
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.
|
|
5735
|
+
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.
|
|
5736
|
+
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.
|
|
5737
|
+
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.
|
|
5904
5738
|
6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5905
5739
|
}
|
|
5906
5740
|
function complete(values, prefix) {
|
|
@@ -5943,7 +5777,7 @@ function createMcpServer(options = {}) {
|
|
|
5943
5777
|
const server = new McpServer(
|
|
5944
5778
|
{ name: "docblocks", version: getPackageVersion() },
|
|
5945
5779
|
{
|
|
5946
|
-
instructions: "
|
|
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."
|
|
5947
5781
|
}
|
|
5948
5782
|
);
|
|
5949
5783
|
registerAgenticTools(server, {
|