@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 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 files = walkSorted(draftRoot);
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
- sourceFiles: files.length,
408
+ preflight: false,
409
+ sourceFiles: rawFiles.length,
410
+ ignoredFiles: ignoredFiles.length,
361
411
  created,
362
412
  skipped,
363
413
  existing,