drafted 1.14.29 → 1.15.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.
Files changed (2) hide show
  1. package/mcp/server.mjs +15 -7
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -245,7 +245,9 @@ BREADCRUMBS: When a frame you write or read corresponds to a file in the user's
245
245
  CONTEXT RULES (follow these before every action):
246
246
  - WIKI CHECK: Before acting on any request, search the org wiki for relevant conventions, existing designs, and prior decisions. Use wiki(action="search") with relevant keywords.
247
247
  - LAYER CONTEXT: Before reading or mutating a frame, read all anchored frames in the same layer. Anchored frames are per-layer required reading (style guides, design systems, conventions). The server enforces this mechanically for writes/edits/deletes/moves — but proactively reading anchored frames before any frame operation prevents wasted work.
248
- IMPORTANT: Any URL containing /f/{uuid} is a Drafted frame link — ALWAYS use read(path=URL) to get frame content, focus(target=URL) to pan the canvas to it. Never curl or WebFetch Drafted URLs.`,
248
+ IMPORTANT: Any URL containing /f/{uuid} is a Drafted frame link — ALWAYS use read(path=URL) to get frame content, focus(target=URL) to pan the canvas to it. Never curl or WebFetch Drafted URLs.
249
+
250
+ LINKING: link the user to the narrowest thing you touched, never the project as a stand-in. Wrote or edited ONE frame → give its \`frameUrl\` (from the write/read/ls response). Touched a whole lane → its \`laneUrl\`; a whole layer → its \`layerUrl\` (both on the matching \`ls\` entries). Only when the work spans the project is the project URL the right link. A project link where a frame link was available makes the user hunt the surface for what you just did.`,
249
251
  }, {
250
252
  // Initialize instructions: the agent-identity contract, so an agent learns its own
251
253
  // tab name + the right way to read it WITHOUT having to "think to" call a tool.
@@ -1260,6 +1262,10 @@ function withFrameBreadcrumb(result, { hint = false } = {}) {
1260
1262
  const out = { ...result, breadcrumb: `drafted:${result.id}` };
1261
1263
  if (hint) {
1262
1264
  out.breadcrumbHint = `If this frame maps to a file in the user's codebase, paste "drafted:${result.id}" as a comment in that file so future agents discover the link.`;
1265
+ // The write path is where the project URL keeps getting substituted for the frame
1266
+ // the agent just wrote. Name the right link at the moment it's produced.
1267
+ const link = out.frameUrl || `${getServerUrl()}/f/${result.id}`;
1268
+ out.linkHint = `Link the user to THIS frame — ${link} — not to the project URL.`;
1263
1269
  }
1264
1270
  return out;
1265
1271
  }
@@ -2953,8 +2959,9 @@ tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by
2953
2959
  lineHash: z.string().describe('The full line anchor copied verbatim from read output — line number + 3-char hash, e.g. "182vix" (the token left of the "|"). NOT the bare hash "vix": that is rejected, since the same hash can recur on multiple lines.'),
2954
2960
  newContent: z.string().optional().describe('New content (for replace, insertAfter, insertBefore)'),
2955
2961
  })).optional().describe('[edit] hashline edit operations'),
2956
- from: z.string().optional().describe('[mv] source path /{layer}/{lane}/{filename}'),
2957
- to: z.string().optional().describe('[mv] destination path /{layer}/{lane}/{filename}'),
2962
+ from: z.string().optional().describe('[mv] source path: /{layer}/{lane}/{filename} for a frame, /{layer}/{lane} for a lane, /{layer} for a whole layer (lane/layer require toProjectId)'),
2963
+ to: z.string().optional().describe('[mv] destination path /{layer}/{lane}/{filename} — the layer segment is always required and explicit'),
2964
+ toProjectId: z.string().optional().describe('[mv] move ACROSS projects: destination project UUID/slug/name. Same-org only. Frame ids and version history follow; frame-scoped shares, public links, and lane share tokens are NOT carried into the destination project. Omit for the classic in-project rename/move.'),
2958
2965
  dryRun: z.boolean().optional().describe('[mv] preview the move without applying it; returns the resolved frame and current path so you can confirm before retrying with dryRun=false.'),
2959
2966
  anchored: z.boolean().optional().describe('[anchor] true to anchor, false to unanchor. Anchored frames MUST be read before writing/editing in the same layer.'),
2960
2967
  query: z.string().optional().describe('[search] term to match against frame names'),
@@ -3314,7 +3321,7 @@ tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by
3314
3321
  });
3315
3322
  }
3316
3323
  case 'mv': {
3317
- const { from, to, dryRun = false } = args;
3324
+ const { from, to, toProjectId, dryRun = false } = args;
3318
3325
  if (!from || !to) throw new Error('from and to required for action=mv');
3319
3326
  if (dryRun) {
3320
3327
  // Resolve current frame at `from`. Don't apply the rename, just
@@ -3324,11 +3331,12 @@ tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by
3324
3331
  dryRun: true,
3325
3332
  from,
3326
3333
  to,
3334
+ toProjectId: toProjectId || undefined,
3327
3335
  currentFrame: current?.frame || current,
3328
- note: 'No changes applied. Re-call with dryRun=false (or omit) to perform the rename.',
3336
+ note: 'No changes applied. Re-call with dryRun=false (or omit) to perform the move.',
3329
3337
  }));
3330
3338
  }
3331
- return ok(withProject(await api('POST', '/api/fs/mv', { from, to })));
3339
+ return ok(withProject(await api('POST', '/api/fs/mv', { from, to, ...(toProjectId ? { toProjectId } : {}) })));
3332
3340
  }
3333
3341
  case 'anchor': {
3334
3342
  const { path, anchored } = args;
@@ -3398,7 +3406,7 @@ tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by
3398
3406
 
3399
3407
  tool('ls', 'List contents of the ACTIVE PROJECT. Use ls / after project(action="open") to see layers, workflow, and confirm you\'re in the right project.', {
3400
3408
  projectId: PROJECT_OVERRIDE_PARAM,
3401
- path: z.string().optional().default('/').describe('Directory path: / (layers), /{layer} (lanes), /{layer}/{lane} (frames). Frame entries include frameUrl (canvas deep link) and id (frame UUID).'),
3409
+ path: z.string().optional().default('/').describe('Directory path: / (layers), /{layer} (lanes), /{layer}/{lane} (frames). Frame entries include frameUrl (canvas deep link) and id (frame UUID); layer entries include layerUrl and lane entries laneUrl — relay those to the user instead of the project URL when your work was scoped to one layer or lane.'),
3402
3410
  recursive: z.boolean().optional().describe('List contents of subdirectories. When true, forces summary mode (metadata only, no full content) to keep results under the 25k token cap.'),
3403
3411
  summary: z.boolean().optional().describe('Include size, updatedAt, title for frames'),
3404
3412
  pattern: z.string().optional().describe('Glob pattern to filter filenames (e.g. "*.html")'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.14.29",
3
+ "version": "1.15.0",
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": [