@hanna84/mcp-writing 3.6.2 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [v3.7.0](https://github.com/hannasdev/mcp-writing/compare/v3.6.2...v3.7.0)
|
|
8
|
+
|
|
9
|
+
- feat(review-bundles): redesign outline_discussion PDF profile [`#191`](https://github.com/hannasdev/mcp-writing/pull/191)
|
|
10
|
+
|
|
7
11
|
#### [v3.6.2](https://github.com/hannasdev/mcp-writing/compare/v3.6.1...v3.6.2)
|
|
8
12
|
|
|
13
|
+
> 14 May 2026
|
|
14
|
+
|
|
9
15
|
- docs(prd): close out beta-reader accountability feature [`#190`](https://github.com/hannasdev/mcp-writing/pull/190)
|
|
16
|
+
- Release 3.6.2 [`7953817`](https://github.com/hannasdev/mcp-writing/commit/79538176f55f06e61b28ed7152edfe798b06d3da)
|
|
10
17
|
|
|
11
18
|
#### [v3.6.1](https://github.com/hannasdev/mcp-writing/compare/v3.6.0...v3.6.1)
|
|
12
19
|
|
package/package.json
CHANGED
|
@@ -129,11 +129,13 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
129
129
|
tag,
|
|
130
130
|
scene_ids,
|
|
131
131
|
strictness = "warn",
|
|
132
|
-
include_scene_ids
|
|
132
|
+
include_scene_ids,
|
|
133
133
|
include_metadata_sidebar = false,
|
|
134
134
|
include_paragraph_anchors = false,
|
|
135
135
|
beta_accountability,
|
|
136
136
|
bundle_name,
|
|
137
|
+
bundle_title,
|
|
138
|
+
author_name,
|
|
137
139
|
recipient_name,
|
|
138
140
|
format = "pdf",
|
|
139
141
|
} = {}) {
|
|
@@ -333,6 +335,8 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
333
335
|
const resolvedRecipientName = profile === "beta_reader_personalized"
|
|
334
336
|
? normalizeRecipientDisplayName(recipient_name)
|
|
335
337
|
: undefined;
|
|
338
|
+
const normalizedBundleTitle = bundle_title == null ? undefined : String(bundle_title).trim() || undefined;
|
|
339
|
+
const normalizedAuthorName = author_name == null ? undefined : String(author_name).trim() || undefined;
|
|
336
340
|
|
|
337
341
|
const safeBundleName = slugifyBundleName(bundle_name || `${project_id}-${profile}`);
|
|
338
342
|
const normalizedSceneIds = Array.isArray(scene_ids)
|
|
@@ -349,7 +353,12 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
349
353
|
? Boolean(beta_accountability ?? true)
|
|
350
354
|
: false;
|
|
351
355
|
const isBetaProfile = profile === "beta_reader_personalized";
|
|
352
|
-
const
|
|
356
|
+
const isOutlineProfile = profile === "outline_discussion";
|
|
357
|
+
// Beta always suppresses scene IDs; outline_discussion defaults to false (structural overview)
|
|
358
|
+
// but can be explicitly enabled. editor_detailed defaults to true.
|
|
359
|
+
const resolvedIncludeSceneIds = isBetaProfile
|
|
360
|
+
? false
|
|
361
|
+
: Boolean(include_scene_ids ?? (isOutlineProfile ? false : true));
|
|
353
362
|
const resolvedIncludeMetadataSidebar = isBetaProfile ? false : Boolean(include_metadata_sidebar);
|
|
354
363
|
const resolvedIncludeParagraphAnchors = isBetaProfile ? false : Boolean(include_paragraph_anchors);
|
|
355
364
|
|
|
@@ -365,6 +374,8 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
365
374
|
include_paragraph_anchors: resolvedIncludeParagraphAnchors,
|
|
366
375
|
beta_accountability: resolvedBetaAccountability,
|
|
367
376
|
...(resolvedRecipientName ? { recipient_name: resolvedRecipientName } : {}),
|
|
377
|
+
...(isOutlineProfile && normalizedBundleTitle ? { bundle_title: normalizedBundleTitle } : {}),
|
|
378
|
+
...(isOutlineProfile && normalizedAuthorName ? { author_name: normalizedAuthorName } : {}),
|
|
368
379
|
},
|
|
369
380
|
},
|
|
370
381
|
ordering: rows.map(row => ({
|
|
@@ -5,6 +5,12 @@ import matter from "gray-matter";
|
|
|
5
5
|
import PDFDocument from "pdfkit";
|
|
6
6
|
import { ReviewBundlePlanError, normalizeRecipientDisplayName } from "./review-bundles-planner.js";
|
|
7
7
|
|
|
8
|
+
function prettifyProjectId(projectId) {
|
|
9
|
+
// "universe-1/book-1-the-lamb" -> "Book 1 The Lamb"
|
|
10
|
+
const slug = String(projectId ?? "").split("/").pop() ?? "";
|
|
11
|
+
return slug.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
function escapeMarkdown(text) {
|
|
9
15
|
return String(text ?? "")
|
|
10
16
|
.replace(/\\/g, "\\\\")
|
|
@@ -604,14 +610,16 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
604
610
|
: Boolean(plan.resolved_scope?.options?.include_scene_ids);
|
|
605
611
|
const syncDir = syncDirOpt ?? process.env.WRITING_SYNC_DIR ?? null;
|
|
606
612
|
const isBetaProfile = profile === "beta_reader_personalized";
|
|
613
|
+
const isOutlineProfile = profile === "outline_discussion";
|
|
607
614
|
const proseFontSize = isBetaProfile ? 8 : 10;
|
|
608
615
|
const proseLineGap = isBetaProfile ? 1.6 : 3;
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
const italicFont = profile === "beta_reader_personalized" ? "Times-Italic" : "Helvetica-Oblique";
|
|
616
|
+
// outline_discussion and beta_reader_personalized both use Times for a professional editorial feel.
|
|
617
|
+
const bodyFont = (isBetaProfile || isOutlineProfile) ? "Times-Roman" : "Helvetica";
|
|
618
|
+
const coverHeadingFont = (isBetaProfile || isOutlineProfile) ? "Times-Bold" : "Helvetica-Bold";
|
|
619
|
+
const italicFont = (isBetaProfile || isOutlineProfile) ? "Times-Italic" : "Helvetica-Oblique";
|
|
614
620
|
const metaFont = italicFont;
|
|
621
|
+
// Beta uses bodyFont (non-bold); outline uses italicFont (elegant, lighter weight); editor uses Bold.
|
|
622
|
+
const sceneHeadingFont = isBetaProfile ? bodyFont : (isOutlineProfile ? italicFont : coverHeadingFont);
|
|
615
623
|
|
|
616
624
|
const sceneIds = plan.ordering.map(row => row.scene_id);
|
|
617
625
|
const rows = loadBundleSceneRowsWithTags(dbHandle, plan.resolved_scope.project_id, sceneIds);
|
|
@@ -627,6 +635,8 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
627
635
|
const fingerprintSeedHash = fingerprintSeed ? buildFingerprintSeedHash(fingerprintSeed) : null;
|
|
628
636
|
const pageTokens = [];
|
|
629
637
|
let pageNumber = 0;
|
|
638
|
+
let outlineCoverCompleted = false;
|
|
639
|
+
let outlineContentPageNumber = 0;
|
|
630
640
|
|
|
631
641
|
const pdfOptions = profile === "beta_reader_personalized"
|
|
632
642
|
? {
|
|
@@ -635,6 +645,13 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
635
645
|
margins: { top: 64, right: 58, bottom: 96, left: 58 },
|
|
636
646
|
autoFirstPage: false,
|
|
637
647
|
}
|
|
648
|
+
: profile === "outline_discussion"
|
|
649
|
+
? {
|
|
650
|
+
size: "Letter",
|
|
651
|
+
// Extra bottom margin reserves space for centered page number footer.
|
|
652
|
+
margins: { top: 72, right: 72, bottom: 90, left: 72 },
|
|
653
|
+
autoFirstPage: false,
|
|
654
|
+
}
|
|
638
655
|
: {
|
|
639
656
|
size: "Letter",
|
|
640
657
|
margin: 50,
|
|
@@ -671,7 +688,46 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
671
688
|
doc.x = previousX;
|
|
672
689
|
doc.y = previousY;
|
|
673
690
|
};
|
|
674
|
-
|
|
691
|
+
|
|
692
|
+
const drawOutlineHeaderFooter = () => {
|
|
693
|
+
if (!isOutlineProfile) return;
|
|
694
|
+
// Do not render running chrome while the cover is still laying out.
|
|
695
|
+
// Very long title/author text can auto-add pages before explicit content starts.
|
|
696
|
+
if (!outlineCoverCompleted) return;
|
|
697
|
+
outlineContentPageNumber += 1;
|
|
698
|
+
const contentPageNum = outlineContentPageNumber;
|
|
699
|
+
const previousX = doc.x;
|
|
700
|
+
const previousY = doc.y;
|
|
701
|
+
// Capture the full text-rendering state before drawing. When a doc.text()
|
|
702
|
+
// call triggers a page break mid-flow, PDFKit re-applies its internal
|
|
703
|
+
// _font/_fontSize to the new page after pageAdded fires. Without restoring
|
|
704
|
+
// these, any title or heading that spans the break renders in the wrong font.
|
|
705
|
+
const prevFontName = doc._font?.name ?? bodyFont;
|
|
706
|
+
const prevFontSize = doc._fontSize ?? proseFontSize;
|
|
707
|
+
const prevFillColor = doc._fillColor ?? "#000000";
|
|
708
|
+
doc.save();
|
|
709
|
+
// Running header: "Outline Overview" — centered, small italic, muted
|
|
710
|
+
doc.font("Times-Italic").fontSize(8).fillColor("#888888");
|
|
711
|
+
const headerText = "Outline Overview";
|
|
712
|
+
const headerX = (doc.page.width - doc.widthOfString(headerText)) / 2;
|
|
713
|
+
doc.text(headerText, headerX, 34, { lineBreak: false });
|
|
714
|
+
// Centered page number in footer
|
|
715
|
+
doc.font("Times-Roman").fontSize(9).fillColor("#888888");
|
|
716
|
+
const pageText = String(contentPageNum);
|
|
717
|
+
const pageX = (doc.page.width - doc.widthOfString(pageText)) / 2;
|
|
718
|
+
doc.text(pageText, pageX, doc.page.height - 48, { lineBreak: false });
|
|
719
|
+
doc.restore();
|
|
720
|
+
// Restore font, size, and fill color. doc.restore() syncs the PDF graphics
|
|
721
|
+
// state operator stack but not PDFKit's internal JS tracking variables.
|
|
722
|
+
doc.font(prevFontName).fontSize(prevFontSize).fillColor(prevFillColor);
|
|
723
|
+
doc.x = previousX;
|
|
724
|
+
doc.y = previousY;
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
doc.on("pageAdded", () => {
|
|
728
|
+
drawAccountabilityFooter();
|
|
729
|
+
drawOutlineHeaderFooter();
|
|
730
|
+
});
|
|
675
731
|
|
|
676
732
|
// Register listeners before any content is written so render-time errors
|
|
677
733
|
// always reject the returned Promise.
|
|
@@ -703,20 +759,60 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
703
759
|
|
|
704
760
|
try {
|
|
705
761
|
doc.addPage();
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
762
|
+
|
|
763
|
+
if (isOutlineProfile) {
|
|
764
|
+
// Clean editorial cover: title, optional author, rule, document type label.
|
|
765
|
+
const textWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
766
|
+
const bundleTitle = plan.resolved_scope.options.bundle_title
|
|
767
|
+
?? prettifyProjectId(plan.resolved_scope.project_id);
|
|
768
|
+
const authorName = plan.resolved_scope.options.author_name ?? null;
|
|
769
|
+
// Position title roughly 35% down the page for a balanced layout.
|
|
770
|
+
const coverTitleY = doc.page.height * 0.35;
|
|
771
|
+
doc.fontSize(28).font("Times-Bold").fillColor("#000000");
|
|
772
|
+
doc.text(bundleTitle, doc.page.margins.left, coverTitleY, { width: textWidth, align: "center" });
|
|
773
|
+
doc.moveDown(1.0);
|
|
774
|
+
if (authorName) {
|
|
775
|
+
doc.fontSize(14).font("Times-Roman");
|
|
776
|
+
doc.text(authorName, { width: textWidth, align: "center" });
|
|
777
|
+
doc.moveDown(1.0);
|
|
778
|
+
} else {
|
|
779
|
+
doc.moveDown(0.9);
|
|
780
|
+
}
|
|
781
|
+
// Hairline rule
|
|
782
|
+
const ruleY = doc.y;
|
|
783
|
+
doc.moveTo(doc.page.margins.left, ruleY)
|
|
784
|
+
.lineTo(doc.page.margins.left + textWidth, ruleY)
|
|
785
|
+
.strokeColor("#888888")
|
|
786
|
+
.lineWidth(0.5)
|
|
787
|
+
.stroke();
|
|
788
|
+
doc.moveDown(0.8);
|
|
789
|
+
// Document type label
|
|
790
|
+
doc.fontSize(11).font("Times-Italic").fillColor("#888888");
|
|
791
|
+
doc.text("Outline Overview", { width: textWidth, align: "center" });
|
|
792
|
+
doc.moveDown(0.3);
|
|
793
|
+
doc.fontSize(9).font("Times-Roman").fillColor("#777777");
|
|
794
|
+
doc.text(`Generated: ${effectiveGeneratedAt}`, { width: textWidth, align: "center" });
|
|
795
|
+
doc.fillColor("#000000");
|
|
796
|
+
outlineCoverCompleted = true;
|
|
797
|
+
// Start scene content on a fresh page so the cover is always standalone
|
|
798
|
+
// and the pageAdded event fires to draw the running header + footer.
|
|
799
|
+
doc.addPage();
|
|
715
800
|
} else {
|
|
716
|
-
|
|
717
|
-
doc.
|
|
801
|
+
const coverLabel = `Review Bundle: ${plan.resolved_scope.project_id}`;
|
|
802
|
+
doc.fontSize(isBetaProfile ? 11 : 24).font(coverHeadingFont).text(coverLabel, { align: "left" });
|
|
803
|
+
doc.moveDown(isBetaProfile ? 0.2 : 0.5);
|
|
804
|
+
doc.fontSize(11).font(bodyFont);
|
|
805
|
+
if (profile !== "beta_reader_personalized") {
|
|
806
|
+
doc.text(`Profile: ${profile}`, { align: "left" });
|
|
807
|
+
}
|
|
808
|
+
if (profile === "beta_reader_personalized") {
|
|
809
|
+
doc.text(`Recipient: ${recipientDisplayName}`, { align: "left" });
|
|
810
|
+
} else {
|
|
811
|
+
doc.text(`Generated: ${effectiveGeneratedAt}`, { align: "left" });
|
|
812
|
+
doc.text(`Scenes: ${plan.summary.scene_count}`, { align: "left" });
|
|
813
|
+
}
|
|
814
|
+
doc.moveDown();
|
|
718
815
|
}
|
|
719
|
-
doc.moveDown();
|
|
720
816
|
|
|
721
817
|
if (profile === "beta_reader_personalized") {
|
|
722
818
|
doc.fontSize(12).font("Times-Bold").text("Usage Notice", { align: "left" });
|
|
@@ -744,20 +840,38 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
744
840
|
doc.moveDown(1.0);
|
|
745
841
|
}
|
|
746
842
|
|
|
747
|
-
//
|
|
748
|
-
|
|
843
|
+
// For outline_discussion: chapter dividers when the chapter changes.
|
|
844
|
+
if (isOutlineProfile && scene.chapter != null) {
|
|
845
|
+
if (!prevScene || prevScene.chapter !== scene.chapter) {
|
|
846
|
+
if (sceneIndex > 0) doc.moveDown(1.2);
|
|
847
|
+
const textWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
848
|
+
const chapterLabel = `Chapter ${scene.chapter}`;
|
|
849
|
+
doc.fontSize(10).font("Times-Bold").fillColor("#000000");
|
|
850
|
+
doc.text(chapterLabel, { width: textWidth });
|
|
851
|
+
doc.moveDown(0.25);
|
|
852
|
+
doc.moveTo(doc.page.margins.left, doc.y)
|
|
853
|
+
.lineTo(doc.page.margins.left + textWidth, doc.y)
|
|
854
|
+
.strokeColor("#cccccc").lineWidth(0.5).stroke();
|
|
855
|
+
doc.moveDown(0.6);
|
|
856
|
+
} else if (sceneIndex > 0) {
|
|
857
|
+
doc.moveDown(1.2);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Skip title for epigraphs in beta and outline profiles.
|
|
862
|
+
const isEpigraph = (isBetaProfile || isOutlineProfile) && isEpigraphScene(scene);
|
|
749
863
|
if (!isEpigraph) {
|
|
750
|
-
doc.fontSize(isBetaProfile ? 13 : 14).font(sceneHeadingFont);
|
|
864
|
+
doc.fontSize(isBetaProfile ? 13 : isOutlineProfile ? 12 : 14).font(sceneHeadingFont);
|
|
751
865
|
let heading = scene.title || scene.scene_id;
|
|
752
866
|
if (includeSceneIds) {
|
|
753
867
|
heading += ` [${scene.scene_id}]`;
|
|
754
868
|
}
|
|
755
869
|
doc.text(heading, { align: isBetaProfile ? "center" : "left" });
|
|
756
|
-
doc.moveDown(isBetaProfile ? 1.6 : 0.2);
|
|
870
|
+
doc.moveDown(isBetaProfile ? 1.6 : isOutlineProfile ? 0.5 : 0.2);
|
|
757
871
|
}
|
|
758
872
|
|
|
759
873
|
const metaParts = [];
|
|
760
|
-
if (profile !== "beta_reader_personalized") {
|
|
874
|
+
if (profile !== "beta_reader_personalized" && !isEpigraph) {
|
|
761
875
|
if (scene.pov) metaParts.push(`POV: ${scene.pov}`);
|
|
762
876
|
if (scene.save_the_cat_beat) metaParts.push(`Beat: ${scene.save_the_cat_beat}`);
|
|
763
877
|
}
|
|
@@ -766,17 +880,17 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
766
880
|
doc.fontSize(9).font(metaFont);
|
|
767
881
|
doc.text(metaParts.join(" • "), { align: "left", width: metaWidth });
|
|
768
882
|
doc.font(bodyFont);
|
|
769
|
-
doc.moveDown(0.2);
|
|
883
|
+
doc.moveDown(isOutlineProfile ? 0.4 : 0.2);
|
|
770
884
|
}
|
|
771
885
|
|
|
772
|
-
if (profile === "outline_discussion" && scene.logline) {
|
|
773
|
-
doc.fontSize(10).font(
|
|
886
|
+
if (profile === "outline_discussion" && !isEpigraph && scene.logline) {
|
|
887
|
+
doc.fontSize(10).font(bodyFont);
|
|
774
888
|
const textWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
775
|
-
doc.text(
|
|
776
|
-
doc.moveDown(0.
|
|
889
|
+
doc.text(scene.logline, { align: "left", width: textWidth, lineGap: 2 });
|
|
890
|
+
doc.moveDown(0.6);
|
|
777
891
|
}
|
|
778
892
|
|
|
779
|
-
if (profile === "editor_detailed" || profile === "beta_reader_personalized") {
|
|
893
|
+
if (profile === "editor_detailed" || profile === "beta_reader_personalized" || (isOutlineProfile && isEpigraph)) {
|
|
780
894
|
let prose = "";
|
|
781
895
|
const resolved = readProse(scene.file_path, { syncDir });
|
|
782
896
|
if (resolved === null) {
|
|
@@ -810,16 +924,37 @@ export function renderReviewBundlePdfWithMetadata(dbHandle, plan, { generatedAt,
|
|
|
810
924
|
}
|
|
811
925
|
|
|
812
926
|
const textWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
927
|
+
if (isOutlineProfile && isEpigraph) {
|
|
928
|
+
// Epigraphs: centered, italic, narrower column with extra breathing room.
|
|
929
|
+
const epigraphWidth = Math.round(textWidth * 0.68);
|
|
930
|
+
const epigraphIndent = Math.round((textWidth - epigraphWidth) / 2);
|
|
931
|
+
doc.moveDown(1.5);
|
|
932
|
+
const savedX = doc.x;
|
|
933
|
+
doc.x = doc.page.margins.left + epigraphIndent;
|
|
934
|
+
renderProseWithInlineEmphasis(doc, prose, {
|
|
935
|
+
bodyFont: italicFont,
|
|
936
|
+
italicFont: bodyFont,
|
|
937
|
+
fontSize: proseFontSize,
|
|
938
|
+
align: "left",
|
|
939
|
+
width: epigraphWidth,
|
|
940
|
+
lineGap: proseLineGap,
|
|
941
|
+
paragraphGap: 0,
|
|
942
|
+
blankLineMoveDown: 0.65,
|
|
943
|
+
});
|
|
944
|
+
doc.x = savedX;
|
|
945
|
+
doc.moveDown(1.5);
|
|
946
|
+
} else {
|
|
947
|
+
renderProseWithInlineEmphasis(doc, prose, {
|
|
948
|
+
bodyFont,
|
|
949
|
+
italicFont,
|
|
950
|
+
fontSize: proseFontSize,
|
|
951
|
+
align: "left",
|
|
952
|
+
width: textWidth,
|
|
953
|
+
lineGap: proseLineGap,
|
|
954
|
+
paragraphGap: 0,
|
|
955
|
+
blankLineMoveDown: isBetaProfile ? 0.15 : 0.65,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
823
958
|
}
|
|
824
959
|
|
|
825
960
|
doc.moveDown(0.5);
|
|
@@ -32,12 +32,14 @@ export function registerReviewBundleTools(s, {
|
|
|
32
32
|
tag: z.string().optional().describe("Optional tag filter (exact match)."),
|
|
33
33
|
scene_ids: z.array(z.string()).optional().describe("Optional explicit scene_id allowlist. Intersects with other filters."),
|
|
34
34
|
strictness: z.enum(REVIEW_BUNDLE_STRICTNESS).optional().describe("Strictness mode: warn (default) or fail."),
|
|
35
|
-
include_scene_ids: z.boolean().optional().describe("Rendering option (default true). Echoed in resolved_scope.options for downstream rendering; does not change planning results."),
|
|
35
|
+
include_scene_ids: z.boolean().optional().describe("Rendering option (default true for editor_detailed; false for outline_discussion). beta_reader_personalized always resolves this to false, even when true is passed. Echoed in resolved_scope.options for downstream rendering; does not change planning results."),
|
|
36
36
|
include_metadata_sidebar: z.boolean().optional().describe("Rendering option (default false). Echoed in resolved_scope.options for downstream rendering; does not change planning results."),
|
|
37
37
|
include_paragraph_anchors: z.boolean().optional().describe("Rendering option (default false). Echoed in resolved_scope.options for downstream rendering; does not change planning results."),
|
|
38
38
|
recipient_name: z.string().optional().describe("Optional recipient display name for beta_reader_personalized profile."),
|
|
39
39
|
beta_accountability: z.boolean().optional().describe("Enable accountability footer + fingerprint metadata for beta_reader_personalized output (default true for beta profile)."),
|
|
40
40
|
bundle_name: z.string().optional().describe("Optional output bundle base name override (slugified in planned outputs)."),
|
|
41
|
+
bundle_title: z.string().optional().describe("Optional book or document title for the cover page (outline_discussion PDF only)."),
|
|
42
|
+
author_name: z.string().optional().describe("Optional author name for the cover page (outline_discussion PDF only)."),
|
|
41
43
|
format: z.enum(["pdf", "markdown", "both"]).optional().describe("Planned output format: pdf (default), markdown, or both. Affects planned_outputs filenames only; preview_review_bundle does not render artifacts."),
|
|
42
44
|
},
|
|
43
45
|
async ({
|
|
@@ -49,12 +51,14 @@ export function registerReviewBundleTools(s, {
|
|
|
49
51
|
tag,
|
|
50
52
|
scene_ids,
|
|
51
53
|
strictness = "warn",
|
|
52
|
-
include_scene_ids
|
|
54
|
+
include_scene_ids,
|
|
53
55
|
include_metadata_sidebar = false,
|
|
54
56
|
include_paragraph_anchors = false,
|
|
55
57
|
recipient_name,
|
|
56
58
|
beta_accountability,
|
|
57
59
|
bundle_name,
|
|
60
|
+
bundle_title,
|
|
61
|
+
author_name,
|
|
58
62
|
format = "pdf",
|
|
59
63
|
}) => {
|
|
60
64
|
const projectIdCheck = validateProjectId(project_id);
|
|
@@ -78,6 +82,8 @@ export function registerReviewBundleTools(s, {
|
|
|
78
82
|
recipient_name,
|
|
79
83
|
beta_accountability,
|
|
80
84
|
bundle_name,
|
|
85
|
+
bundle_title,
|
|
86
|
+
author_name,
|
|
81
87
|
format,
|
|
82
88
|
});
|
|
83
89
|
return jsonResponse({
|
|
@@ -120,12 +126,14 @@ export function registerReviewBundleTools(s, {
|
|
|
120
126
|
tag: z.string().optional().describe("Optional tag filter (exact match)."),
|
|
121
127
|
scene_ids: z.array(z.string()).optional().describe("Optional explicit scene_id allowlist. Intersects with other filters."),
|
|
122
128
|
strictness: z.enum(REVIEW_BUNDLE_STRICTNESS).optional().describe("Strictness mode: warn (default) or fail."),
|
|
123
|
-
include_scene_ids: z.boolean().optional().describe("Include scene IDs in headings (default true). Applies to both PDF and markdown."),
|
|
129
|
+
include_scene_ids: z.boolean().optional().describe("Include scene IDs in headings (default true for editor_detailed; false for outline_discussion). beta_reader_personalized always resolves this to false, even when true is passed. Applies to both PDF and markdown."),
|
|
124
130
|
include_metadata_sidebar: z.boolean().optional().describe("Include metadata sidebar in markdown output (default false). Markdown only — no effect on PDF."),
|
|
125
131
|
include_paragraph_anchors: z.boolean().optional().describe("Include paragraph anchors in markdown output (default false). Markdown only — no effect on PDF."),
|
|
126
132
|
recipient_name: z.string().optional().describe("Optional recipient display name for beta_reader_personalized profile."),
|
|
127
133
|
beta_accountability: z.boolean().optional().describe("Enable accountability footer + fingerprint metadata for beta_reader_personalized output (default true for beta profile)."),
|
|
128
134
|
bundle_name: z.string().optional().describe("Optional output bundle base name override (slugified in filenames)."),
|
|
135
|
+
bundle_title: z.string().optional().describe("Optional book or document title for the cover page (outline_discussion PDF only)."),
|
|
136
|
+
author_name: z.string().optional().describe("Optional author name for the cover page (outline_discussion PDF only)."),
|
|
129
137
|
source_commit: z.string().optional().describe("Optional explicit source commit for provenance. Defaults to current HEAD when available."),
|
|
130
138
|
format: z.enum(["pdf", "markdown", "both"]).optional().describe("Output format: pdf (default), markdown, or both."),
|
|
131
139
|
},
|
|
@@ -139,12 +147,14 @@ export function registerReviewBundleTools(s, {
|
|
|
139
147
|
tag,
|
|
140
148
|
scene_ids,
|
|
141
149
|
strictness = "warn",
|
|
142
|
-
include_scene_ids
|
|
150
|
+
include_scene_ids,
|
|
143
151
|
include_metadata_sidebar = false,
|
|
144
152
|
include_paragraph_anchors = false,
|
|
145
153
|
recipient_name,
|
|
146
154
|
beta_accountability,
|
|
147
155
|
bundle_name,
|
|
156
|
+
bundle_title,
|
|
157
|
+
author_name,
|
|
148
158
|
source_commit,
|
|
149
159
|
format = "pdf",
|
|
150
160
|
}) => {
|
|
@@ -182,6 +192,8 @@ export function registerReviewBundleTools(s, {
|
|
|
182
192
|
recipient_name,
|
|
183
193
|
beta_accountability,
|
|
184
194
|
bundle_name,
|
|
195
|
+
bundle_title,
|
|
196
|
+
author_name,
|
|
185
197
|
format,
|
|
186
198
|
});
|
|
187
199
|
|