drafted 1.14.7 → 1.14.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.
package/cli/drafted.mjs CHANGED
@@ -1591,9 +1591,12 @@ async function syncOneSkill(ref, outDir, org) {
1591
1591
  mkdirSync(dirname(full), { recursive: true });
1592
1592
  writeFileSync(full, content);
1593
1593
  }
1594
- // createdBy lets the caller decide whether to auto-run this skill's setup: shell authored
1595
- // by someone else must not run unattended on sync (RCE via a teammate's/forked skill).
1596
- return { ref, slug, hash, status: 'ok', createdBy: skill.createdBy ?? null };
1594
+ // createdBy + forkedFrom decide whether this skill's setup may auto-run: shell authored by
1595
+ // someone else must not run unattended on sync. forkedFrom is load-bearing — the CLI/MCP
1596
+ // AUTO-forks a global/other-org skill on use, which copies the source author's setup but
1597
+ // stamps createdBy = the forking (victim) user, so createdBy alone would mislabel foreign
1598
+ // setup as self-authored. A forked skill is never treated as self-authored.
1599
+ return { ref, slug, hash, status: 'ok', createdBy: skill.createdBy ?? null, forkedFrom: skill.forkedFrom ?? null };
1597
1600
  }
1598
1601
 
1599
1602
  const skillCmd = program.command('skill').description('Skill library operations');
@@ -1626,7 +1629,10 @@ skillCmd
1626
1629
  // authored by another org member, a fork, or an imported bundle must NOT run their
1627
1630
  // shell unattended (Causeway auto-syncs pinned skills), or a teammate's skill is
1628
1631
  // RCE on this host. `--allow-untrusted-setup` is the explicit opt-in.
