@hanna84/mcp-writing 1.9.4 → 1.11.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 +20 -0
- package/importer.js +53 -3
- package/index.js +728 -50
- package/package.json +4 -1
- package/scripts/async-job-runner.mjs +136 -0
- package/scripts/manual-scrivener-realtest.mjs +262 -0
- package/scripts/merge-scrivx.js +16 -269
- package/scrivener-direct.js +355 -0
- package/sync.js +42 -3
- package/world-entity-templates.js +116 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,31 @@ 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.11.0](https://github.com/hannasdev/mcp-writing.git
|
|
8
|
+
/compare/v1.10.0...v1.11.0)
|
|
9
|
+
|
|
10
|
+
- feat: add reusable manual real-data Scrivener test runner [`#58`](https://github.com/hannasdev/mcp-writing.git
|
|
11
|
+
/pull/58)
|
|
12
|
+
|
|
13
|
+
#### [v1.10.0](https://github.com/hannasdev/mcp-writing.git
|
|
14
|
+
/compare/v1.9.4...v1.10.0)
|
|
15
|
+
|
|
16
|
+
> 21 April 2026
|
|
17
|
+
|
|
18
|
+
- feat: Scrivener direct extraction beta (M1, M2, M2.5) [`#57`](https://github.com/hannasdev/mcp-writing.git
|
|
19
|
+
/pull/57)
|
|
20
|
+
- Release 1.10.0 [`504bd7f`](https://github.com/hannasdev/mcp-writing.git
|
|
21
|
+
/commit/504bd7facfaf11052e98127e2ae0a104259d8a57)
|
|
22
|
+
|
|
7
23
|
#### [v1.9.4](https://github.com/hannasdev/mcp-writing.git
|
|
8
24
|
/compare/v1.9.3...v1.9.4)
|
|
9
25
|
|
|
26
|
+
> 20 April 2026
|
|
27
|
+
|
|
10
28
|
- docs: split README into focused topic guides [`#56`](https://github.com/hannasdev/mcp-writing.git
|
|
11
29
|
/pull/56)
|
|
30
|
+
- Release 1.9.4 [`ccc8660`](https://github.com/hannasdev/mcp-writing.git
|
|
31
|
+
/commit/ccc86602b1bacaaec92967141f40334182f99177)
|
|
12
32
|
|
|
13
33
|
#### [v1.9.3](https://github.com/hannasdev/mcp-writing.git
|
|
14
34
|
/compare/v1.9.2...v1.9.3)
|
package/importer.js
CHANGED
|
@@ -84,6 +84,21 @@ function walkSorted(dir) {
|
|
|
84
84
|
return files.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function compileIgnorePatterns(patterns) {
|
|
88
|
+
return patterns.map((pattern) => {
|
|
89
|
+
try {
|
|
90
|
+
return new RegExp(pattern);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const error = new Error(
|
|
93
|
+
`Invalid ignore pattern '${pattern}': ${err instanceof Error ? err.message : String(err)}`
|
|
94
|
+
);
|
|
95
|
+
error.code = "INVALID_IGNORE_PATTERN";
|
|
96
|
+
error.pattern = pattern;
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
87
102
|
function loadYamlFile(filePath) {
|
|
88
103
|
try {
|
|
89
104
|
return yaml.load(fs.readFileSync(filePath, "utf8")) ?? {};
|
|
@@ -185,6 +200,8 @@ export function importScrivenerSync({
|
|
|
185
200
|
mcpSyncDir,
|
|
186
201
|
projectId,
|
|
187
202
|
dryRun = false,
|
|
203
|
+
preflight = false,
|
|
204
|
+
ignorePatterns = [],
|
|
188
205
|
logger = () => {},
|
|
189
206
|
}) {
|
|
190
207
|
const scrivenerDirAbs = path.resolve(scrivenerDir);
|
|
@@ -241,7 +258,16 @@ export function importScrivenerSync({
|
|
|
241
258
|
const hasDraft = fs.existsSync(draftDir);
|
|
242
259
|
const draftRoot = hasDraft ? draftDir : scrivenerDirAbs;
|
|
243
260
|
|
|
244
|
-
const
|
|
261
|
+
const compiledIgnorePatterns = compileIgnorePatterns(ignorePatterns);
|
|
262
|
+
|
|
263
|
+
function isIgnored(filename) {
|
|
264
|
+
return compiledIgnorePatterns.some(re => re.test(filename));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const rawFiles = walkSorted(draftRoot);
|
|
268
|
+
const ignoredFiles = rawFiles.filter(f => isIgnored(path.basename(f)));
|
|
269
|
+
const files = rawFiles.filter(f => !isIgnored(path.basename(f)));
|
|
270
|
+
|
|
245
271
|
const existingScenes = buildExistingSceneIndex(scenesDir);
|
|
246
272
|
|
|
247
273
|
let created = 0;
|
|
@@ -250,13 +276,35 @@ export function importScrivenerSync({
|
|
|
250
276
|
let beatMarkersSeen = 0;
|
|
251
277
|
let beatCarry = null;
|
|
252
278
|
|
|
279
|
+
if (preflight) {
|
|
280
|
+
const previewFiles = files.map(f => path.relative(draftRoot, f));
|
|
281
|
+
return {
|
|
282
|
+
projectId: resolvedProjectId,
|
|
283
|
+
scrivenerDir: scrivenerDirAbs,
|
|
284
|
+
mcpSyncDir: mcpSyncDirAbs,
|
|
285
|
+
scenesDir,
|
|
286
|
+
preflight: true,
|
|
287
|
+
dryRun: true,
|
|
288
|
+
sourceFiles: rawFiles.length,
|
|
289
|
+
ignoredFiles: ignoredFiles.length,
|
|
290
|
+
ignoredFilenames: ignoredFiles.map(f => path.basename(f)),
|
|
291
|
+
filesToProcess: files.length,
|
|
292
|
+
filePreviews: previewFiles,
|
|
293
|
+
existingSidecars: existingScenes.size,
|
|
294
|
+
created: 0,
|
|
295
|
+
existing: 0,
|
|
296
|
+
skipped: 0,
|
|
297
|
+
beatMarkersSeen: 0,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
if (!dryRun) {
|
|
254
302
|
fs.mkdirSync(scenesDir, { recursive: true });
|
|
255
303
|
}
|
|
256
304
|
|
|
257
305
|
logger(`Project: ${resolvedProjectId}`);
|
|
258
306
|
logger(`Scenes to: ${scenesDir}`);
|
|
259
|
-
logger(`Files: ${files.length}`);
|
|
307
|
+
logger(`Files: ${files.length} (${ignoredFiles.length} ignored)`);
|
|
260
308
|
logger("");
|
|
261
309
|
|
|
262
310
|
for (const file of files) {
|
|
@@ -357,7 +405,9 @@ export function importScrivenerSync({
|
|
|
357
405
|
scrivenerDir: scrivenerDirAbs,
|
|
358
406
|
mcpSyncDir: mcpSyncDirAbs,
|
|
359
407
|
scenesDir,
|
|
360
|
-
|
|
408
|
+
preflight: false,
|
|
409
|
+
sourceFiles: rawFiles.length,
|
|
410
|
+
ignoredFiles: ignoredFiles.length,
|
|
361
411
|
created,
|
|
362
412
|
skipped,
|
|
363
413
|
existing,
|