drafted 1.17.8 → 1.17.9

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.
Files changed (2) hide show
  1. package/mcp/server.mjs +20 -6
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -2678,7 +2678,11 @@ tool('fs', 'Navigate Drafted like a local filesystem. An org is the top folder:
2678
2678
  }
2679
2679
  case 'read': {
2680
2680
  const canonPath = wikiPath.replace(/\.md$/, '');
2681
- const page = await api('GET', `/api/wiki/page?path=${encodeURIComponent(canonPath)}&raw=true`, undefined, orgHeader);
2681
+ // hashline-annotated (read edit stays surgical), like frames; lines
2682
+ // honors partial reads. raw=true keeps it body-only (no synthesized
2683
+ // frontmatter) so ops anchor to the same bytes /edit applies against.
2684
+ const q = `&raw=true&format=hashline${lines ? `&lines=${encodeURIComponent(lines)}` : ''}`;
2685
+ const page = await api('GET', `/api/wiki/page?path=${encodeURIComponent(canonPath)}${q}`, undefined, orgHeader);
2682
2686
  if (!page?.content) return err(new Error(`wiki page not found: ${canonPath}`));
2683
2687
  return ok(page.content);
2684
2688
  }
@@ -2704,15 +2708,25 @@ tool('fs', 'Navigate Drafted like a local filesystem. An org is the top folder:
2704
2708
  return ok(result);
2705
2709
  }
2706
2710
  case 'rm': {
2707
- // Soft delete: move the page under archive/ (agents never hard-delete;
2708
- // the web UI offers permanent delete in the archive). mv cascades
2709
- // inbound refs, so links keep working into the archive.
2711
+ // Live page → soft delete: move it under archive/ (mv cascades inbound
2712
+ // refs, so links keep working into the archive). Already-archived page
2713
+ // hard purge: it IS the trash (the web UI offers permanent delete
2714
+ // there), and without it an occupied archive slot had no escape hatch
2715
+ // via MCP — the old behavior self-moved (from == to) and reported "as
2716
+ // if nothing happened" while the server renamed the row to its own
2717
+ // collision suffix.
2710
2718
  const canonPath = wikiPath.replace(/\.md$/, '');
2711
- const archivePath = canonPath.startsWith('archive/') ? canonPath : `archive/${canonPath}`;
2712
2719
  const page = await api('GET', `/api/wiki/page?path=${encodeURIComponent(canonPath)}`, undefined, orgHeader).catch(() => null);
2713
2720
  if (!page?.id) return err(new Error(`wiki page not found: ${canonPath}`));
2721
+ if (canonPath.startsWith('archive/')) {
2722
+ const del = await api('DELETE', `/api/wiki/pages/${page.id}`, undefined, orgHeader);
2723
+ return ok({ deleted: true, path: canonPath, ...(del && del.deleted ? { id: del.deleted } : {}) });
2724
+ }
2725
+ const archivePath = `archive/${canonPath}`;
2714
2726
  const result = await api('PATCH', `/api/wiki/pages/${page.id}/move`, { path: archivePath }, orgHeader);
2715
- return ok({ archived: true, from: canonPath, to: archivePath, ...(result?.referrersUpdated ? { referrersUpdated: result.referrersUpdated } : {}) });
2727
+ // Report the server-authoritative final path an occupied archive slot
2728
+ // auto-suffixes (-old), and the response must match the DB, not guess.
2729
+ return ok({ archived: true, from: canonPath, to: result?.path || archivePath, ...(result?.referrersUpdated ? { referrersUpdated: result.referrersUpdated } : {}) });
2716
2730
  }
2717
2731
  case 'search': {
2718
2732
  markSearched(gs, 'wiki');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.17.8",
3
+ "version": "1.17.9",
4
4
  "description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
5
5
  "type": "module",
6
6
  "files": [