1629
- const selfAuthored = r.createdBy && auth?.userId && r.createdBy === auth.userId;
1632
+ // Self-authored = created by me AND not a fork (a fork carries another author's
1633
+ // setup under my createdBy — see syncOneSkill). Forks/other-authored require the
1634
+ // explicit opt-in.
1635
+ const selfAuthored = r.createdBy && auth?.userId && r.createdBy === auth.userId && !r.forkedFrom;
1630
1636
  if (selfAuthored || opts.allowUntrustedSetup) {
1631
1637
  const s = runSkillSetup(join(opts.out, r.slug));
1632
1638
  r.setup = s.skipped ? 'none' : (s.ok ? 'ok' : 'failed');
package/mcp/server.mjs CHANGED
@@ -267,9 +267,7 @@ const TOOL_ANNOTATIONS = {
267
267
  // Assets
268
268
  asset: { title: 'Assets', readOnlyHint: false, destructiveHint: false, openWorldHint: false, description: 'Manage project assets (CSS/JS/images/fonts referenced by frames). `action=upload` or `action=list`.' },
269
269
 
270
- // Shapes / connectors / groups / layout
271
- shape: { title: 'Add shape', readOnlyHint: false, destructiveHint: false, openWorldHint: false },
272
- group: { title: 'Create group', readOnlyHint: false, destructiveHint: false, openWorldHint: false },
270
+ // Connectors / layout
273
271
  connector: { title: 'Connectors', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Connect or disconnect frames with arrows on the surface. `action=connect` or `action=disconnect`.' },
274
272
  layout: { title: 'Auto-layout', readOnlyHint: false, destructiveHint: false, openWorldHint: false },
275
273
 
@@ -307,8 +305,6 @@ function isMutatingToolCall(name, args = {}) {
307
305
  case 'minion':
308
306
  return ['create', 'update', 'enable', 'disable', 'delete', 'test_start', 'test_say', 'test_resolve'].includes(action);
309
307
  case 'rm':
310
- case 'shape':
311
- case 'group':
312
308
  case 'connector':
313
309
  case 'layout':
314
310
  return true;
@@ -1824,7 +1820,7 @@ tool('health', {}, async () => {
1824
1820
 
1825
1821
  // ── Project management tools (direct HTTP) ────────────────────────
1826
1822
 
1827
- tool('project', 'START HERE for project management. Dispatch by `action`: list (lists all projects across all orgs — always call first), open (bind this agent session to a project; required before reading/writing frames — the org derives from the project), create (new project; org= names where it is born), update (change name/folder/description/layers), move (transfer to another org), export (the project as an OKF v0.1 bundle — <layer>/<lane>/<file>.md concepts, index.md/log.md synthesized), import (ingest an OKF bundle: concepts become markdown document frames, links between them become connectors; dryRun supported). There is no org switching: for project-less work (wiki/skills) pass org=... on the call. **Skill gate:** projects with attached skills will REJECT all mutations (write, edit, mv, rm, shape, group, connector, layout, layer, asset upload) until you have loaded each attached skill via skill(action="load"). Skills tell you HOW to do the work — they\'re not optional. Open returns the attached skill list and auto-inlines content for projects with ≤3 skills.', {
1823
+ tool('project', 'START HERE for project management. Dispatch by `action`: list (lists all projects across all orgs — always call first), open (bind this agent session to a project; required before reading/writing frames — the org derives from the project), create (new project; org= names where it is born), update (change name/folder/description/layers), move (transfer to another org), export (the project as an OKF v0.1 bundle — <layer>/<lane>/<file>.md concepts, index.md/log.md synthesized), import (ingest an OKF bundle: concepts become markdown document frames, links between them become connectors; dryRun supported). There is no org switching: for project-less work (wiki/skills) pass org=... on the call. **Skill gate:** projects with attached skills will REJECT all mutations (write, edit, mv, rm, connector, layout, layer, asset upload) until you have loaded each attached skill via skill(action="load"). Skills tell you HOW to do the work — they\'re not optional. Open returns the attached skill list and auto-inlines content for projects with ≤3 skills.', {
1828
1824
  action: z.enum(['list', 'open', 'create', 'update', 'move', 'export', 'import']).describe('Operation to perform.'),
1829
1825
  projectId: z.string().optional().describe('[open|update|move|export|import] project ID. Get IDs from action=list. For export/import: defaults to the bound project.'),
1830
1826
  name: z.string().optional().describe('[create|update] project name'),
@@ -2157,7 +2153,7 @@ tool('focus', {
2157
2153
  } catch (error) { return err(error); }
2158
2154
  });
2159
2155
 
2160
- tool('screenshot', 'Render a PNG via headless browser. `scope=frame` captures a single frame (default 1440×900, fullPage). `scope=canvas` captures a region of the project surface (default 1600×1200, typically the "plans" layer where shapes live).', {
2156
+ tool('screenshot', 'Render a PNG via headless browser. `scope=frame` captures a single frame (default 1440×900, fullPage). `scope=canvas` captures a region of the project surface (default 1600×1200, typically the "plans" layer where frames live).', {
2161
2157
  scope: z.enum(['frame', 'canvas']).describe('What to capture.'),
2162
2158
  target: z.string().optional().describe('[scope=frame] frame URL, UUID, or /{layer}/{lane}/{filename} path.'),
2163
2159
  slug: z.string().optional().describe('[scope=canvas] project slug. Defaults to the currently active project.'),
@@ -3293,56 +3289,6 @@ tool('asset', 'Manage supporting files (CSS, JS, images, fonts) in the ACTIVE PR
3293
3289
  } catch (error) { return err(error); }
3294
3290
  });
3295
3291
 
3296
- // ── Shape tool ───────────────────────────────────────────────────────
3297
-
3298
- tool('shape', 'Create a flowchart shape on the surface. Shapes are lightweight text nodes — use them for flowcharts, process diagrams, and decision trees. Connect shapes with connector(action="connect"), then call layout to auto-arrange.', {
3299
- text: z.string().describe('Text to display inside the shape. Supports multi-line with \\n.'),
3300
- shape: z.enum(['rectangle', 'diamond', 'oval', 'pill']).optional().default('rectangle').describe('Shape type: rectangle (process/action), diamond (decision), oval (start/end), pill (rounded step)'),
3301
- layer: z.string().optional().describe('Layer to place the shape in (default: plans)'),
3302
- lane: z.string().optional().describe('Lane within the layer (default: default)'),
3303
- color: z.string().optional().describe('Border color (CSS color string)'),
3304
- fill: z.string().optional().describe('Background fill color (CSS color string)'),
3305
- textColor: z.string().optional().describe('Text color (CSS color string)'),
3306
- group: z.string().optional().describe('Group ID to assign this shape to (for swim lane clustering). Use the ID returned by the group tool.'),
3307
- width: z.number().optional().describe('Explicit width in pixels (overrides auto-sizing from text)'),
3308
- height: z.number().optional().describe('Explicit height in pixels (overrides auto-sizing from text)'),
3309
- layoutIgnore: z.boolean().optional().describe('Exclude this shape from auto-layout. Use for annotations, labels, or manually positioned elements.'),
3310
- }, async ({ text, shape, layer, lane, color, fill, textColor, group, width, height, layoutIgnore }) => {
3311
- try {
3312
- const body = { text, shape };
3313
- if (layer) body.layer = layer;
3314
- if (lane) body.lane = lane;
3315
- if (color) body.color = color;
3316
- if (fill) body.fill = fill;
3317
- if (textColor) body.textColor = textColor;
3318
- if (group) body.group = group;
3319
- if (width) body.width = width;
3320
- if (height) body.height = height;
3321
- if (layoutIgnore) body.layoutIgnore = true;
3322
- const result = await api('POST', '/api/fs/shape', body);
3323
- return ok(result);
3324
- } catch (error) { return err(error); }
3325
- });
3326
-
3327
- tool('group', 'Create a group (swim lane / region) on the surface. Groups are labeled, colored background areas that visually contain shapes. Assign shapes to a group using the group parameter on the shape tool. Use layout with groups=true to auto-cluster.', {
3328
- label: z.string().describe('Group label text (displayed in the header bar)'),
3329
- color: z.string().optional().describe('Header bar and border color (CSS color string)'),
3330
- fill: z.string().optional().describe('Background fill color (CSS color string). Defaults to a tinted version of color.'),
3331
- layer: z.string().optional().describe('Layer to place the group in (default: plans)'),
3332
- lane: z.string().optional().describe('Lane within the layer (default: default)'),
3333
- order: z.number().optional().describe('Sort order for group positioning (lower = first). Default: 0'),
3334
- }, async ({ label, color, fill, layer, lane, order }) => {
3335
- try {
3336
- const body = { label };
3337
- if (color) body.color = color;
3338
- if (fill) body.fill = fill;
3339
- if (layer) body.layer = layer;
3340
- if (lane) body.lane = lane;
3341
- if (order != null) body.order = order;
3342
- const result = await api('POST', '/api/fs/group', body);
3343
- return ok(result);
3344
- } catch (error) { return err(error); }
3345
- });
3346
3292
  // ── Connector tools ───────────────────────────────────────────────
3347
3293
 
3348
3294
  tool('connector', 'Create or remove connectors (arrows) between frames on the surface. `action=connect` adds an arrow from source to target. `action=disconnect` removes one — pass either connectorId directly or source+target to find and delete.', {
@@ -3393,11 +3339,9 @@ tool('connector', 'Create or remove connectors (arrows) between frames on the su
3393
3339
 
3394
3340
  tool('layout', 'Auto-arrange frames using graph layout algorithm. Positions connected frames as a directed graph.', {
3395
3341
  direction: z.enum(['TB', 'LR', 'BT', 'RL']).optional().default('TB').describe('Layout direction: TB (top-bottom), LR (left-right), BT (bottom-top), RL (right-left)'),
3396
- groups: z.boolean().optional().default(false).describe('Enable group clustering. When true, shapes assigned to a group (via the group param) are clustered together, and group frames are resized to enclose their members.'),
3397
- }, async ({ direction, groups }) => {
3342
+ }, async ({ direction }) => {
3398
3343
  try {
3399
3344
  const body = { direction };
3400
- if (groups) body.groups = true;
3401
3345
  const result = await api('POST', '/api/layout', body);
3402
3346
  return ok(result);
3403
3347
  } catch (error) { return err(error); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.14.7",
3
+ "version": "1.14.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": [