@bendyline/docblocks-cli 2.1.1 → 2.1.2
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 +651 -116
- package/package.json +7 -7
package/dist/bin.js
CHANGED
|
@@ -869,6 +869,9 @@ async function withMaterializationLock(target, operation) {
|
|
|
869
869
|
if (materializationTails.get(key) === tail) materializationTails.delete(key);
|
|
870
870
|
}
|
|
871
871
|
}
|
|
872
|
+
function authorityError(message, hint) {
|
|
873
|
+
return Object.assign(new Error(message), { code: "unauthorized-path", hint });
|
|
874
|
+
}
|
|
872
875
|
var DEFAULT_MAX_INPUT_FILE_BYTES, MCP_FILE_AUTHORITY_LIMITS, MAX_PATH_CHARACTERS, MATERIALIZATION_WRITE_CHUNK_BYTES, HASH_READ_CHUNK_BYTES, materializationTails, McpFileAuthority;
|
|
873
876
|
var init_authority = __esm({
|
|
874
877
|
"src/mcp/authority.ts"() {
|
|
@@ -947,7 +950,12 @@ var init_authority = __esm({
|
|
|
947
950
|
/** Resolve a root-relative read without ever treating the alias as new authority. */
|
|
948
951
|
async authorizeRootRead(rootId, workspacePath) {
|
|
949
952
|
const root = this.readRoots.find((candidate2) => candidate2.id === rootId);
|
|
950
|
-
if (!root)
|
|
953
|
+
if (!root) {
|
|
954
|
+
throw authorityError(
|
|
955
|
+
"Unknown or unreadable MCP root",
|
|
956
|
+
"Call list_roots and copy a returned read-enabled root id. If no roots are listed, use an inline markdown or bundle source, or restart the server with --allow-read."
|
|
957
|
+
);
|
|
958
|
+
}
|
|
951
959
|
const candidate = path7.join(root.physical, ...parseRootRelativePath(workspacePath));
|
|
952
960
|
const physical = await realpath4(candidate).catch(() => null);
|
|
953
961
|
if (!physical || !isPathInside(root.physical, physical)) {
|
|
@@ -958,7 +966,12 @@ var init_authority = __esm({
|
|
|
958
966
|
/** Resolve a root-relative write without ever treating the alias as new authority. */
|
|
959
967
|
async authorizeRootWrite(rootId, workspacePath) {
|
|
960
968
|
const root = this.writeRoots.find((candidate2) => candidate2.id === rootId);
|
|
961
|
-
if (!root)
|
|
969
|
+
if (!root) {
|
|
970
|
+
throw authorityError(
|
|
971
|
+
"Unknown or unwritable MCP root",
|
|
972
|
+
"Call list_roots and copy a returned write-enabled root id. If none is writable, keep the result as a session artifact or restart the server with --allow-write."
|
|
973
|
+
);
|
|
974
|
+
}
|
|
962
975
|
const candidate = path7.join(root.physical, ...parseRootRelativePath(workspacePath));
|
|
963
976
|
const authorized = await this.authorizeWrite(candidate);
|
|
964
977
|
const physicalParent = await realpath4(path7.dirname(authorized));
|
|
@@ -1537,7 +1550,10 @@ var init_document_service = __esm({
|
|
|
1537
1550
|
const { parseMarkdown: parseMarkdown2 } = await import("@bendyline/squisq/markdown");
|
|
1538
1551
|
const { markdownToDoc: markdownToDoc2 } = await import("@bendyline/squisq/doc");
|
|
1539
1552
|
const markdownDoc = parseMarkdown2(markdown);
|
|
1540
|
-
const doc = markdownToDoc2(markdownDoc
|
|
1553
|
+
const doc = markdownToDoc2(markdownDoc, {
|
|
1554
|
+
defaultTemplate: "content",
|
|
1555
|
+
autoTemplates: false
|
|
1556
|
+
});
|
|
1541
1557
|
const assets = await this.assetSummaries(container, signal);
|
|
1542
1558
|
throwIfAborted5(signal);
|
|
1543
1559
|
return {
|
|
@@ -1776,7 +1792,8 @@ async function inspectPreparedDocument(_documents, prepared, maximumBlocks = DEF
|
|
|
1776
1792
|
...validation.diagnostics.map(
|
|
1777
1793
|
(diagnostic) => mapDocDiagnostic(diagnostic, prepared.sourceFormat)
|
|
1778
1794
|
),
|
|
1779
|
-
...missingAltTextDiagnostics(prepared.markdownDoc, prepared.sourceFormat)
|
|
1795
|
+
...missingAltTextDiagnostics(prepared.markdownDoc, prepared.sourceFormat),
|
|
1796
|
+
...await authoringDiagnostics(prepared, "inspect", prepared.sourceFormat, signal)
|
|
1780
1797
|
];
|
|
1781
1798
|
let wordCount = 0;
|
|
1782
1799
|
for (let index = 0; index < analyzed.length; index += 1) {
|
|
@@ -1833,6 +1850,12 @@ async function validatePreparedDocument(_documents, prepared, targetFormat, sign
|
|
|
1833
1850
|
prepared.markdownDoc,
|
|
1834
1851
|
normalizedTargetFormat ?? prepared.sourceFormat
|
|
1835
1852
|
),
|
|
1853
|
+
...await authoringDiagnostics(
|
|
1854
|
+
prepared,
|
|
1855
|
+
"validate",
|
|
1856
|
+
normalizedTargetFormat ?? prepared.sourceFormat,
|
|
1857
|
+
signal
|
|
1858
|
+
),
|
|
1836
1859
|
...normalizedTargetFormat ? await targetPreflight(prepared, normalizedTargetFormat, signal) : []
|
|
1837
1860
|
];
|
|
1838
1861
|
await yieldForCancellation(signal);
|
|
@@ -1849,6 +1872,111 @@ async function validatePreparedDocument(_documents, prepared, targetFormat, sign
|
|
|
1849
1872
|
diagnostics: unique
|
|
1850
1873
|
};
|
|
1851
1874
|
}
|
|
1875
|
+
async function authoringDiagnostics(prepared, stage, format, signal) {
|
|
1876
|
+
throwIfAborted5(signal);
|
|
1877
|
+
const {
|
|
1878
|
+
TEMPLATE_AUTHORING_METADATA,
|
|
1879
|
+
flattenBlocks,
|
|
1880
|
+
getBlockBodyText,
|
|
1881
|
+
materializeBlockLayers,
|
|
1882
|
+
resolveTemplateName,
|
|
1883
|
+
resolveThemeForDoc
|
|
1884
|
+
} = await import("@bendyline/squisq/doc");
|
|
1885
|
+
const blocks = flattenBlocks(prepared.doc.blocks);
|
|
1886
|
+
const analyzedBlocks = blocks.slice(0, MCP_WIRE_LIMITS5.arrayEntries);
|
|
1887
|
+
const theme = resolveThemeForDoc(prepared.doc);
|
|
1888
|
+
const diagnostics = [];
|
|
1889
|
+
for (let index = 0; index < analyzedBlocks.length; index += 1) {
|
|
1890
|
+
const checkpoint = cancellationCheckpoint(signal, index);
|
|
1891
|
+
if (checkpoint) await checkpoint;
|
|
1892
|
+
const block = analyzedBlocks[index];
|
|
1893
|
+
throwIfAborted5(signal);
|
|
1894
|
+
if (block.standaloneAnnotation) {
|
|
1895
|
+
diagnostics.push({
|
|
1896
|
+
code: "standalone-template-block",
|
|
1897
|
+
severity: "warning",
|
|
1898
|
+
stage,
|
|
1899
|
+
format,
|
|
1900
|
+
count: 1,
|
|
1901
|
+
message: `Block "${block.id}" was created by a standalone template annotation and has no heading.`,
|
|
1902
|
+
remediation: "Move the annotation onto the intended heading, for example `# Heading {[content]}`, unless the additional heading-less block is deliberate.",
|
|
1903
|
+
retryable: false,
|
|
1904
|
+
location: {
|
|
1905
|
+
kind: "block",
|
|
1906
|
+
blockId: toWireIdentifier(block.id, "block"),
|
|
1907
|
+
nodeType: "block"
|
|
1908
|
+
}
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
if (!block.template) continue;
|
|
1912
|
+
const templateId = resolveTemplateName(block.template);
|
|
1913
|
+
const behavior = TEMPLATE_AUTHORING_METADATA[templateId];
|
|
1914
|
+
if (!behavior) continue;
|
|
1915
|
+
const body = getBlockBodyText(block).trim();
|
|
1916
|
+
if (!body) continue;
|
|
1917
|
+
if (behavior.bodyPolicy === "ignored") {
|
|
1918
|
+
diagnostics.push({
|
|
1919
|
+
code: "template-body-not-rendered",
|
|
1920
|
+
severity: "warning",
|
|
1921
|
+
stage,
|
|
1922
|
+
format,
|
|
1923
|
+
count: 1,
|
|
1924
|
+
message: `Template "${templateId}" does not render the body of block "${block.title ?? block.id}".`,
|
|
1925
|
+
remediation: "Use the content template to preserve the complete body, or move this prose into a separate content block before visual optimization.",
|
|
1926
|
+
retryable: false,
|
|
1927
|
+
location: {
|
|
1928
|
+
kind: "block",
|
|
1929
|
+
blockId: toWireIdentifier(block.id, "block"),
|
|
1930
|
+
nodeType: "block"
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
if (behavior.bodyPolicy !== "complete") continue;
|
|
1936
|
+
const materialized = materializeBlockLayers(block, {
|
|
1937
|
+
theme,
|
|
1938
|
+
customTemplates: prepared.doc.customTemplates,
|
|
1939
|
+
persistentLayers: false,
|
|
1940
|
+
blockIndex: index,
|
|
1941
|
+
totalBlocks: analyzedBlocks.length
|
|
1942
|
+
});
|
|
1943
|
+
const renderedText = materialized.layers.filter((layer) => layer.type === "text").map((layer) => layer.content.text).join("\n");
|
|
1944
|
+
if (!normalizedText(renderedText).includes(normalizedText(body))) {
|
|
1945
|
+
diagnostics.push({
|
|
1946
|
+
code: "rendered-content-omitted",
|
|
1947
|
+
severity: "error",
|
|
1948
|
+
stage,
|
|
1949
|
+
format,
|
|
1950
|
+
count: 1,
|
|
1951
|
+
message: `The complete body of block "${block.title ?? block.id}" is not present in its rendered text layers.`,
|
|
1952
|
+
remediation: "Keep the content template and repair its inputs, then validate and preview again before conversion.",
|
|
1953
|
+
retryable: false,
|
|
1954
|
+
location: {
|
|
1955
|
+
kind: "block",
|
|
1956
|
+
blockId: toWireIdentifier(block.id, "block"),
|
|
1957
|
+
nodeType: "block"
|
|
1958
|
+
}
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
if (analyzedBlocks.length < blocks.length) {
|
|
1963
|
+
diagnostics.push({
|
|
1964
|
+
code: "authoring-analysis-truncated",
|
|
1965
|
+
severity: "info",
|
|
1966
|
+
stage,
|
|
1967
|
+
format,
|
|
1968
|
+
count: blocks.length - analyzedBlocks.length,
|
|
1969
|
+
message: `Authoring diagnostics analyzed the first ${analyzedBlocks.length} blocks.`,
|
|
1970
|
+
remediation: "Split the document before requesting exhaustive per-block authoring diagnostics.",
|
|
1971
|
+
retryable: false,
|
|
1972
|
+
location: null
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
return diagnostics;
|
|
1976
|
+
}
|
|
1977
|
+
function normalizedText(value) {
|
|
1978
|
+
return value.normalize("NFKC").replace(/\s+/gu, " ").trim().toLocaleLowerCase("en-US");
|
|
1979
|
+
}
|
|
1852
1980
|
async function comparePreparedDocuments(documents, left, right, signal) {
|
|
1853
1981
|
throwIfAborted5(signal);
|
|
1854
1982
|
await yieldForCancellation(signal);
|
|
@@ -2728,6 +2856,7 @@ async function previewPreparedDocument(artifacts, prepared, request, signal, pro
|
|
|
2728
2856
|
diagnostics: boundDiagnostics(
|
|
2729
2857
|
[
|
|
2730
2858
|
...prepared.diagnostics,
|
|
2859
|
+
...await authoringDiagnostics(prepared, "render", prepared.sourceFormat, signal),
|
|
2731
2860
|
...rendered.diagnostics ?? [],
|
|
2732
2861
|
...previewBasis === "reconstructed-import" ? [reconstructedPreviewDiagnostic(prepared.sourceFormat)] : []
|
|
2733
2862
|
],
|
|
@@ -3149,10 +3278,13 @@ async function prepareRenderedDocument(prepared, options, signal) {
|
|
|
3149
3278
|
}
|
|
3150
3279
|
};
|
|
3151
3280
|
registry.register(captureDefinition);
|
|
3281
|
+
const canonicalMarkdown = options.autoTemplates === true ? prepared.markdownDoc : (await import("@bendyline/squisq/doc")).docToMarkdown(prepared.doc, {
|
|
3282
|
+
defaultTemplate: "sectionHeader"
|
|
3283
|
+
});
|
|
3152
3284
|
const result = await convert(
|
|
3153
3285
|
{
|
|
3154
3286
|
kind: "markdown",
|
|
3155
|
-
markdown:
|
|
3287
|
+
markdown: canonicalMarkdown,
|
|
3156
3288
|
container: prepared.container,
|
|
3157
3289
|
baseName: prepared.baseName
|
|
3158
3290
|
},
|
|
@@ -4029,13 +4161,258 @@ var init_progress = __esm({
|
|
|
4029
4161
|
}
|
|
4030
4162
|
});
|
|
4031
4163
|
|
|
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
|
+
|
|
4032
4408
|
// src/mcp/agentic-tools.ts
|
|
4033
4409
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4034
4410
|
import { z } from "zod";
|
|
4035
4411
|
import {
|
|
4036
|
-
MCP_WIRE_LIMITS as
|
|
4412
|
+
MCP_WIRE_LIMITS as MCP_WIRE_LIMITS8,
|
|
4037
4413
|
parseComparisonResult,
|
|
4038
4414
|
parseConversionResult as parseConversionResult2,
|
|
4415
|
+
parseDocumentRevisionResult as parseDocumentRevisionResult2,
|
|
4039
4416
|
parseDocumentSource,
|
|
4040
4417
|
parseInspectionResult,
|
|
4041
4418
|
parseMaterializationOptions,
|
|
@@ -4047,6 +4424,7 @@ import {
|
|
|
4047
4424
|
bundleDocumentSourceSchema,
|
|
4048
4425
|
DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS,
|
|
4049
4426
|
documentSourceSchema,
|
|
4427
|
+
documentRevisionRequestSchema,
|
|
4050
4428
|
formatInputSchema,
|
|
4051
4429
|
materializationOptionsSchema
|
|
4052
4430
|
} from "@bendyline/docblocks/mcp/zod";
|
|
@@ -4142,7 +4520,7 @@ function registerAgenticTools(server, context) {
|
|
|
4142
4520
|
server.registerTool(
|
|
4143
4521
|
"create_document_bundle",
|
|
4144
4522
|
{
|
|
4145
|
-
description: "
|
|
4523
|
+
description: "Stage authored Markdown and authority-scoped assets as an immutable DBK artifact. Use the returned artifact URI directly with validate_document, preview_document, and convert_document; no local Markdown file or invented root id is required.",
|
|
4146
4524
|
inputSchema: z.object({ source: bundleDocumentSourceSchema }).strict(),
|
|
4147
4525
|
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.create_document_bundle,
|
|
4148
4526
|
annotations: ARTIFACT_CREATING
|
|
@@ -4174,6 +4552,38 @@ function registerAgenticTools(server, context) {
|
|
|
4174
4552
|
}
|
|
4175
4553
|
}
|
|
4176
4554
|
);
|
|
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
|
+
);
|
|
4177
4587
|
server.registerTool(
|
|
4178
4588
|
"save_artifact",
|
|
4179
4589
|
{
|
|
@@ -4422,43 +4832,30 @@ function registerAuthoringGuideResource(server) {
|
|
|
4422
4832
|
"docblocks://authoring-guide",
|
|
4423
4833
|
{
|
|
4424
4834
|
description: "Compact linked-Squisq authoring vocabulary, artifact workflow, fidelity modes, templates, themes, and transform styles.",
|
|
4425
|
-
mimeType: "application/json"
|
|
4835
|
+
mimeType: "application/json",
|
|
4836
|
+
annotations: { audience: ["assistant"], priority: 1 }
|
|
4426
4837
|
},
|
|
4427
4838
|
async () => {
|
|
4428
|
-
const [
|
|
4429
|
-
|
|
4839
|
+
const [templates, schemaModule, transformModule] = await Promise.all([
|
|
4840
|
+
authoringTemplateCatalog(),
|
|
4430
4841
|
import("@bendyline/squisq/schemas"),
|
|
4431
4842
|
import("@bendyline/squisq/transform")
|
|
4432
4843
|
]);
|
|
4433
|
-
const templates = docModule.getAvailableTemplates().slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((id) => ({
|
|
4434
|
-
id: requireWireIdentifier(id, "template id"),
|
|
4435
|
-
label: boundWireText(
|
|
4436
|
-
docModule.TEMPLATE_METADATA[id]?.label ?? id,
|
|
4437
|
-
MCP_WIRE_LIMITS7.labelCharacters,
|
|
4438
|
-
id
|
|
4439
|
-
),
|
|
4440
|
-
description: boundWireText(docModule.TEMPLATE_METADATA[id]?.description ?? ""),
|
|
4441
|
-
inputs: (docModule.TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
|
|
4442
|
-
key: requireWireIdentifier(input.key, "template input key"),
|
|
4443
|
-
required: input.required ?? false,
|
|
4444
|
-
values: boundWireTextArray(
|
|
4445
|
-
input.values ?? [],
|
|
4446
|
-
MCP_WIRE_LIMITS7.arrayEntries,
|
|
4447
|
-
MCP_WIRE_LIMITS7.labelCharacters
|
|
4448
|
-
),
|
|
4449
|
-
hint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
|
|
4450
|
-
}))
|
|
4451
|
-
}));
|
|
4452
4844
|
const guide = {
|
|
4453
|
-
version:
|
|
4845
|
+
version: 3,
|
|
4454
4846
|
workflow: [
|
|
4455
|
-
"
|
|
4456
|
-
"
|
|
4457
|
-
"
|
|
4847
|
+
"author complete Markdown with annotations bound to headings; ordinary headings default to content",
|
|
4848
|
+
"keep Markdown inline or stage Markdown plus assets with create_document_bundle",
|
|
4849
|
+
"inspect_document and validate_document before visual optimization",
|
|
4850
|
+
"for a staged DBK, use revise_document with inspected block ids and the exact parent SHA-256 for targeted repairs",
|
|
4458
4851
|
"preview_document and inspect previewBasis before treating images as source renders, reconstructed imports, or native extraction",
|
|
4852
|
+
"replace selected content blocks with compatible visual templates and preview again",
|
|
4853
|
+
"convert_document to one or more immutable artifacts",
|
|
4459
4854
|
"save_artifact only when a durable file is required"
|
|
4460
4855
|
],
|
|
4461
|
-
markdownAnnotation: '{[templateId key="value"]}',
|
|
4856
|
+
markdownAnnotation: '# Heading {[templateId key="value"]}',
|
|
4857
|
+
standaloneAnnotation: '{[templateId key="value"]}',
|
|
4858
|
+
standaloneWarning: "A standalone annotation creates an additional heading-less block. Bind the annotation to a heading unless that extra block is deliberate.",
|
|
4462
4859
|
fidelity: {
|
|
4463
4860
|
semantic: "Prioritize meaning and portability.",
|
|
4464
4861
|
"editable-native": "Produce editable native structures with target-specific diagnostics.",
|
|
@@ -4468,14 +4865,14 @@ function registerAuthoringGuideResource(server) {
|
|
|
4468
4865
|
fidelityByFormat: MCP_FORMAT_FIDELITIES,
|
|
4469
4866
|
formats: boundedFormatCapabilities(),
|
|
4470
4867
|
templates,
|
|
4471
|
-
themes: schemaModule.getThemeSummaries().slice(0,
|
|
4868
|
+
themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((theme) => ({
|
|
4472
4869
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
4473
|
-
name: boundWireText(theme.name,
|
|
4870
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS8.labelCharacters, theme.id),
|
|
4474
4871
|
description: boundWireText(theme.description ?? "")
|
|
4475
4872
|
})),
|
|
4476
|
-
transformStyles: transformModule.getTransformStyleSummaries().slice(0,
|
|
4873
|
+
transformStyles: transformModule.getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((style) => ({
|
|
4477
4874
|
id: requireWireIdentifier(style.id, "transform style id"),
|
|
4478
|
-
name: boundWireText(style.name,
|
|
4875
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS8.labelCharacters, style.id),
|
|
4479
4876
|
description: boundWireText(style.description)
|
|
4480
4877
|
}))
|
|
4481
4878
|
};
|
|
@@ -4492,6 +4889,115 @@ function registerAuthoringGuideResource(server) {
|
|
|
4492
4889
|
);
|
|
4493
4890
|
}
|
|
4494
4891
|
function registerTemplateTools(server, context) {
|
|
4892
|
+
server.registerTool(
|
|
4893
|
+
"get_authoring_context",
|
|
4894
|
+
{
|
|
4895
|
+
description: "Get the complete linked-Squisq authoring vocabulary, canonical heading syntax, content-retention behavior, workflow, and optional block recommendations in one call.",
|
|
4896
|
+
inputSchema: z.object({
|
|
4897
|
+
targetFormat: formatInputSchema.optional(),
|
|
4898
|
+
goal: z.enum(["content-first", "visual-polish"]).optional(),
|
|
4899
|
+
source: documentSourceSchema.optional(),
|
|
4900
|
+
maxBlocks: z.number().int().min(1).max(100).optional()
|
|
4901
|
+
}).strict(),
|
|
4902
|
+
outputSchema: DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS.get_authoring_context,
|
|
4903
|
+
annotations: READ_ONLY
|
|
4904
|
+
},
|
|
4905
|
+
async ({ targetFormat, goal: requestedGoal, source, maxBlocks }, extra) => {
|
|
4906
|
+
try {
|
|
4907
|
+
return await context.runOperation(extra.signal, async (operationSignal) => {
|
|
4908
|
+
const goal = requestedGoal ?? "content-first";
|
|
4909
|
+
const normalizedTarget = targetFormat?.toLowerCase() ?? null;
|
|
4910
|
+
const formats = boundedFormatCapabilities();
|
|
4911
|
+
if (normalizedTarget) {
|
|
4912
|
+
const target = formats.find((format) => format.id === normalizedTarget);
|
|
4913
|
+
if (!target) throw new Error(`Unknown target format "${normalizedTarget}"`);
|
|
4914
|
+
if (!target.export.supported) {
|
|
4915
|
+
throw new Error(`Format "${normalizedTarget}" is not export-capable`);
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
const [templates, schemaModule, transformModule] = await Promise.all([
|
|
4919
|
+
authoringTemplateCatalog(),
|
|
4920
|
+
import("@bendyline/squisq/schemas"),
|
|
4921
|
+
import("@bendyline/squisq/transform")
|
|
4922
|
+
]);
|
|
4923
|
+
let recommendations = [];
|
|
4924
|
+
let totalBlocks = null;
|
|
4925
|
+
let truncated = false;
|
|
4926
|
+
if (source) {
|
|
4927
|
+
const documents = new DocumentService(await context.authority, context.artifacts);
|
|
4928
|
+
const prepared = await documents.prepare(
|
|
4929
|
+
requireDocumentSource(source),
|
|
4930
|
+
operationSignal
|
|
4931
|
+
);
|
|
4932
|
+
const [{ flattenBlocks }, recommendModule] = await Promise.all([
|
|
4933
|
+
import("@bendyline/squisq/doc"),
|
|
4934
|
+
import("@bendyline/squisq/recommend")
|
|
4935
|
+
]);
|
|
4936
|
+
const blocks = flattenBlocks(prepared.doc.blocks);
|
|
4937
|
+
const limit = maxBlocks ?? 50;
|
|
4938
|
+
recommendations = blocks.slice(0, limit).map((block) => {
|
|
4939
|
+
const profile = recommendModule.profileBlockContents(block.contents ?? []);
|
|
4940
|
+
const visual = recommendModule.recommendTemplatesForBlock(
|
|
4941
|
+
profile,
|
|
4942
|
+
templates.map((template) => template.id)
|
|
4943
|
+
).recommended;
|
|
4944
|
+
const recommendedTemplateIds = goal === "content-first" ? ["content", ...visual.filter((id) => id !== "content")] : visual;
|
|
4945
|
+
return {
|
|
4946
|
+
blockId: toWireIdentifier(block.id, "block"),
|
|
4947
|
+
title: boundNullableWireText(block.title, MCP_WIRE_LIMITS8.labelCharacters),
|
|
4948
|
+
profile: {
|
|
4949
|
+
...profile,
|
|
4950
|
+
hasAsciiDiagram: profile.hasAsciiDiagram ?? false,
|
|
4951
|
+
hasTimeline: profile.hasTimeline ?? false,
|
|
4952
|
+
hasTree: profile.hasTree ?? false
|
|
4953
|
+
},
|
|
4954
|
+
recommendedTemplateIds: recommendedTemplateIds.slice(0, 256).map((id) => requireWireIdentifier(id, "recommended template id"))
|
|
4955
|
+
};
|
|
4956
|
+
});
|
|
4957
|
+
totalBlocks = blocks.length;
|
|
4958
|
+
truncated = recommendations.length < blocks.length;
|
|
4959
|
+
}
|
|
4960
|
+
return textAndStructured({
|
|
4961
|
+
goal,
|
|
4962
|
+
targetFormat: normalizedTarget,
|
|
4963
|
+
defaultTemplateId: "content",
|
|
4964
|
+
defaultFidelity: authoringDefaultFidelity(normalizedTarget, goal),
|
|
4965
|
+
workflow: [
|
|
4966
|
+
"Author complete content with heading-bound annotations; ordinary headings default to content.",
|
|
4967
|
+
"Keep Markdown inline or stage Markdown plus assets with create_document_bundle.",
|
|
4968
|
+
"Validate the source and repair content-retention, accessibility, and target diagnostics.",
|
|
4969
|
+
"For a staged DBK, use revise_document with inspected block ids and the exact parent SHA-256 for targeted repairs.",
|
|
4970
|
+
"Preview the bounded visual items and repair overflow before changing layouts.",
|
|
4971
|
+
"For visual polish, replace selected content blocks with compatible recommended templates and preview again.",
|
|
4972
|
+
"Convert to immutable artifacts, inspect conversion reports, and save only final durable outputs."
|
|
4973
|
+
],
|
|
4974
|
+
syntax: {
|
|
4975
|
+
headingAnnotation: "# Heading {[content]}",
|
|
4976
|
+
standaloneAnnotation: "{[content]}",
|
|
4977
|
+
standaloneWarning: "A standalone annotation creates an additional heading-less block. Bind it to a heading unless that extra block is deliberate."
|
|
4978
|
+
},
|
|
4979
|
+
formats,
|
|
4980
|
+
templates,
|
|
4981
|
+
themes: schemaModule.getThemeSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((theme) => ({
|
|
4982
|
+
id: requireWireIdentifier(theme.id, "theme id"),
|
|
4983
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS8.labelCharacters, theme.id),
|
|
4984
|
+
description: boundWireText(theme.description ?? "")
|
|
4985
|
+
})),
|
|
4986
|
+
transformStyles: transformModule.getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((style) => ({
|
|
4987
|
+
id: requireWireIdentifier(style.id, "transform style id"),
|
|
4988
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS8.labelCharacters, style.id),
|
|
4989
|
+
description: boundWireText(style.description)
|
|
4990
|
+
})),
|
|
4991
|
+
recommendations,
|
|
4992
|
+
totalBlocks,
|
|
4993
|
+
truncated
|
|
4994
|
+
});
|
|
4995
|
+
});
|
|
4996
|
+
} catch (caught) {
|
|
4997
|
+
return errorResult(caught, "inspect", null, extra.signal);
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
);
|
|
4495
5001
|
server.registerTool(
|
|
4496
5002
|
"list_templates",
|
|
4497
5003
|
{
|
|
@@ -4502,15 +5008,10 @@ function registerTemplateTools(server, context) {
|
|
|
4502
5008
|
},
|
|
4503
5009
|
async () => {
|
|
4504
5010
|
try {
|
|
4505
|
-
const {
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
TEMPLATE_METADATA[id]?.label ?? id,
|
|
4510
|
-
MCP_WIRE_LIMITS7.labelCharacters,
|
|
4511
|
-
id
|
|
4512
|
-
),
|
|
4513
|
-
description: boundWireText(TEMPLATE_METADATA[id]?.description ?? "")
|
|
5011
|
+
const templates = (await authoringTemplateCatalog()).map(({ id, label, description }) => ({
|
|
5012
|
+
id,
|
|
5013
|
+
label,
|
|
5014
|
+
description
|
|
4514
5015
|
}));
|
|
4515
5016
|
return textAndStructured({ templates });
|
|
4516
5017
|
} catch (caught) {
|
|
@@ -4528,41 +5029,21 @@ function registerTemplateTools(server, context) {
|
|
|
4528
5029
|
},
|
|
4529
5030
|
async ({ templateId }) => {
|
|
4530
5031
|
try {
|
|
4531
|
-
const
|
|
4532
|
-
|
|
5032
|
+
const described = (await authoringTemplateCatalog()).find(
|
|
5033
|
+
(template2) => template2.id === templateId
|
|
5034
|
+
);
|
|
5035
|
+
if (!described) {
|
|
4533
5036
|
return errorResult(new Error(`Unknown template "${templateId}"`), "validate");
|
|
4534
5037
|
}
|
|
4535
|
-
const inputs = (TEMPLATE_INPUT_DESCRIPTORS[templateId] ?? []).slice(0, MCP_WIRE_LIMITS7.arrayEntries).map((input) => ({
|
|
4536
|
-
key: requireWireIdentifier(input.key, "template input key"),
|
|
4537
|
-
description: boundWireText(input.description),
|
|
4538
|
-
type: boundWireText(
|
|
4539
|
-
input.coerce ?? "string",
|
|
4540
|
-
MCP_WIRE_LIMITS7.labelCharacters,
|
|
4541
|
-
"string"
|
|
4542
|
-
),
|
|
4543
|
-
required: input.required ?? false,
|
|
4544
|
-
values: boundWireTextArray(
|
|
4545
|
-
input.values ?? [],
|
|
4546
|
-
MCP_WIRE_LIMITS7.arrayEntries,
|
|
4547
|
-
MCP_WIRE_LIMITS7.labelCharacters
|
|
4548
|
-
),
|
|
4549
|
-
valueHint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS7.labelCharacters)
|
|
4550
|
-
}));
|
|
4551
|
-
const metadata = TEMPLATE_METADATA[templateId];
|
|
4552
5038
|
const template = {
|
|
4553
|
-
id:
|
|
4554
|
-
label:
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
templateId
|
|
4558
|
-
),
|
|
4559
|
-
description: boundWireText(metadata?.description ?? ""),
|
|
4560
|
-
inputs
|
|
5039
|
+
id: described.id,
|
|
5040
|
+
label: described.label,
|
|
5041
|
+
description: described.description,
|
|
5042
|
+
inputs: described.inputs
|
|
4561
5043
|
};
|
|
4562
|
-
const required = inputs.filter((input) => input.required).map((input) => `${input.key}="\u2026"`).join(" ");
|
|
4563
5044
|
return textAndStructured({
|
|
4564
5045
|
template,
|
|
4565
|
-
annotationExample:
|
|
5046
|
+
annotationExample: described.annotationExample
|
|
4566
5047
|
});
|
|
4567
5048
|
} catch (caught) {
|
|
4568
5049
|
return errorResult(caught, "inspect");
|
|
@@ -4600,7 +5081,7 @@ function registerTemplateTools(server, context) {
|
|
|
4600
5081
|
const profile = recommendModule.profileBlockContents(block.contents ?? []);
|
|
4601
5082
|
return {
|
|
4602
5083
|
blockId: toWireIdentifier(block.id, "block"),
|
|
4603
|
-
title: boundNullableWireText(block.title,
|
|
5084
|
+
title: boundNullableWireText(block.title, MCP_WIRE_LIMITS8.labelCharacters),
|
|
4604
5085
|
profile: {
|
|
4605
5086
|
...profile,
|
|
4606
5087
|
hasAsciiDiagram: profile.hasAsciiDiagram ?? false,
|
|
@@ -4699,12 +5180,12 @@ function registerThemeTools(server, context) {
|
|
|
4699
5180
|
layouts: (inferred.layouts ?? []).slice(0, 1e3).map((layout, index) => ({
|
|
4700
5181
|
id: boundWireText(
|
|
4701
5182
|
layout.name,
|
|
4702
|
-
|
|
5183
|
+
MCP_WIRE_LIMITS8.labelCharacters,
|
|
4703
5184
|
`layout-${index + 1}`
|
|
4704
5185
|
),
|
|
4705
5186
|
name: boundWireText(
|
|
4706
5187
|
layout.label,
|
|
4707
|
-
|
|
5188
|
+
MCP_WIRE_LIMITS8.labelCharacters,
|
|
4708
5189
|
`Layout ${index + 1}`
|
|
4709
5190
|
),
|
|
4710
5191
|
description: boundWireText(layout.description ?? "")
|
|
@@ -4749,21 +5230,21 @@ function registerThemeTools(server, context) {
|
|
|
4749
5230
|
layouts: result.layouts.slice(0, 2048).map((layout, index) => ({
|
|
4750
5231
|
layoutPath: boundWireText(
|
|
4751
5232
|
layout.layoutPath,
|
|
4752
|
-
|
|
5233
|
+
MCP_WIRE_LIMITS8.pathCharacters,
|
|
4753
5234
|
`ppt/slideLayouts/slideLayout${index + 1}.xml`
|
|
4754
5235
|
),
|
|
4755
5236
|
name: boundWireText(
|
|
4756
5237
|
layout.name,
|
|
4757
|
-
|
|
5238
|
+
MCP_WIRE_LIMITS8.labelCharacters,
|
|
4758
5239
|
`Layout ${index + 1}`
|
|
4759
5240
|
),
|
|
4760
|
-
masterName: boundNullableWireText(layout.masterName,
|
|
4761
|
-
type: boundNullableWireText(layout.typeAttr,
|
|
5241
|
+
masterName: boundNullableWireText(layout.masterName, MCP_WIRE_LIMITS8.labelCharacters),
|
|
5242
|
+
type: boundNullableWireText(layout.typeAttr, MCP_WIRE_LIMITS8.labelCharacters),
|
|
4762
5243
|
slideCount: layout.slideCount,
|
|
4763
5244
|
verdict: layout.verdict,
|
|
4764
5245
|
templateId: boundNullableWireText(
|
|
4765
5246
|
layout.builtinTemplate ?? layout.customTemplate?.name,
|
|
4766
|
-
|
|
5247
|
+
MCP_WIRE_LIMITS8.labelCharacters
|
|
4767
5248
|
),
|
|
4768
5249
|
notes: boundWireTextArray(layout.notes ?? [], 1e3)
|
|
4769
5250
|
}))
|
|
@@ -4863,7 +5344,7 @@ function registerThemeTools(server, context) {
|
|
|
4863
5344
|
const payload = {
|
|
4864
5345
|
result,
|
|
4865
5346
|
theme: summarizeTheme(inferred.theme),
|
|
4866
|
-
layoutIds: (inferred.layouts ?? []).slice(0, 1e3).map((layout) => boundWireText(layout.name,
|
|
5347
|
+
layoutIds: (inferred.layouts ?? []).slice(0, 1e3).map((layout) => boundWireText(layout.name, MCP_WIRE_LIMITS8.labelCharacters)),
|
|
4867
5348
|
warnings: boundWireWarnings(inferred.warnings)
|
|
4868
5349
|
};
|
|
4869
5350
|
return {
|
|
@@ -4938,34 +5419,80 @@ function summarizeTheme(theme) {
|
|
|
4938
5419
|
const id = requireWireIdentifier(theme.id, "theme id");
|
|
4939
5420
|
return {
|
|
4940
5421
|
id,
|
|
4941
|
-
name: boundWireText(theme.name,
|
|
5422
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS8.labelCharacters, id),
|
|
4942
5423
|
description: boundWireText(theme.description ?? ""),
|
|
4943
5424
|
colors: {
|
|
4944
|
-
primary: boundWireText(theme.colors.primary,
|
|
4945
|
-
secondary: boundWireText(theme.colors.secondary,
|
|
5425
|
+
primary: boundWireText(theme.colors.primary, MCP_WIRE_LIMITS8.labelCharacters, "#000000"),
|
|
5426
|
+
secondary: boundWireText(theme.colors.secondary, MCP_WIRE_LIMITS8.labelCharacters, "#000000"),
|
|
4946
5427
|
background: boundWireText(
|
|
4947
5428
|
theme.colors.background,
|
|
4948
|
-
|
|
5429
|
+
MCP_WIRE_LIMITS8.labelCharacters,
|
|
4949
5430
|
"#ffffff"
|
|
4950
5431
|
),
|
|
4951
|
-
text: boundWireText(theme.colors.text,
|
|
4952
|
-
highlight: boundWireText(theme.colors.highlight,
|
|
5432
|
+
text: boundWireText(theme.colors.text, MCP_WIRE_LIMITS8.labelCharacters, "#000000"),
|
|
5433
|
+
highlight: boundWireText(theme.colors.highlight, MCP_WIRE_LIMITS8.labelCharacters, "#000000")
|
|
4953
5434
|
},
|
|
4954
5435
|
bodyFont: boundWireText(JSON.stringify(theme.typography.bodyFont) ?? ""),
|
|
4955
5436
|
titleFont: boundWireText(JSON.stringify(theme.typography.titleFont) ?? "")
|
|
4956
5437
|
};
|
|
4957
5438
|
}
|
|
5439
|
+
async function authoringTemplateCatalog() {
|
|
5440
|
+
const {
|
|
5441
|
+
getAvailableTemplates,
|
|
5442
|
+
TEMPLATE_AUTHORING_METADATA,
|
|
5443
|
+
TEMPLATE_INPUT_DESCRIPTORS,
|
|
5444
|
+
TEMPLATE_METADATA
|
|
5445
|
+
} = await import("@bendyline/squisq/doc");
|
|
5446
|
+
const authoring = TEMPLATE_AUTHORING_METADATA;
|
|
5447
|
+
return getAvailableTemplates().slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((id) => {
|
|
5448
|
+
const metadata = TEMPLATE_METADATA[id];
|
|
5449
|
+
const behavior = authoring[id];
|
|
5450
|
+
if (!behavior) throw new Error(`Template "${id}" has no authoring metadata`);
|
|
5451
|
+
const inputs = (TEMPLATE_INPUT_DESCRIPTORS[id] ?? []).slice(0, MCP_WIRE_LIMITS8.arrayEntries).map((input) => ({
|
|
5452
|
+
key: requireWireIdentifier(input.key, "template input key"),
|
|
5453
|
+
description: boundWireText(input.description),
|
|
5454
|
+
type: boundWireText(input.coerce ?? "string", MCP_WIRE_LIMITS8.labelCharacters, "string"),
|
|
5455
|
+
required: input.required ?? false,
|
|
5456
|
+
values: boundWireTextArray(
|
|
5457
|
+
input.values ?? [],
|
|
5458
|
+
MCP_WIRE_LIMITS8.arrayEntries,
|
|
5459
|
+
MCP_WIRE_LIMITS8.labelCharacters
|
|
5460
|
+
),
|
|
5461
|
+
valueHint: boundNullableWireText(input.valueHint, MCP_WIRE_LIMITS8.labelCharacters)
|
|
5462
|
+
}));
|
|
5463
|
+
const required = inputs.filter((input) => input.required).map((input) => `${input.key}="\u2026"`).join(" ");
|
|
5464
|
+
return {
|
|
5465
|
+
id: requireWireIdentifier(id, "template id"),
|
|
5466
|
+
label: boundWireText(metadata?.label ?? id, MCP_WIRE_LIMITS8.labelCharacters, id),
|
|
5467
|
+
description: boundWireText(metadata?.description ?? ""),
|
|
5468
|
+
inputs,
|
|
5469
|
+
...behavior,
|
|
5470
|
+
annotationExample: boundWireText(`# Heading {[${id}${required ? ` ${required}` : ""}]}`)
|
|
5471
|
+
};
|
|
5472
|
+
});
|
|
5473
|
+
}
|
|
5474
|
+
function authoringDefaultFidelity(targetFormat, goal) {
|
|
5475
|
+
if (!targetFormat) return null;
|
|
5476
|
+
if (targetFormat === "mp4" || targetFormat === "gif") return "rendered-fidelity";
|
|
5477
|
+
if (goal === "visual-polish" && (targetFormat === "pptx" || targetFormat === "pdf")) {
|
|
5478
|
+
return "rendered-fidelity";
|
|
5479
|
+
}
|
|
5480
|
+
if (targetFormat === "docx" || targetFormat === "pptx" || targetFormat === "xlsx") {
|
|
5481
|
+
return "editable-native";
|
|
5482
|
+
}
|
|
5483
|
+
return "semantic";
|
|
5484
|
+
}
|
|
4958
5485
|
function boundedFormatCapabilities() {
|
|
4959
5486
|
return MCP_FORMAT_CAPABILITIES.slice(0, 64).map((format) => {
|
|
4960
5487
|
const id = requireWireIdentifier(format.id, "format id");
|
|
4961
|
-
if (id.length >
|
|
5488
|
+
if (id.length > MCP_WIRE_LIMITS8.formatCharacters || !MCP_OUTPUT_PATTERNS.format.test(id)) {
|
|
4962
5489
|
throw new Error("Linked Squisq returned an invalid MCP format id");
|
|
4963
5490
|
}
|
|
4964
5491
|
return {
|
|
4965
5492
|
id,
|
|
4966
|
-
label: boundWireText(format.label,
|
|
5493
|
+
label: boundWireText(format.label, MCP_WIRE_LIMITS8.labelCharacters, id),
|
|
4967
5494
|
mimeType: format.mimeType,
|
|
4968
|
-
extensions: boundWireTextArray(format.extensions, 64,
|
|
5495
|
+
extensions: boundWireTextArray(format.extensions, 64, MCP_WIRE_LIMITS8.labelCharacters),
|
|
4969
5496
|
import: { ...format.import },
|
|
4970
5497
|
export: { ...format.export }
|
|
4971
5498
|
};
|
|
@@ -4981,6 +5508,7 @@ var init_agentic_tools = __esm({
|
|
|
4981
5508
|
init_preview_service();
|
|
4982
5509
|
init_error_result();
|
|
4983
5510
|
init_progress();
|
|
5511
|
+
init_revision_service();
|
|
4984
5512
|
init_output_bounds();
|
|
4985
5513
|
READ_ONLY = {
|
|
4986
5514
|
readOnlyHint: true,
|
|
@@ -5000,7 +5528,7 @@ var init_agentic_tools = __esm({
|
|
|
5000
5528
|
idempotentHint: false,
|
|
5001
5529
|
openWorldHint: false
|
|
5002
5530
|
};
|
|
5003
|
-
identifierSchema = z.string().min(1).max(
|
|
5531
|
+
identifierSchema = z.string().min(1).max(MCP_WIRE_LIMITS8.identifierCharacters).regex(MCP_OUTPUT_PATTERNS.identifier);
|
|
5004
5532
|
fidelitySchemas = {
|
|
5005
5533
|
editable: z.enum(MCP_FORMAT_FIDELITIES.docx),
|
|
5006
5534
|
pdf: z.enum(MCP_FORMAT_FIDELITIES.pdf),
|
|
@@ -5113,7 +5641,7 @@ var init_agentic_tools = __esm({
|
|
|
5113
5641
|
|
|
5114
5642
|
// src/mcp/discovery-tools.ts
|
|
5115
5643
|
import { z as z2 } from "zod";
|
|
5116
|
-
import { MCP_WIRE_LIMITS as
|
|
5644
|
+
import { MCP_WIRE_LIMITS as MCP_WIRE_LIMITS9 } from "@bendyline/docblocks/mcp";
|
|
5117
5645
|
import { DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS as DOCBLOCKS_MCP_TOOL_OUTPUT_SCHEMAS2 } from "@bendyline/docblocks/mcp/zod";
|
|
5118
5646
|
function registerDiscoveryTools(server) {
|
|
5119
5647
|
server.registerTool(
|
|
@@ -5148,9 +5676,9 @@ function registerDiscoveryTools(server) {
|
|
|
5148
5676
|
try {
|
|
5149
5677
|
const { getThemeSummaries } = await import("@bendyline/squisq/schemas");
|
|
5150
5678
|
const result = {
|
|
5151
|
-
themes: getThemeSummaries().slice(0,
|
|
5679
|
+
themes: getThemeSummaries().slice(0, MCP_WIRE_LIMITS9.arrayEntries).map((theme) => ({
|
|
5152
5680
|
id: requireWireIdentifier(theme.id, "theme id"),
|
|
5153
|
-
name: boundWireText(theme.name,
|
|
5681
|
+
name: boundWireText(theme.name, MCP_WIRE_LIMITS9.labelCharacters, theme.id),
|
|
5154
5682
|
description: boundWireText(theme.description ?? "")
|
|
5155
5683
|
}))
|
|
5156
5684
|
};
|
|
@@ -5175,9 +5703,9 @@ function registerDiscoveryTools(server) {
|
|
|
5175
5703
|
try {
|
|
5176
5704
|
const { getTransformStyleSummaries } = await import("@bendyline/squisq/transform");
|
|
5177
5705
|
const result = {
|
|
5178
|
-
styles: getTransformStyleSummaries().slice(0,
|
|
5706
|
+
styles: getTransformStyleSummaries().slice(0, MCP_WIRE_LIMITS9.arrayEntries).map((style) => ({
|
|
5179
5707
|
id: requireWireIdentifier(style.id, "transform style id"),
|
|
5180
|
-
name: boundWireText(style.name,
|
|
5708
|
+
name: boundWireText(style.name, MCP_WIRE_LIMITS9.labelCharacters, style.id),
|
|
5181
5709
|
description: boundWireText(style.description)
|
|
5182
5710
|
}))
|
|
5183
5711
|
};
|
|
@@ -5211,16 +5739,16 @@ function registerDiscoveryTools(server) {
|
|
|
5211
5739
|
function serializableCapabilities() {
|
|
5212
5740
|
return MCP_FORMAT_CAPABILITIES.slice(0, 64).map((format) => ({
|
|
5213
5741
|
id: requireWireFormat(format.id),
|
|
5214
|
-
label: boundWireText(format.label,
|
|
5742
|
+
label: boundWireText(format.label, MCP_WIRE_LIMITS9.labelCharacters, format.id),
|
|
5215
5743
|
mimeType: format.mimeType,
|
|
5216
|
-
extensions: boundWireTextArray(format.extensions, 64,
|
|
5744
|
+
extensions: boundWireTextArray(format.extensions, 64, MCP_WIRE_LIMITS9.labelCharacters),
|
|
5217
5745
|
import: { ...format.import },
|
|
5218
5746
|
export: { ...format.export }
|
|
5219
5747
|
}));
|
|
5220
5748
|
}
|
|
5221
5749
|
function requireWireFormat(value) {
|
|
5222
5750
|
requireWireIdentifier(value, "format id");
|
|
5223
|
-
if (value.length >
|
|
5751
|
+
if (value.length > MCP_WIRE_LIMITS9.formatCharacters || !MCP_OUTPUT_PATTERNS.format.test(value)) {
|
|
5224
5752
|
throw new Error("Linked Squisq returned an invalid MCP format id");
|
|
5225
5753
|
}
|
|
5226
5754
|
return value;
|
|
@@ -5345,19 +5873,21 @@ function registerAuthoringPrompts(server) {
|
|
|
5345
5873
|
function presentationPrompt(topic, style, theme, template) {
|
|
5346
5874
|
return `Create a presentation about: ${topic}
|
|
5347
5875
|
|
|
5348
|
-
1.
|
|
5349
|
-
2. Author
|
|
5350
|
-
3.
|
|
5351
|
-
4. Call
|
|
5352
|
-
5. Call preview_document and inspect
|
|
5353
|
-
6.
|
|
5876
|
+
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. 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"}".
|
|
5878
|
+
3. Keep the draft inline or stage Markdown plus assets with create_document_bundle. Do not write a temporary local Markdown file or invent a root id.
|
|
5879
|
+
4. Call validate_document with targetFormat "pptx" and repair actionable diagnostics before optimizing layouts.
|
|
5880
|
+
5. Call preview_document and inspect every bounded slide image, especially overflow and body-retention diagnostics.
|
|
5881
|
+
6. If a staged DBK needs targeted repairs, call inspect_document for block IDs and revise_document with the artifact's exact SHA-256 instead of resending the full Markdown. Only after content coverage is acceptable, replace selected \`content\` blocks with more visual recommended templates and preview again.
|
|
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.
|
|
5883
|
+
8. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
5354
5884
|
}
|
|
5355
5885
|
function videoPrompt(topic, orientation, theme, template) {
|
|
5356
5886
|
return `Create a video about: ${topic}
|
|
5357
5887
|
|
|
5358
|
-
1.
|
|
5359
|
-
2. Author concise Markdown for a ${orientation ?? "landscape"} animated sequence using theme "${theme ?? "choose from
|
|
5360
|
-
3. Call validate_document with targetFormat "mp4" and repair actionable diagnostics.
|
|
5888
|
+
1. Call get_authoring_context with targetFormat "mp4" for the complete linked-Squisq vocabulary and workflow.
|
|
5889
|
+
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. If the draft is staged, use inspect_document plus revise_document for targeted repairs instead of resending the full Markdown. Call validate_document with targetFormat "mp4" and repair actionable diagnostics.
|
|
5361
5891
|
4. Call preview_document to review representative frames.
|
|
5362
5892
|
5. Call convert_document with an mp4 target and orientation "${orientation ?? "landscape"}"; monitor progress and honor cancellation.
|
|
5363
5893
|
6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
@@ -5366,9 +5896,9 @@ function documentPrompt(topic, format, theme, template) {
|
|
|
5366
5896
|
const target = format ?? "pdf";
|
|
5367
5897
|
return `Create a professional document about: ${topic}
|
|
5368
5898
|
|
|
5369
|
-
1.
|
|
5370
|
-
2. Author structured Markdown using theme "${theme ?? "choose from
|
|
5371
|
-
3. Call validate_document with targetFormat "${target}" and repair actionable diagnostics.
|
|
5899
|
+
1. Call get_authoring_context with targetFormat "${target}" for the complete linked-Squisq vocabulary and workflow.
|
|
5900
|
+
2. Author structured Markdown using theme "${theme ?? "choose from the returned themes"}" and template "${template ?? "content until visual optimization"}". Bind annotations to headings.
|
|
5901
|
+
3. If the draft is staged, use inspect_document plus revise_document for targeted repairs instead of resending the full Markdown. Call validate_document with targetFormat "${target}" and repair actionable diagnostics.
|
|
5372
5902
|
4. Call convert_document with a ${target} target to create an immutable artifact.
|
|
5373
5903
|
5. Call preview_document and review the bounded page images.
|
|
5374
5904
|
6. Use get_conversion_report for provenance and save_artifact only when a durable file is required.`;
|
|
@@ -5410,7 +5940,12 @@ function createMcpServer(options = {}) {
|
|
|
5410
5940
|
options.shutdownDrainTimeoutMs ?? DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS
|
|
5411
5941
|
);
|
|
5412
5942
|
const artifacts = new ArtifactStore(options);
|
|
5413
|
-
const server = new McpServer(
|
|
5943
|
+
const server = new McpServer(
|
|
5944
|
+
{ name: "docblocks", version: getPackageVersion() },
|
|
5945
|
+
{
|
|
5946
|
+
instructions: "Start with get_authoring_context for one-call format, theme, transform, template, syntax, and workflow guidance. Author template annotations on headings, for example `# Heading {[content]}`; standalone `{[template]}` creates an extra heading-less block. DocBlocks MCP defaults ordinary headings to the loss-averse `content` template. Keep authored work in an inline source or stage Markdown plus assets with create_document_bundle. For targeted repairs to a staged DBK, inspect its block ids and call revise_document with the artifact's exact SHA-256 instead of resending the full Markdown. Then validate, preview, convert, and only save the final artifact when durable output is required. Never invent root ids: call list_roots and use an id exactly as returned."
|
|
5947
|
+
}
|
|
5948
|
+
);
|
|
5414
5949
|
registerAgenticTools(server, {
|
|
5415
5950
|
authority,
|
|
5416
5951
|
artifacts,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/docblocks-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Build, preview, convert, render, inspect, and automate documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"typecheck": "tsc --noEmit"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@bendyline/docblocks": "2.1.
|
|
50
|
-
"@bendyline/squisq": "2.3.
|
|
51
|
-
"@bendyline/squisq-cli": "2.3.
|
|
52
|
-
"@bendyline/squisq-formats": "2.3.
|
|
53
|
-
"@bendyline/squisq-react": "2.3.
|
|
54
|
-
"@bendyline/squisq-video": "2.2.
|
|
49
|
+
"@bendyline/docblocks": "2.1.2",
|
|
50
|
+
"@bendyline/squisq": "2.3.2",
|
|
51
|
+
"@bendyline/squisq-cli": "2.3.2",
|
|
52
|
+
"@bendyline/squisq-formats": "2.3.2",
|
|
53
|
+
"@bendyline/squisq-react": "2.3.2",
|
|
54
|
+
"@bendyline/squisq-video": "2.2.2",
|
|
55
55
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
56
56
|
"commander": "13.1.0",
|
|
57
57
|
"jszip": "3.10.1",
|