@hanna84/mcp-writing 1.16.2 → 1.17.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 +10 -0
- package/index.js +8 -2
- package/package.json +2 -1
- package/review-bundles.js +111 -4
- package/scripts/manual/run_create_review_bundle.js +47 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,21 @@ 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
|
+
#### [v1.17.0](https://github.com/hannasdev/mcp-writing.git
|
|
8
|
+
/compare/v1.16.2...v1.17.0)
|
|
9
|
+
|
|
10
|
+
- feat: add beta_reader_personalized review bundle profile [`#77`](https://github.com/hannasdev/mcp-writing.git
|
|
11
|
+
/pull/77)
|
|
12
|
+
|
|
7
13
|
#### [v1.16.2](https://github.com/hannasdev/mcp-writing.git
|
|
8
14
|
/compare/v1.16.1...v1.16.2)
|
|
9
15
|
|
|
16
|
+
> 25 April 2026
|
|
17
|
+
|
|
10
18
|
- docs: close out review bundles M1 and add troubleshooting guidance [`#76`](https://github.com/hannasdev/mcp-writing.git
|
|
11
19
|
/pull/76)
|
|
20
|
+
- Release 1.16.2 [`557623a`](https://github.com/hannasdev/mcp-writing.git
|
|
21
|
+
/commit/557623afb18e4f5f5dbec565eee1cbd481051275)
|
|
12
22
|
|
|
13
23
|
#### [v1.16.1](https://github.com/hannasdev/mcp-writing.git
|
|
14
24
|
/compare/v1.16.0...v1.16.1)
|
package/index.js
CHANGED
|
@@ -1352,7 +1352,7 @@ function createMcpServer() {
|
|
|
1352
1352
|
"Dry-run planning tool for review bundles. Resolves scene scope, deterministic ordering, warnings, and planned output filenames without writing files. Note: include_scene_ids/include_metadata_sidebar/include_paragraph_anchors are advisory placeholders in Phase 4A.1 and do not alter planning semantics yet.",
|
|
1353
1353
|
{
|
|
1354
1354
|
project_id: z.string().describe("Project ID to scope the review bundle (e.g. 'test-novel')."),
|
|
1355
|
-
profile: z.enum(REVIEW_BUNDLE_PROFILES).describe("Bundle profile: outline_discussion or
|
|
1355
|
+
profile: z.enum(REVIEW_BUNDLE_PROFILES).describe("Bundle profile: outline_discussion, editor_detailed, or beta_reader_personalized."),
|
|
1356
1356
|
part: z.number().int().optional().describe("Optional part filter."),
|
|
1357
1357
|
chapter: z.number().int().optional().describe("Optional chapter filter."),
|
|
1358
1358
|
tag: z.string().optional().describe("Optional tag filter (exact match)."),
|
|
@@ -1361,6 +1361,7 @@ function createMcpServer() {
|
|
|
1361
1361
|
include_scene_ids: z.boolean().optional().describe("Advisory placeholder for later rendering behavior (default true). Included in preview output options, but does not change planning results in Phase 4A.1."),
|
|
1362
1362
|
include_metadata_sidebar: z.boolean().optional().describe("Advisory placeholder for later rendering behavior (default false). Included in preview output options, but does not change planning results in Phase 4A.1."),
|
|
1363
1363
|
include_paragraph_anchors: z.boolean().optional().describe("Advisory placeholder for later rendering behavior (default false). Included in preview output options, but does not change planning results in Phase 4A.1."),
|
|
1364
|
+
recipient_name: z.string().optional().describe("Optional recipient display name for beta_reader_personalized profile."),
|
|
1364
1365
|
bundle_name: z.string().optional().describe("Optional output bundle base name override (slugified in planned outputs)."),
|
|
1365
1366
|
},
|
|
1366
1367
|
async ({
|
|
@@ -1374,6 +1375,7 @@ function createMcpServer() {
|
|
|
1374
1375
|
include_scene_ids = true,
|
|
1375
1376
|
include_metadata_sidebar = false,
|
|
1376
1377
|
include_paragraph_anchors = false,
|
|
1378
|
+
recipient_name,
|
|
1377
1379
|
bundle_name,
|
|
1378
1380
|
}) => {
|
|
1379
1381
|
const projectIdCheck = validateProjectId(project_id);
|
|
@@ -1393,6 +1395,7 @@ function createMcpServer() {
|
|
|
1393
1395
|
include_scene_ids,
|
|
1394
1396
|
include_metadata_sidebar,
|
|
1395
1397
|
include_paragraph_anchors,
|
|
1398
|
+
recipient_name,
|
|
1396
1399
|
bundle_name,
|
|
1397
1400
|
});
|
|
1398
1401
|
return jsonResponse(plan);
|
|
@@ -1414,7 +1417,7 @@ function createMcpServer() {
|
|
|
1414
1417
|
"Generate markdown review bundle artifacts from planned scene scope. Writes files only under output_dir and returns manifest/provenance details.",
|
|
1415
1418
|
{
|
|
1416
1419
|
project_id: z.string().describe("Project ID to scope the review bundle (e.g. 'test-novel')."),
|
|
1417
|
-
profile: z.enum(REVIEW_BUNDLE_PROFILES).describe("Bundle profile: outline_discussion or
|
|
1420
|
+
profile: z.enum(REVIEW_BUNDLE_PROFILES).describe("Bundle profile: outline_discussion, editor_detailed, or beta_reader_personalized."),
|
|
1418
1421
|
output_dir: z.string().describe("Directory path to write bundle artifacts into."),
|
|
1419
1422
|
part: z.number().int().optional().describe("Optional part filter."),
|
|
1420
1423
|
chapter: z.number().int().optional().describe("Optional chapter filter."),
|
|
@@ -1424,6 +1427,7 @@ function createMcpServer() {
|
|
|
1424
1427
|
include_scene_ids: z.boolean().optional().describe("Include scene IDs in markdown headings (default true)."),
|
|
1425
1428
|
include_metadata_sidebar: z.boolean().optional().describe("Include metadata sidebar in markdown output (default false)."),
|
|
1426
1429
|
include_paragraph_anchors: z.boolean().optional().describe("Include paragraph anchors in markdown output (default false)."),
|
|
1430
|
+
recipient_name: z.string().optional().describe("Optional recipient display name for beta_reader_personalized profile."),
|
|
1427
1431
|
bundle_name: z.string().optional().describe("Optional output bundle base name override (slugified in filenames)."),
|
|
1428
1432
|
source_commit: z.string().optional().describe("Optional explicit source commit for provenance. Defaults to current HEAD when available."),
|
|
1429
1433
|
},
|
|
@@ -1439,6 +1443,7 @@ function createMcpServer() {
|
|
|
1439
1443
|
include_scene_ids = true,
|
|
1440
1444
|
include_metadata_sidebar = false,
|
|
1441
1445
|
include_paragraph_anchors = false,
|
|
1446
|
+
recipient_name,
|
|
1442
1447
|
bundle_name,
|
|
1443
1448
|
source_commit,
|
|
1444
1449
|
}) => {
|
|
@@ -1472,6 +1477,7 @@ function createMcpServer() {
|
|
|
1472
1477
|
include_scene_ids,
|
|
1473
1478
|
include_metadata_sidebar,
|
|
1474
1479
|
include_paragraph_anchors,
|
|
1480
|
+
recipient_name,
|
|
1475
1481
|
bundle_name,
|
|
1476
1482
|
});
|
|
1477
1483
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanna84/mcp-writing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "MCP service for AI-assisted reasoning and editing on long-form fiction projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"manual:test": "node scripts/manual/test.mjs",
|
|
30
30
|
"manual:scenarios": "node scripts/manual/test-scenarios.mjs",
|
|
31
31
|
"manual:merge-beta-test": "node scripts/manual/run_mcp_test.js",
|
|
32
|
+
"manual:review-bundle": "node scripts/manual/run_create_review_bundle.js",
|
|
32
33
|
"setup:openclaw-env": "sh scripts/setup-openclaw-env.sh",
|
|
33
34
|
"release": "release-it",
|
|
34
35
|
"lint": "eslint index.js importer.js db.js sync.js metadata-lint.js scripts/",
|
package/review-bundles.js
CHANGED
|
@@ -4,7 +4,7 @@ import matter from "gray-matter";
|
|
|
4
4
|
|
|
5
5
|
const MAX_SORT_VALUE = Number.MAX_SAFE_INTEGER;
|
|
6
6
|
|
|
7
|
-
export const REVIEW_BUNDLE_PROFILES = ["outline_discussion", "editor_detailed"];
|
|
7
|
+
export const REVIEW_BUNDLE_PROFILES = ["outline_discussion", "editor_detailed", "beta_reader_personalized"];
|
|
8
8
|
export const REVIEW_BUNDLE_STRICTNESS = ["warn", "fail"];
|
|
9
9
|
|
|
10
10
|
export class ReviewBundlePlanError extends Error {
|
|
@@ -63,6 +63,62 @@ function escapeMarkdown(text) {
|
|
|
63
63
|
.replace(/([*_`\[\]#])/g, "\\$1");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function normalizeRecipientDisplayName(recipientName) {
|
|
67
|
+
const normalized = String(recipientName ?? "")
|
|
68
|
+
.replace(/[\x00-\x1f\x7f]+/g, " ")
|
|
69
|
+
.replace(/\s+/g, " ")
|
|
70
|
+
.trim()
|
|
71
|
+
.slice(0, 100);
|
|
72
|
+
|
|
73
|
+
return normalized || "Beta Reader";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function renderBetaNoticeMarkdown({ projectId, recipientName }) {
|
|
77
|
+
const displayName = normalizeRecipientDisplayName(recipientName);
|
|
78
|
+
return [
|
|
79
|
+
"# Non-Distribution Notice",
|
|
80
|
+
"",
|
|
81
|
+
`This review packet is prepared for ${escapeMarkdown(displayName)} for private beta-reading purposes only.`,
|
|
82
|
+
"",
|
|
83
|
+
"Please do not distribute, repost, or share this material without explicit author permission.",
|
|
84
|
+
"",
|
|
85
|
+
"This notice is informational only and is not legal advice.",
|
|
86
|
+
"",
|
|
87
|
+
`Project: ${escapeMarkdown(projectId)}`,
|
|
88
|
+
].join("\n") + "\n";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderBetaFeedbackFormMarkdown({ projectId, recipientName, generatedAt }) {
|
|
92
|
+
const displayName = normalizeRecipientDisplayName(recipientName);
|
|
93
|
+
const feedbackDate = String(generatedAt ?? new Date().toISOString()).slice(0, 10);
|
|
94
|
+
return [
|
|
95
|
+
"# Beta Reader Feedback Form",
|
|
96
|
+
"",
|
|
97
|
+
`- Project: ${escapeMarkdown(projectId)}`,
|
|
98
|
+
`- Reader: ${escapeMarkdown(displayName)}`,
|
|
99
|
+
`- Date: ${feedbackDate}`,
|
|
100
|
+
"",
|
|
101
|
+
"## Big-Picture Questions",
|
|
102
|
+
"",
|
|
103
|
+
"1. Which sections felt most compelling, and why?",
|
|
104
|
+
"2. Where did pacing feel slow, rushed, or unclear?",
|
|
105
|
+
"3. Were any character motivations confusing or unconvincing?",
|
|
106
|
+
"",
|
|
107
|
+
"## Scene-Level Notes",
|
|
108
|
+
"",
|
|
109
|
+
"Use scene IDs when possible.",
|
|
110
|
+
"",
|
|
111
|
+
"- Scene ID:",
|
|
112
|
+
"- Comment:",
|
|
113
|
+
"- Severity (nit / moderate / major):",
|
|
114
|
+
"",
|
|
115
|
+
"## Final Thoughts",
|
|
116
|
+
"",
|
|
117
|
+
"- What should be prioritized in the next revision?",
|
|
118
|
+
"- Any continuity concerns to flag?",
|
|
119
|
+
].join("\n") + "\n";
|
|
120
|
+
}
|
|
121
|
+
|
|
66
122
|
function resolveOutputFilePath(outputDir, fileName) {
|
|
67
123
|
const normalizedOutputDir = path.resolve(outputDir);
|
|
68
124
|
const target = path.resolve(normalizedOutputDir, fileName);
|
|
@@ -125,6 +181,7 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
125
181
|
include_metadata_sidebar = false,
|
|
126
182
|
include_paragraph_anchors = false,
|
|
127
183
|
bundle_name,
|
|
184
|
+
recipient_name,
|
|
128
185
|
} = {}) {
|
|
129
186
|
if (!project_id) {
|
|
130
187
|
throw new ReviewBundlePlanError("INVALID_PROJECT_ID", "project_id is required.");
|
|
@@ -264,6 +321,9 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
264
321
|
const count = Number(row.word_count);
|
|
265
322
|
return sum + (Number.isFinite(count) ? count : 0);
|
|
266
323
|
}, 0);
|
|
324
|
+
const resolvedRecipientName = profile === "beta_reader_personalized"
|
|
325
|
+
? normalizeRecipientDisplayName(recipient_name)
|
|
326
|
+
: undefined;
|
|
267
327
|
|
|
268
328
|
const safeBundleName = slugifyBundleName(bundle_name || `${project_id}-${profile}`);
|
|
269
329
|
const appliedFilters = {
|
|
@@ -283,6 +343,7 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
283
343
|
include_scene_ids: Boolean(include_scene_ids),
|
|
284
344
|
include_metadata_sidebar: Boolean(include_metadata_sidebar),
|
|
285
345
|
include_paragraph_anchors: Boolean(include_paragraph_anchors),
|
|
346
|
+
...(resolvedRecipientName ? { recipient_name: resolvedRecipientName } : {}),
|
|
286
347
|
},
|
|
287
348
|
},
|
|
288
349
|
ordering: rows.map(row => ({
|
|
@@ -308,6 +369,12 @@ export function buildReviewBundlePlan(dbHandle, {
|
|
|
308
369
|
},
|
|
309
370
|
planned_outputs: [
|
|
310
371
|
`${safeBundleName}.md`,
|
|
372
|
+
...(profile === "beta_reader_personalized"
|
|
373
|
+
? [
|
|
374
|
+
`${safeBundleName}.notice.md`,
|
|
375
|
+
`${safeBundleName}.feedback-form.md`,
|
|
376
|
+
]
|
|
377
|
+
: []),
|
|
311
378
|
`${safeBundleName}.manifest.json`,
|
|
312
379
|
],
|
|
313
380
|
};
|
|
@@ -567,19 +634,35 @@ export function renderReviewBundleMarkdown(dbHandle, plan, { generatedAt, syncDi
|
|
|
567
634
|
const sceneIds = plan.ordering.map(row => row.scene_id);
|
|
568
635
|
const rows = loadBundleSceneRows(dbHandle, plan.resolved_scope.project_id, sceneIds);
|
|
569
636
|
const sections = [];
|
|
637
|
+
const recipientName = plan.resolved_scope?.options?.recipient_name;
|
|
638
|
+
const recipientDisplayName = normalizeRecipientDisplayName(recipientName);
|
|
570
639
|
|
|
571
640
|
const headerLines = [
|
|
572
641
|
`# Review Bundle: ${escapeMarkdown(plan.resolved_scope.project_id)}`,
|
|
573
642
|
"",
|
|
574
643
|
`- Profile: ${profile}`,
|
|
644
|
+
...(profile === "beta_reader_personalized"
|
|
645
|
+
? [`- Recipient: ${escapeMarkdown(recipientDisplayName)}`]
|
|
646
|
+
: []),
|
|
575
647
|
`- Generated at: ${generatedAt ?? new Date().toISOString()}`,
|
|
576
648
|
`- Scene count: ${plan.summary.scene_count}`,
|
|
577
649
|
];
|
|
578
650
|
sections.push(headerLines.join("\n"));
|
|
579
651
|
|
|
652
|
+
if (profile === "beta_reader_personalized") {
|
|
653
|
+
sections.push(
|
|
654
|
+
[
|
|
655
|
+
"## Usage Notice",
|
|
656
|
+
"",
|
|
657
|
+
"This beta-reader draft is intended for private review and feedback.",
|
|
658
|
+
"Please do not redistribute without explicit author permission.",
|
|
659
|
+
].join("\n")
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
580
663
|
for (const scene of rows) {
|
|
581
664
|
let prose = "";
|
|
582
|
-
if (profile === "editor_detailed") {
|
|
665
|
+
if (profile === "editor_detailed" || profile === "beta_reader_personalized") {
|
|
583
666
|
const resolved = readProse(scene.file_path, { syncDir });
|
|
584
667
|
if (resolved === null) {
|
|
585
668
|
throw new ReviewBundlePlanError(
|
|
@@ -636,7 +719,14 @@ export function createReviewBundleArtifacts(dbHandle, {
|
|
|
636
719
|
);
|
|
637
720
|
}
|
|
638
721
|
|
|
639
|
-
const
|
|
722
|
+
const noticeFileName = plan.planned_outputs.find(name => name.endsWith(".notice.md")) ?? null;
|
|
723
|
+
const feedbackFileName = plan.planned_outputs.find(name => name.endsWith(".feedback-form.md")) ?? null;
|
|
724
|
+
const markdownFileName = plan.planned_outputs.find(
|
|
725
|
+
name =>
|
|
726
|
+
name.endsWith(".md") &&
|
|
727
|
+
!name.endsWith(".notice.md") &&
|
|
728
|
+
!name.endsWith(".feedback-form.md")
|
|
729
|
+
);
|
|
640
730
|
const manifestFileName = plan.planned_outputs.find(name => name.endsWith(".manifest.json"));
|
|
641
731
|
if (!markdownFileName || !manifestFileName) {
|
|
642
732
|
throw new ReviewBundlePlanError(
|
|
@@ -647,9 +737,18 @@ export function createReviewBundleArtifacts(dbHandle, {
|
|
|
647
737
|
|
|
648
738
|
const markdownPath = resolveOutputFilePath(normalizedOutputDir, markdownFileName);
|
|
649
739
|
const manifestPath = resolveOutputFilePath(normalizedOutputDir, manifestFileName);
|
|
740
|
+
const noticePath = noticeFileName ? resolveOutputFilePath(normalizedOutputDir, noticeFileName) : null;
|
|
741
|
+
const feedbackPath = feedbackFileName ? resolveOutputFilePath(normalizedOutputDir, feedbackFileName) : null;
|
|
650
742
|
|
|
651
743
|
const generatedAt = new Date().toISOString();
|
|
652
744
|
const markdown = renderReviewBundleMarkdown(dbHandle, plan, { generatedAt, syncDir });
|
|
745
|
+
const recipientName = plan.resolved_scope?.options?.recipient_name;
|
|
746
|
+
const betaNotice = plan.profile === "beta_reader_personalized"
|
|
747
|
+
? renderBetaNoticeMarkdown({ projectId: plan.resolved_scope.project_id, recipientName })
|
|
748
|
+
: null;
|
|
749
|
+
const betaFeedbackForm = plan.profile === "beta_reader_personalized"
|
|
750
|
+
? renderBetaFeedbackFormMarkdown({ projectId: plan.resolved_scope.project_id, recipientName, generatedAt })
|
|
751
|
+
: null;
|
|
653
752
|
const manifest = {
|
|
654
753
|
bundle_id: path.basename(markdownFileName, ".md"),
|
|
655
754
|
profile: plan.profile,
|
|
@@ -665,7 +764,7 @@ export function createReviewBundleArtifacts(dbHandle, {
|
|
|
665
764
|
scene_ids: plan.ordering.map(row => row.scene_id),
|
|
666
765
|
};
|
|
667
766
|
|
|
668
|
-
for (const outputPath of [markdownPath, manifestPath]) {
|
|
767
|
+
for (const outputPath of [markdownPath, manifestPath, noticePath, feedbackPath].filter(Boolean)) {
|
|
669
768
|
try {
|
|
670
769
|
const stat = fs.lstatSync(outputPath);
|
|
671
770
|
if (stat.isSymbolicLink()) {
|
|
@@ -692,12 +791,20 @@ export function createReviewBundleArtifacts(dbHandle, {
|
|
|
692
791
|
|
|
693
792
|
fs.writeFileSync(markdownPath, markdown, "utf8");
|
|
694
793
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
794
|
+
if (noticePath && betaNotice != null) {
|
|
795
|
+
fs.writeFileSync(noticePath, betaNotice, "utf8");
|
|
796
|
+
}
|
|
797
|
+
if (feedbackPath && betaFeedbackForm != null) {
|
|
798
|
+
fs.writeFileSync(feedbackPath, betaFeedbackForm, "utf8");
|
|
799
|
+
}
|
|
695
800
|
|
|
696
801
|
return {
|
|
697
802
|
bundle_id: manifest.bundle_id,
|
|
698
803
|
output_paths: {
|
|
699
804
|
bundle_markdown: markdownPath,
|
|
700
805
|
manifest_json: manifestPath,
|
|
806
|
+
...(noticePath ? { notice_md: noticePath } : {}),
|
|
807
|
+
...(feedbackPath ? { feedback_form_md: feedbackPath } : {}),
|
|
701
808
|
},
|
|
702
809
|
generated_at: generatedAt,
|
|
703
810
|
};
|
|
@@ -2,11 +2,12 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
2
2
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import process from "process";
|
|
5
|
+
import fs from "node:fs";
|
|
5
6
|
|
|
6
7
|
async function main() {
|
|
7
8
|
const [projectId, profile, outputDir, ...flags] = process.argv.slice(2);
|
|
8
9
|
if (!projectId || !profile || !outputDir) {
|
|
9
|
-
console.error("Usage: WRITING_SYNC_DIR=/path/to/sync node scripts/manual/run_create_review_bundle.js <project_id> <profile> <output_dir> [--anchors] [--bundle-name <name>]");
|
|
10
|
+
console.error("Usage: WRITING_SYNC_DIR=/path/to/sync node scripts/manual/run_create_review_bundle.js <project_id> <profile> <output_dir> [--anchors] [--recipient <name>] [--bundle-name <name>] [--skip-preview] [--show-files]");
|
|
10
11
|
process.exit(1);
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -19,8 +20,28 @@ async function main() {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const includeParagraphAnchors = flags.includes("--anchors");
|
|
23
|
+
const skipPreview = flags.includes("--skip-preview");
|
|
24
|
+
const showFiles = flags.includes("--show-files");
|
|
22
25
|
const bundleNameIdx = flags.indexOf("--bundle-name");
|
|
23
26
|
const bundleName = bundleNameIdx !== -1 ? flags[bundleNameIdx + 1] : undefined;
|
|
27
|
+
const recipientIdx = flags.indexOf("--recipient");
|
|
28
|
+
const recipientName = recipientIdx !== -1 ? flags[recipientIdx + 1] : undefined;
|
|
29
|
+
|
|
30
|
+
const baseArguments = {
|
|
31
|
+
project_id: projectId,
|
|
32
|
+
profile,
|
|
33
|
+
...(includeParagraphAnchors ? { include_paragraph_anchors: true } : {}),
|
|
34
|
+
...(bundleName ? { bundle_name: bundleName } : {}),
|
|
35
|
+
...(recipientName ? { recipient_name: recipientName } : {}),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function printArtifactExcerpt(label, filePath) {
|
|
39
|
+
if (!filePath || !fs.existsSync(filePath)) return;
|
|
40
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
41
|
+
const excerpt = content.split("\n").slice(0, 20).join("\n");
|
|
42
|
+
console.log(`\n--- ${label}: ${filePath} ---`);
|
|
43
|
+
console.log(excerpt);
|
|
44
|
+
}
|
|
24
45
|
|
|
25
46
|
const transport = new StdioClientTransport({
|
|
26
47
|
command: process.execPath,
|
|
@@ -41,18 +62,38 @@ async function main() {
|
|
|
41
62
|
|
|
42
63
|
try {
|
|
43
64
|
await client.connect(transport);
|
|
65
|
+
|
|
66
|
+
if (!skipPreview) {
|
|
67
|
+
const previewResult = await client.callTool({
|
|
68
|
+
name: "preview_review_bundle",
|
|
69
|
+
arguments: baseArguments,
|
|
70
|
+
});
|
|
71
|
+
const previewText = previewResult.content?.[0]?.text ?? "";
|
|
72
|
+
console.log("\n=== preview_review_bundle ===");
|
|
73
|
+
console.log(previewText);
|
|
74
|
+
}
|
|
75
|
+
|
|
44
76
|
const result = await client.callTool({
|
|
45
77
|
name: "create_review_bundle",
|
|
46
78
|
arguments: {
|
|
47
|
-
|
|
48
|
-
profile: profile,
|
|
79
|
+
...baseArguments,
|
|
49
80
|
output_dir: outputDir,
|
|
50
|
-
...(includeParagraphAnchors ? { include_paragraph_anchors: true } : {}),
|
|
51
|
-
...(bundleName ? { bundle_name: bundleName } : {})
|
|
52
81
|
}
|
|
53
82
|
});
|
|
54
83
|
|
|
55
|
-
|
|
84
|
+
const resultText = result.content?.[0]?.text ?? "";
|
|
85
|
+
console.log("\n=== create_review_bundle ===");
|
|
86
|
+
console.log(resultText);
|
|
87
|
+
|
|
88
|
+
if (showFiles) {
|
|
89
|
+
const parsed = JSON.parse(resultText);
|
|
90
|
+
if (parsed.ok && parsed.output_paths) {
|
|
91
|
+
printArtifactExcerpt("Bundle Markdown", parsed.output_paths.bundle_markdown);
|
|
92
|
+
printArtifactExcerpt("Manifest JSON", parsed.output_paths.manifest_json);
|
|
93
|
+
printArtifactExcerpt("Notice Markdown", parsed.output_paths.notice_md);
|
|
94
|
+
printArtifactExcerpt("Feedback Form Markdown", parsed.output_paths.feedback_form_md);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
56
97
|
} catch (e) {
|
|
57
98
|
console.error(e);
|
|
58
99
|
process.exitCode = 1;
|