@hanna84/mcp-writing 2.9.0 → 2.9.1

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,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
+ #### [v2.9.1](https://github.com/hannasdev/mcp-writing.git
8
+ /compare/v2.9.0...v2.9.1)
9
+
10
+ - fix(describe-workflows): suppress structural dir names as project_id [`#93`](https://github.com/hannasdev/mcp-writing.git
11
+ /pull/93)
12
+
7
13
  #### [v2.9.0](https://github.com/hannasdev/mcp-writing.git
8
14
  /compare/v2.8.0...v2.9.0)
9
15
 
16
+ > 26 April 2026
17
+
10
18
  - feat(get_runtime_config): include server_version in response [`#92`](https://github.com/hannasdev/mcp-writing.git
11
19
  /pull/92)
20
+ - Release 2.9.0 [`bea57f7`](https://github.com/hannasdev/mcp-writing.git
21
+ /commit/bea57f7c7548a428e5335425114b1dbc2cd19450)
12
22
 
13
23
  #### [v2.8.0](https://github.com/hannasdev/mcp-writing.git
14
24
  /compare/v2.7.0...v2.8.0)
package/index.js CHANGED
@@ -12,7 +12,7 @@ import matter from "gray-matter";
12
12
  import yaml from "js-yaml";
13
13
  import { z } from "zod";
14
14
  import { openDb } from "./db.js";
15
- import { syncAll, isSyncDirWritable, getSyncOwnershipDiagnostics, getFileWriteDiagnostics, writeMeta, readMeta, indexSceneFile, normalizeSceneMetaForPath, sidecarPath } from "./sync.js";
15
+ import { syncAll, isSyncDirWritable, getSyncOwnershipDiagnostics, getFileWriteDiagnostics, writeMeta, readMeta, indexSceneFile, normalizeSceneMetaForPath, sidecarPath, isStructuralProjectId } from "./sync.js";
16
16
  import { isGitAvailable, isGitRepository, initGitRepository, createSnapshot, listSnapshots, getSceneProseAtCommit, getHeadCommitHash } from "./git.js";
17
17
  import { renderCharacterArcTemplate, renderCharacterSheetTemplate, renderPlaceSheetTemplate, slugifyEntityName } from "./world-entity-templates.js";
18
18
  import { importScrivenerSync, validateProjectId, validateUniverseId } from "./importer.js";
@@ -866,7 +866,7 @@ const WORKFLOW_CATALOGUE = [
866
866
  steps: [
867
867
  { tool: "describe_workflows", note: "Check context.scene_count; use that value as max_scenes in the next call." },
868
868
  { tool: "bootstrap_prose_styleguide_config", note: "Detect dominant conventions. Confirm suggestions with the user before applying." },
869
- { tool: "setup_prose_styleguide_config", note: "Create config at project_root scope if context.styleguide_exists.project_root is false. Requires language (e.g. 'english_us')." },
869
+ { tool: "setup_prose_styleguide_config", note: "Only if ALL context.styleguide_exists fields are false — a config at any scope is sufficient. Create at project_root scope (requires project_id and language e.g. 'english_us'), or sync_root if no project_id is known." },
870
870
  { tool: "update_prose_styleguide_config", note: "Apply the fields accepted from bootstrap suggestions." },
871
871
  ],
872
872
  },
@@ -960,7 +960,15 @@ function createMcpServer() {
960
960
  const projectRow = db.prepare(
961
961
  `SELECT project_id FROM scenes GROUP BY project_id ORDER BY COUNT(*) DESC, project_id ASC LIMIT 1`
962
962
  ).get();
963
- const project_id = projectRow?.project_id ?? null;
963
+ // Suppress structural-dir names (e.g. "scenes") that appear when SYNC_DIR points at the
964
+ // project directory itself rather than the universe root. They are path artifacts, not
965
+ // real project identifiers. Only suppress when no real project directory exists at that
966
+ // path, so a project intentionally named "scenes" (though inadvisable) is still honoured.
967
+ const rawProjectId = projectRow?.project_id ?? null;
968
+ const rawProjectRootPath = rawProjectId ? resolveProjectRoot(rawProjectId) : null;
969
+ const project_id = (
970
+ isStructuralProjectId(rawProjectId) && !fs.existsSync(rawProjectRootPath)
971
+ ) ? null : rawProjectId;
964
972
 
965
973
  const sceneCountRow = db.prepare(`SELECT COUNT(*) as count FROM scenes`).get();
966
974
  const scene_count = sceneCountRow?.count ?? 0;
@@ -974,6 +982,10 @@ function createMcpServer() {
974
982
  ? path.join(SYNC_DIR, "universes", universeSegment, STYLEGUIDE_CONFIG_BASENAME)
975
983
  : null;
976
984
 
985
+ const syncRootExists = fs.existsSync(syncRootConfigPath);
986
+ const universeRootExists = universeRootConfigPath !== null && fs.existsSync(universeRootConfigPath);
987
+ const projectRootExists = projectRootConfigPath !== null && fs.existsSync(projectRootConfigPath);
988
+
977
989
  return jsonResponse({
978
990
  ok: true,
979
991
  context: {
@@ -981,9 +993,9 @@ function createMcpServer() {
981
993
  scene_count,
982
994
  sync_dir: SYNC_DIR_ABS,
983
995
  styleguide_exists: {
984
- sync_root: fs.existsSync(syncRootConfigPath),
985
- universe_root: universeRootConfigPath !== null && fs.existsSync(universeRootConfigPath),
986
- project_root: projectRootConfigPath !== null && fs.existsSync(projectRootConfigPath),
996
+ sync_root: syncRootExists,
997
+ universe_root: universeRootExists,
998
+ project_root: projectRootExists,
987
999
  },
988
1000
  git_available: GIT_AVAILABLE,
989
1001
  pending_proposals: pendingProposals.size,
@@ -994,6 +1006,7 @@ function createMcpServer() {
994
1006
  "If a tool returns a next_step field (in a success or error response), follow it before trying anything else.",
995
1007
  "Use find_scenes without filters to discover what project_ids are indexed.",
996
1008
  "When calling bootstrap_prose_styleguide_config or check_prose_styleguide_drift, set max_scenes to context.scene_count to avoid the default limit.",
1009
+ "Styleguide tools resolve config in priority order: project_root > universe_root > sync_root. If any styleguide_exists field is true, a config exists and styleguide tools will work — do not run setup_prose_styleguide_config unless ALL styleguide_exists fields are false.",
997
1010
  ],
998
1011
  });
999
1012
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanna84/mcp-writing",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
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",
package/sync.js CHANGED
@@ -99,6 +99,10 @@ export function normalizeSceneMetaForPath(syncDir, filePath, meta = {}) {
99
99
  // Structural directory names that are never project slugs under projects/<id>/.
100
100
  const PROJECT_STRUCTURAL_DIRS = new Set(["world", "scenes", "misc", "fragments", "feedback", "draft"]);
101
101
 
102
+ export function isStructuralProjectId(name) {
103
+ return typeof name === "string" && PROJECT_STRUCTURAL_DIRS.has(name.toLowerCase());
104
+ }
105
+
102
106
  // Cache universe project root existence checks during sync scans.
103
107
  const UNIVERSE_PROJECT_ROOT_CACHE = new Map();
104
108