@hanna84/mcp-writing 2.12.15 → 2.12.17

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
+ #### [v2.12.17](https://github.com/hannasdev/mcp-writing.git
8
+ /compare/v2.12.16...v2.12.17)
9
+
10
+ - fix: restrict package files allowlist after src test move [`#140`](https://github.com/hannasdev/mcp-writing.git
11
+ /pull/140)
12
+
13
+ #### [v2.12.16](https://github.com/hannasdev/mcp-writing.git
14
+ /compare/v2.12.15...v2.12.16)
15
+
16
+ > 30 April 2026
17
+
18
+ - refactor(src): move scripts and tests under src [`#139`](https://github.com/hannasdev/mcp-writing.git
19
+ /pull/139)
20
+ - Release 2.12.16 [`cd34af2`](https://github.com/hannasdev/mcp-writing.git
21
+ /commit/cd34af28c4a06e7c44d78b9b9ef8f185bf063222)
22
+
7
23
  #### [v2.12.15](https://github.com/hannasdev/mcp-writing.git
8
24
  /compare/v2.12.14...v2.12.15)
9
25
 
26
+ > 29 April 2026
27
+
10
28
  - chore(ci): skip CI jobs on automated release commits [`#130`](https://github.com/hannasdev/mcp-writing.git
11
29
  /pull/130)
30
+ - Release 2.12.15 [`392e0fd`](https://github.com/hannasdev/mcp-writing.git
31
+ /commit/392e0fdf311d6873d95f63a73373ba79f33cb731)
12
32
 
13
33
  #### [v2.12.14](https://github.com/hannasdev/mcp-writing.git
14
34
  /compare/v2.12.13...v2.12.14)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanna84/mcp-writing",
3
- "version": "2.12.15",
3
+ "version": "2.12.17",
4
4
  "description": "MCP service for AI-assisted reasoning and editing on long-form fiction projects",
5
5
  "homepage": "https://hannasdev.github.io/mcp-writing/",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "./prose-styleguide-drift.js": "./src/styleguide/prose-styleguide-drift.js",
31
31
  "./prose-styleguide-skill.js": "./src/styleguide/prose-styleguide-skill.js",
32
32
  "./tools/*": "./src/tools/*",
33
- "./scripts/*": "./scripts/*",
33
+ "./scripts/*": "./src/scripts/*",
34
34
  "./src/*": "./src/*",
35
35
  "./package.json": "./package.json"
36
36
  },
@@ -41,8 +41,16 @@
41
41
  "files": [
42
42
  "bin/",
43
43
  "index.js",
44
- "src/",
45
- "scripts/",
44
+ "src/index.js",
45
+ "src/core/",
46
+ "src/review-bundles/",
47
+ "src/runtime/",
48
+ "src/scripts/",
49
+ "src/styleguide/",
50
+ "src/sync/",
51
+ "src/tools/",
52
+ "src/workflows/",
53
+ "src/world/",
46
54
  "README.md",
47
55
  "CHANGELOG.md"
48
56
  ],
@@ -51,22 +59,22 @@
51
59
  },
52
60
  "scripts": {
53
61
  "start": "node --experimental-sqlite index.js",
54
- "new:entity": "node scripts/new-world-entity.js",
55
- "manual:realtest": "node scripts/manual-scrivener-realtest.mjs",
56
- "manual:test": "node scripts/manual/test.mjs",
57
- "manual:scenarios": "node scripts/manual/test-scenarios.mjs",
58
- "manual:merge-beta-test": "node scripts/manual/run_mcp_test.js",
59
- "manual:review-bundle": "node scripts/manual/run_create_review_bundle.js",
60
- "normalize:scene-characters": "node --experimental-sqlite scripts/normalize-scene-characters.mjs",
61
- "setup:openclaw-env": "sh scripts/setup-openclaw-env.sh",
62
+ "new:entity": "node src/scripts/new-world-entity.js",
63
+ "manual:realtest": "node src/scripts/manual-scrivener-realtest.mjs",
64
+ "manual:test": "node src/scripts/manual/test.mjs",
65
+ "manual:scenarios": "node src/scripts/manual/test-scenarios.mjs",
66
+ "manual:merge-beta-test": "node src/scripts/manual/run_mcp_test.js",
67
+ "manual:review-bundle": "node src/scripts/manual/run_create_review_bundle.js",
68
+ "normalize:scene-characters": "node --experimental-sqlite src/scripts/normalize-scene-characters.mjs",
69
+ "setup:openclaw-env": "sh src/scripts/setup-openclaw-env.sh",
62
70
  "release": "release-it",
63
- "lint": "eslint *.js src/ scripts/",
64
- "guard:legacy-root-imports": "node scripts/check-legacy-root-imports.mjs",
65
- "docs": "node scripts/generate-tool-docs.mjs",
66
- "lint:metadata": "node scripts/lint-metadata.mjs",
67
- "sync:server-json-version": "node scripts/sync-server-json-version.mjs",
68
- "test:unit": "node --experimental-sqlite --test test/unit/*.test.mjs",
69
- "test:integration": "node --experimental-sqlite --test --test-concurrency=1 test/integration/*.test.mjs",
71
+ "lint": "eslint *.js src/index.js src/core src/review-bundles src/runtime src/scripts src/styleguide src/sync src/tools src/workflows src/world",
72
+ "guard:legacy-root-imports": "node src/scripts/check-legacy-root-imports.mjs",
73
+ "docs": "node src/scripts/generate-tool-docs.mjs",
74
+ "lint:metadata": "node src/scripts/lint-metadata.mjs",
75
+ "sync:server-json-version": "node src/scripts/sync-server-json-version.mjs",
76
+ "test:unit": "node --experimental-sqlite --test src/test/unit/*.test.mjs",
77
+ "test:integration": "node --experimental-sqlite --test --test-concurrency=1 src/test/integration/*.test.mjs",
70
78
  "test": "npm run test:unit && npm run test:integration"
71
79
  },
