drafted 1.5.0 → 1.6.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 +24 -15
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -1545,8 +1545,8 @@ tool('batch', 'Batch operations on the ACTIVE PROJECT. Response includes "projec
1545
1545
 
1546
1546
  // ── Asset tools ──────────────────────────────────────────────────
1547
1547
 
1548
- tool('asset', 'Manage supporting files (CSS, JS, images, fonts) in the ACTIVE PROJECT. Assets are referenced by frames via relative paths — e.g., if your HTML has <link href="css/styles.css">, upload with asset_path="css/styles.css". Assets are NOT frames — they don\'t appear on the canvas. `action=upload` to add/replace, `action=list` to browse.', {
1549
- action: z.enum(['upload', 'list']).describe('Operation to perform.'),
1548
+ tool('asset', 'Manage supporting files (CSS, JS, images, fonts) in the ACTIVE PROJECT. Assets are referenced by frames via relative paths — e.g., if your HTML has <link href="css/styles.css">, upload with asset_path="css/styles.css". Assets are NOT frames — they don\'t appear on the canvas. `action=upload` to add/replace, `action=list` to browse, `action=rm` to delete.', {
1549
+ action: z.enum(['upload', 'list', 'rm']).describe('Operation to perform.'),
1550
1550
  asset_path: z.string().optional().describe('[upload] relative asset path (e.g. "css/styles.css"). Must match the path used in HTML references.'),
1551
1551
  file_path: z.string().optional().describe('[upload] absolute path to a local file. Mutually exclusive with content/base64.'),
1552
1552
  content: z.string().optional().describe('[upload] text content (for CSS/JS). Mutually exclusive with file_path/base64.'),
@@ -1589,6 +1589,14 @@ tool('asset', 'Manage supporting files (CSS, JS, images, fonts) in the ACTIVE PR
1589
1589
  const query = frame_id ? `?frameId=${frame_id}` : '';
1590
1590
  return ok(await api('GET', `/api/projects/${projectId}/assets${query}`));
1591
1591
  }
1592
+ if (action === 'rm') {
1593
+ const skillErr = await checkProjectSkills(projectId);
1594
+ if (skillErr) return err(new Error(skillErr));
1595
+ const { asset_path } = args;
1596
+ if (!asset_path) throw new Error('asset_path is required for action=rm');
1597
+ if (asset_path.includes('..')) throw new Error('asset_path must not contain ".."');
1598
+ return ok(await api('DELETE', `/api/projects/${projectId}/assets/${asset_path}`));
1599
+ }
1592
1600
  throw new Error(`Unknown asset action: ${action}`);
1593
1601
  } catch (error) { return err(error); }
1594
1602
  });
@@ -1960,20 +1968,18 @@ tool('wiki', 'Per-org wiki. Markdown pages with paths as hierarchy. You and othe
1960
1968
  }
1961
1969
 
1962
1970
  // ── read ────────────────────────────────────────────────────
1971
+ // Returns content in hashline format (`lineNum:hash|content`) so the
1972
+ // agent can produce hashline edit operations. Mirrors frame.read.
1963
1973
  case 'read': {
1964
1974
  const { path: readPath, lines: readLines } = args;
1965
1975
  if (!readPath) throw new Error('path required for action=read');
1966
1976
  const normalized = normalizeWikiPath(readPath);
1967
- const page = await api('GET', `/api/wiki/page?path=${encodeURIComponent(normalized)}`);
1968
- // Client-side line slice — server returns full content; we trim if `lines` was passed.
1969
- let outContent = page.content;
1970
- if (readLines && typeof outContent === 'string') {
1971
- const m = readLines.match(/^(\d+)-(\d+)$/);
1972
- if (!m) throw new Error(`lines must be "N-M" (e.g. "10-50"), got: ${readLines}`);
1973
- const [start, end] = [parseInt(m[1]), parseInt(m[2])];
1974
- const allLines = outContent.split('\n');
1975
- outContent = allLines.slice(Math.max(0, start - 1), end).join('\n');
1977
+ const params = new URLSearchParams({ path: normalized, format: 'hashline' });
1978
+ if (readLines) {
1979
+ if (!/^\d+-\d+$/.test(readLines)) throw new Error(`lines must be "N-M" (e.g. "10-50"), got: ${readLines}`);
1980
+ params.set('lines', readLines);
1976
1981
  }
1982
+ const page = await api('GET', `/api/wiki/page?${params.toString()}`);
1977
1983
  // Get backlink count via search (approximate)
1978
1984
  let backlinkCount = 0;
1979
1985
  try {
@@ -1985,7 +1991,8 @@ tool('wiki', 'Per-org wiki. Markdown pages with paths as hierarchy. You and othe
1985
1991
  title: page.title,
1986
1992
  type: page.type,
1987
1993
  frontmatter: page.frontmatter,
1988
- content: outContent,
1994
+ content: page.content,
1995
+ totalLines: page.totalLines,
1989
1996
  lastEditedBy: page.updatedBy,
1990
1997
  lastEditedAt: page.updatedAt,
1991
1998
  backlinkCount,
@@ -2069,15 +2076,17 @@ tool('wiki', 'Per-org wiki. Markdown pages with paths as hierarchy. You and othe
2069
2076
  }
2070
2077
 
2071
2078
  // ── edit ────────────────────────────────────────────────────
2079
+ // Hashlines come from `read` (which now formats content as
2080
+ // `lineNum:hash|content`). The server applies the ops via the same
2081
+ // hashline algorithm — no client-side re-hashing, no algorithm drift.
2072
2082
  case 'edit': {
2073
2083
  const { path: editPath, operations: editOps } = args;
2074
2084
  if (!editPath) throw new Error('path required for action=edit');
2075
2085
  if (!Array.isArray(editOps) || editOps.length === 0) throw new Error('operations (array) required for action=edit');
2076
2086
  const normalized = normalizeWikiPath(editPath);
2077
2087
  const page = await api('GET', `/api/wiki/page?path=${encodeURIComponent(normalized)}`);
2078
- const newContent = applyHashlineOps(page.content || '', editOps);
2079
- const result = await api('PATCH', `/api/wiki/pages/${page.id}`, { content: newContent });
2080
- return ok(withOrg({ path: result.path, id: result.id, updated: true }));
2088
+ const result = await api('POST', `/api/wiki/pages/${page.id}/edit`, { operations: editOps });
2089
+ return ok(withOrg({ path: result.path, id: result.id, updated: true, applied: result.applied }));
2081
2090
  }
2082
2091
 
2083
2092
  // ── mv ──────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Drafted CLI - Design preview server for Claude agents",
5
5
  "type": "module",
6
6
  "files": [