drafted 1.10.1 → 1.10.2

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 +34 -18
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -832,6 +832,34 @@ function summarizeSkillForSearch(skill) {
832
832
  };
833
833
  }
834
834
 
835
+ // Ultra-lean catalog entry for browsing large libraries within an agent's token
836
+ // budget: just enough to recognize a skill and load it by slug. Full
837
+ // description/triggerPatterns are only needed on load.
838
+ function compactSkillEntry(skill) {
839
+ if (!skill || typeof skill !== 'object') return skill;
840
+ return { slug: skill.slug, name: skill.name, tags: skill.tags || [] };
841
+ }
842
+
843
+ // Shape a {skills:[...]} catalog result (search or list) with limit/offset
844
+ // pagination and an optional compact mode, so an 82-entry global catalog stays
845
+ // within an agent's token budget. Mirrors how `search` already caps client-side.
846
+ function shapeSkillCatalog(result, { limit, offset = 0, compact = false } = {}) {
847
+ if (!Array.isArray(result?.skills)) return result;
848
+ const total = result.skills.length;
849
+ const start = Math.max(0, Math.floor(Number(offset) || 0));
850
+ const cap = Math.min(Math.max(1, Math.floor(Number(limit) || 25)), 100);
851
+ const page = result.skills.slice(start, start + cap);
852
+ result.totalAvailable = total;
853
+ result.offset = start;
854
+ result.returned = page.length;
855
+ result.truncated = start + page.length < total;
856
+ result.skills = page.map(compact ? compactSkillEntry : summarizeSkillForSearch);
857
+ result.note = compact
858
+ ? 'Compact catalog: {slug,name,tags} only. Load full content with skill(action="load", skill="<slug>"). Pass compact=false for descriptions/triggerPatterns; use offset to page.'
859
+ : 'Search/list returns skill summaries only (no SKILL.md body). Use skill(action="load", skill="<slug-or-id>") for full content. Pass compact=true for a leaner {slug,name,tags} catalog; use limit/offset to page.';
860
+ return result;
861
+ }
862
+
835
863
  // Build the structuredContent shape that the frame-preview widget reads.
836
864
  // Tools that produce or return a frame (read/write/edit) call this so the
837
865
  // model and widget see the same metadata view.
@@ -2663,7 +2691,9 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2663
2691
  query: z.string().optional().describe('[search] term to match against name/description/content'),
2664
2692
  tags: z.array(z.string()).optional().describe('[search] filter by tags; [add|update] tag list'),
2665
2693
  scope: z.enum(['all', 'org', 'global']).optional().describe('[search|list] library scope (default: all for search; when provided to list, lists the library instead of project/org attachments)'),
2666
- limit: z.number().optional().describe('[search] max results (default 25, max 100)'),
2694
+ limit: z.number().optional().describe('[search|list] max results per page (default 25, max 100)'),
2695
+ offset: z.number().optional().describe('[search|list] skip N results for pagination (default 0). Response reports totalAvailable/returned/truncated.'),
2696
+ compact: z.boolean().optional().describe('[search|list] return only {slug,name,tags} per skill instead of full summaries — for browsing large catalogs within token budget'),
2667
2697
  skill: z.string().optional().describe('[load] skill ID (UUID) or slug'),
2668
2698
  skillId: z.string().optional().describe('[update|remove|attach|detach|favorite|unfavorite|read_file|update_file] skill ID'),
2669
2699
  projectId: z.string().optional().describe('[list] project to list skills for (defaults to active project; falls back to org-attached skills if no project)'),
@@ -2682,7 +2712,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2682
2712
  const { action } = args;
2683
2713
  switch (action) {
2684
2714
  case 'search': {
2685
- const { query, tags, scope = 'all', limit = 25 } = args;
2715
+ const { query, tags, scope = 'all', limit, offset, compact } = args;
2686
2716
  const params = new URLSearchParams();
2687
2717
  if (query) params.set('q', query);
2688
2718
  if (tags?.length) params.set('tags', tags.join(','));
@@ -2691,17 +2721,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2691
2721
  const endpoint = query ? '/api/skills/search' : '/api/skills';
2692
2722
  const result = await api('GET', `${endpoint}${qs ? '?' + qs : ''}`);
2693
2723
  markSearched(getSessionState().gates, 'skill');
2694
- const cap = Math.min(Math.max(1, limit || 25), 100);
2695
- if (Array.isArray(result?.skills)) {
2696
- if (result.skills.length > cap) {
2697
- result.totalAvailable = result.skills.length;
2698
- result.truncated = true;
2699
- result.skills = result.skills.slice(0, cap);
2700
- }
2701
- result.skills = result.skills.map(summarizeSkillForSearch);
2702
- result.note = 'Search/list returns skill summaries only. Use skill(action="load", skill="<slug-or-id>") to read full SKILL.md content and supporting file list.';
2703
- }
2704
- return ok(result);
2724
+ return ok(shapeSkillCatalog(result, { limit, offset, compact }));
2705
2725
  }
2706
2726
  case 'load': {
2707
2727
  const { skill } = args;
@@ -2718,11 +2738,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2718
2738
  params.set('scope', args.scope || 'all');
2719
2739
  if (args.tags?.length) params.set('tags', args.tags.join(','));
2720
2740
  const result = await api('GET', `/api/skills?${params.toString()}`);
2721
- if (Array.isArray(result?.skills)) {
2722
- result.skills = result.skills.map(summarizeSkillForSearch);
2723
- result.note = 'Library list returns skill summaries only. Use skill(action="load", skill="<slug-or-id>") to read full SKILL.md content and supporting file list.';
2724
- }
2725
- return ok(result);
2741
+ return ok(shapeSkillCatalog(result, { limit: args.limit, offset: args.offset, compact: args.compact }));
2726
2742
  }
2727
2743
 
2728
2744
  // Prefer the explicit projectId param; otherwise the active project;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.10.1",
3
+ "version": "1.10.2",
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": [