72
80
  "keywords": [
@@ -91,7 +91,7 @@ export function getRuntimeDiagnostics({
91
91
  recommendations.push("If git reports 'dubious ownership' for mounted repos, add: git config --system --add safe.directory /sync");
92
92
  }
93
93
 
94
- recommendations.push("If indexing finds many files without scene_id, run scripts/import.js first for Scrivener Draft exports, then run sync.");
94
+ recommendations.push("If indexing finds many files without scene_id, run src/scripts/import.js first for Scrivener Draft exports, then run sync.");
95
95
 
96
96
  return { warnings, recommendations };
97
97
  }
@@ -92,7 +92,7 @@ async function main() {
92
92
  const resultPath = process.argv[3];
93
93
 
94
94
  if (!requestPath || !resultPath) {
95
- throw new Error("Usage: node scripts/async-job-runner.mjs <request.json> <result.json>");
95
+ throw new Error("Usage: node src/scripts/async-job-runner.mjs <request.json> <result.json>");
96
96
  }
97
97
 
98
98
  const request = JSON.parse(fs.readFileSync(requestPath, "utf8"));
@@ -4,9 +4,9 @@ import { fileURLToPath } from "node:url";
4
4
 
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
- const ROOT = path.resolve(__dirname, "..");
7
+ const ROOT = path.resolve(__dirname, "../..");
8
8
 
9
- const SCAN_PATHS = ["src", "scripts", "test", "index.js"];
9
+ const SCAN_PATHS = ["src", "index.js"];
10
10
  const SOURCE_EXTENSIONS = new Set([".js", ".mjs"]);
11
11
  const LEGACY_ROOT_MODULES = new Set([
12
12
  "async-jobs.js",
@@ -3,7 +3,7 @@
3
3
  * Generates docs/tools.md from tool definitions in the runtime entrypoint
4
4
  * (src/index.js when present, otherwise index.js) and src/tools/*.js.
5
5
  *
6
- * Run: node scripts/generate-tool-docs.mjs
6
+ * Run: node src/scripts/generate-tool-docs.mjs
7
7
  * or: npm run docs
8
8
  *
9
9
  * The output is the single source of truth for the tool reference.
@@ -14,7 +14,7 @@ import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
 
17
- const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
17
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
18
18
  const OUT = path.join(ROOT, 'docs', 'tools.md');
19
19
 
20
20
  // Build a map from each registration function name to its module source.
@@ -3,14 +3,14 @@
3
3
  * Import a Scrivener External Folder Sync output into mcp-writing sidecar format.
4
4
  *
5
5
  * Usage:
6
- * node scripts/import.js <scrivener-sync-dir> <mcp-sync-dir> [--project <id>] [--dry-run]
6
+ * node src/scripts/import.js <scrivener-sync-dir> <mcp-sync-dir> [--project <id>] [--dry-run]
7
7
  */
8
8
 
9
9
  import path from "node:path";
10
- import { importScrivenerSync, validateProjectId } from "../src/sync/importer.js";
10
+ import { importScrivenerSync, validateProjectId } from "../sync/importer.js";
11
11
 
12
12
  function printUsage() {
13
- console.log("Usage: node scripts/import.js <scrivener-sync-dir> <mcp-sync-dir> [--project <id>] [--dry-run]");
13
+ console.log("Usage: node src/scripts/import.js <scrivener-sync-dir> <mcp-sync-dir> [--project <id>] [--dry-run]");
14
14
  }
15
15
 
16
16
  const args = process.argv.slice(2);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import path from "node:path";
3
- import { lintMetadataInSyncDir } from "../src/sync/metadata-lint.js";
3
+ import { lintMetadataInSyncDir } from "../sync/metadata-lint.js";
4
4
 
5
5
  function parseArgs(argv) {
6
6
  const args = { syncDir: process.env.WRITING_SYNC_DIR ?? "./sync" };
@@ -7,14 +7,14 @@ import fs from "node:fs";
7
7
  async function main() {
8
8
  const [projectId, profile, outputDir, ...flags] = process.argv.slice(2);
9
9
  if (!projectId || !profile || !outputDir) {
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
+ console.error("Usage: WRITING_SYNC_DIR=/path/to/sync node src/scripts/manual/run_create_review_bundle.js <project_id> <profile> <output_dir> [--anchors] [--recipient <name>] [--bundle-name <name>] [--skip-preview] [--show-files]");
11
11
  process.exit(1);
12
12
  }
13
13
 
14
14
  if (!process.env.WRITING_SYNC_DIR) {
15
15
  console.error(
16
16
  "WRITING_SYNC_DIR is required. Set it to the root of your sync directory.\n" +
17
- "Usage: WRITING_SYNC_DIR=/path/to/sync node scripts/manual/run_create_review_bundle.js <project_id> <profile> <output_dir>"
17
+ "Usage: WRITING_SYNC_DIR=/path/to/sync node src/scripts/manual/run_create_review_bundle.js <project_id> <profile> <output_dir>"
18
18
  );
19
19
  process.exit(1);
20
20
  }
@@ -17,7 +17,7 @@ async function main() {
17
17
 
18
18
  if (!projectId) {
19
19
  console.error(
20
- "Usage: WRITING_SYNC_DIR=/path/to/sync node scripts/manual/run_mcp_and_review.js <projectId> [outputDir]"
20
+ "Usage: WRITING_SYNC_DIR=/path/to/sync node src/scripts/manual/run_mcp_and_review.js <projectId> [outputDir]"
21
21
  );
22
22
  process.exit(1);
23
23
  }
@@ -25,7 +25,7 @@ async function main() {
25
25
  if (!envWritingSyncDir) {
26
26
  console.error(
27
27
  "WRITING_SYNC_DIR is required. Set it to the root of your sync directory.\n" +
28
- "Usage: WRITING_SYNC_DIR=/path/to/sync node scripts/manual/run_mcp_and_review.js <projectId> [outputDir]"
28
+ "Usage: WRITING_SYNC_DIR=/path/to/sync node src/scripts/manual/run_mcp_and_review.js <projectId> [outputDir]"
29
29
  );
30
30
  process.exit(1);
31
31
  }
@@ -4,8 +4,8 @@ import path from "path";
4
4
  import process from "process";
5
5
 
6
6
  function usage() {
7
- console.log("Usage: node scripts/manual/run_mcp_test.js <source_project_dir> [project_id] [sync_dir]");
8
- console.log("Example: node scripts/manual/run_mcp_test.js ~/Novel.scriv demo-project ~/sync-root");
7
+ console.log("Usage: node src/scripts/manual/run_mcp_test.js <source_project_dir> [project_id] [sync_dir]");
8
+ console.log("Example: node src/scripts/manual/run_mcp_test.js ~/Novel.scriv demo-project ~/sync-root");
9
9
  }
10
10
 
11
11
  async function runCase(env, args) {
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import os from "node:os";
4
- import { importScrivenerSync, validateProjectId } from "../src/sync/importer.js";
5
- import { mergeScrivenerProjectMetadata } from "../src/sync/scrivener-direct.js";
4
+ import { importScrivenerSync, validateProjectId } from "../sync/importer.js";
5
+ import { mergeScrivenerProjectMetadata } from "../sync/scrivener-direct.js";
6
6
 
7
7
  function usage() {
8
8
  return [
9
9
  "Usage:",
10
- " node scripts/manual-scrivener-realtest.mjs \\",
10
+ " node src/scripts/manual-scrivener-realtest.mjs \\",
11
11
  " --source-dir <external-sync-dir> \\",
12
12
  " --scriv-path <copied-project.scriv> \\",
13
13
  " --project-id <project|universe/project> [options]",
@@ -137,7 +137,7 @@ async function runPhaseB() {
137
137
  // Import
138
138
  try {
139
139
  const importOutput = execSync(
140
- `node scripts/import.js ./txt ${IMPORT_DIR} --project scrivener-export`,
140
+ `node src/scripts/import.js ./txt ${IMPORT_DIR} --project scrivener-export`,
141
141
  { cwd: ROOT, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
142
142
  );
143
143
  console.log("✓ Import completed");
@@ -151,7 +151,7 @@ async function runPhaseB() {
151
151
  // Lint
152
152
  try {
153
153
  const lintOutput = execSync(
154
- `node scripts/lint-metadata.mjs --sync-dir ${IMPORT_DIR}`,
154
+ `node src/scripts/lint-metadata.mjs --sync-dir ${IMPORT_DIR}`,
155
155
  { cwd: ROOT, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
156
156
  );
157
157
  results.lintOutput = lintOutput;
@@ -3,7 +3,7 @@
3
3
  * Merge Scrivener project metadata into mcp-writing sidecar files.
4
4
  *
5
5
  * Usage:
6
- * node scripts/merge-scrivx.js <path-to.scriv> <mcp-sync-dir> [options]
6
+ * node src/scripts/merge-scrivx.js <path-to.scriv> <mcp-sync-dir> [options]
7
7
  *
8
8
  * <path-to.scriv> Path to the Scrivener .scriv bundle (the folder)
9
9
  * <mcp-sync-dir> The WRITING_SYNC_DIR root (e.g. ./sync)
@@ -27,15 +27,15 @@
27
27
  */
28
28
 
29
29
  import path from "node:path";
30
- import { validateProjectId } from "../src/sync/importer.js";
31
- import { mergeScrivenerProjectMetadata } from "../src/sync/scrivener-direct.js";
30
+ import { validateProjectId } from "../sync/importer.js";
31
+ import { mergeScrivenerProjectMetadata } from "../sync/scrivener-direct.js";
32
32
 
33
33
  // ---------------------------------------------------------------------------
34
34
  // Args
35
35
  // ---------------------------------------------------------------------------
36
36
  const args = process.argv.slice(2);
37
37
  if (args.length < 2 || args[0] === "--help") {
38
- console.log("Usage: node scripts/merge-scrivx.js <path-to.scriv> <mcp-sync-dir> [--project <id>] [--dry-run] [--organize-by-chapters]");
38
+ console.log("Usage: node src/scripts/merge-scrivx.js <path-to.scriv> <mcp-sync-dir> [--project <id>] [--dry-run] [--organize-by-chapters]");
39
39
  process.exit(args[0] === "--help" ? 0 : 1);
40
40
  }
41
41
 
@@ -6,7 +6,7 @@ import {
6
6
  renderCharacterSheetTemplate,
7
7
  renderPlaceSheetTemplate,
8
8
  slugifyEntityName,
9
- } from "../src/world/world-entity-templates.js";
9
+ } from "../world/world-entity-templates.js";
10
10
 
11
11
  function parseArgs(argv) {
12
12
  const args = argv.slice(2);
@@ -39,11 +39,11 @@ function parseArgs(argv) {
39
39
  function usage() {
40
40
  return [
41
41
  "Usage:",
42
- " node scripts/new-world-entity.js --sync-dir <dir> --kind <character|place> --scope <project|universe> --name <display name> [--project <project-id>] [--universe <universe-id>] [--dry-run]",
42
+ " node src/scripts/new-world-entity.js --sync-dir <dir> --kind <character|place> --scope <project|universe> --name <display name> [--project <project-id>] [--universe <universe-id>] [--dry-run]",
43
43
  "",
44
44
  "Examples:",
45
- " node scripts/new-world-entity.js --sync-dir ./writing --kind character --scope universe --universe universe-1 --name 'Mira Nystrom'",
46
- " node scripts/new-world-entity.js --sync-dir ./writing --kind place --scope project --project universe-1/book-1-the-lamb --name 'University Hospital'",
45
+ " node src/scripts/new-world-entity.js --sync-dir ./writing --kind character --scope universe --universe universe-1 --name 'Mira Nystrom'",
46
+ " node src/scripts/new-world-entity.js --sync-dir ./writing --kind place --scope project --project universe-1/book-1-the-lamb --name 'University Hospital'",
47
47
  ].join("\n");
48
48
  }
49
49
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import path from "node:path";
3
- import { openDb } from "../src/core/db.js";
4
- import { buildCharacterNormalizationContext, normalizeSceneCharacters } from "../src/sync/scene-character-normalization.js";
5
- import { normalizeSceneMetaForPath, readMeta, syncAll, writeMeta } from "../src/sync/sync.js";
3
+ import { openDb } from "../core/db.js";
4
+ import { buildCharacterNormalizationContext, normalizeSceneCharacters } from "../sync/scene-character-normalization.js";
5
+ import { normalizeSceneMetaForPath, readMeta, syncAll, writeMeta } from "../sync/sync.js";
6
6
 
7
7
  function readRequiredValue(argv, index, option) {
8
8
  const value = argv[index + 1];
@@ -54,7 +54,7 @@ function parseArgs(argv) {
54
54
  function usage() {
55
55
  return [
56
56
  "Usage:",
57
- " node --experimental-sqlite scripts/normalize-scene-characters.mjs [--sync-dir <dir>] [--project-id <id>] [--limit <n>] [--write] [--json]",
57
+ " node --experimental-sqlite src/scripts/normalize-scene-characters.mjs [--sync-dir <dir>] [--project-id <id>] [--limit <n>] [--write] [--json]",
58
58
  "",
59
59
  "Options:",
60
60
  " --sync-dir, -d WRITING_SYNC_DIR root (default: env WRITING_SYNC_DIR or ./sync)",
@@ -2,19 +2,19 @@
2
2
 
3
3
  /**
4
4
  * Profile review-bundle generation performance
5
- * Usage: node --experimental-sqlite scripts/profile-review-bundles.mjs
5
+ * Usage: node --experimental-sqlite src/scripts/profile-review-bundles.mjs
6
6
  */
7
7
 
8
8
  import fs from "node:fs";
9
9
  import path from "node:path";
10
10
  import os from "node:os";
11
- import { openDb } from "../src/core/db.js";
12
- import { syncAll } from "../src/sync/sync.js";
13
- import { isGitRepository, getHeadCommitHash } from "../src/core/git.js";
11
+ import { openDb } from "../core/db.js";
12
+ import { syncAll } from "../sync/sync.js";
13
+ import { isGitRepository, getHeadCommitHash } from "../core/git.js";
14
14
  import {
15
15
  buildReviewBundlePlan,
16
16
  createReviewBundleArtifacts,
17
- } from "../src/review-bundles/review-bundles.js";
17
+ } from "../review-bundles/review-bundles.js";
18
18
 
19
19
  const PROJECT_SYNC_DIR = process.env.WRITING_SYNC_DIR ?? process.argv[2] ?? null;
20
20
  const DB_PATH = process.env.DB_PATH ?? (PROJECT_SYNC_DIR ? path.join(PROJECT_SYNC_DIR, ".mcp", "writing.db") : null);
@@ -119,7 +119,7 @@ async function profileScenario(
119
119
  async function main() {
120
120
  if (!PROJECT_SYNC_DIR) {
121
121
  console.error("✗ WRITING_SYNC_DIR env var or a path argument is required.");
122
- console.error(" Usage: WRITING_SYNC_DIR=/path/to/project node --experimental-sqlite scripts/profile-review-bundles.mjs");
122
+ console.error(" Usage: WRITING_SYNC_DIR=/path/to/project node --experimental-sqlite src/scripts/profile-review-bundles.mjs");
123
123
  process.exit(1);
124
124
  }
125
125
 
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
 
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
- const ROOT = path.resolve(__dirname, "..");
7
+ const ROOT = path.resolve(__dirname, "../..");
8
8
  const packageJsonPath = path.join(ROOT, "package.json");
9
9
  const serverJsonPath = path.join(ROOT, "server.json");
10
10
 
package/src/tools/sync.js CHANGED
@@ -31,7 +31,7 @@ export function registerSyncTools(s, {
31
31
  if (result.sidecarsMigrated) parts.push(`${result.sidecarsMigrated} sidecar(s) auto-generated from frontmatter.`);
32
32
  if (result.skipped) {
33
33
  parts.push(`${result.skipped} file(s) skipped (no scene_id).`);
34
- parts.push(`Tip: for raw Scrivener Draft exports, run scripts/import.js first, then run sync again.`);
34
+ parts.push(`Tip: for raw Scrivener Draft exports, run src/scripts/import.js first, then run sync again.`);
35
35
  }
36
36
  const summary = result.warningSummary;
37
37
  const summaryEntries = Object.entries(summary);
@@ -1 +0,0 @@
1
- import "../src/scripts/async-job-runner.mjs";
File without changes
File without changes
File without changes
File without changes