drafted 1.10.1 → 1.11.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.
- package/mcp/server.mjs +49 -21
- 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.
|
|
@@ -2651,9 +2679,9 @@ function collectSkillTreeForPush(dir) {
|
|
|
2651
2679
|
return out;
|
|
2652
2680
|
}
|
|
2653
2681
|
|
|
2654
|
-
tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/guidelines agents can load and follow. Dispatch by `action`: search/load/list for discovery; add/update/remove for org skills; fork/push for source-only skills; attach/detach for project binding; favorite/unfavorite for personal pins; read_file/update_file for supporting files inside a skill directory.', {
|
|
2682
|
+
tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/guidelines agents can load and follow. Dispatch by `action`: search/load/list for discovery; history for a skill\'s version git-log; add/update/remove for org skills; fork/push for source-only skills; attach/detach for project binding; favorite/unfavorite for personal pins; read_file/update_file for supporting files inside a skill directory.', {
|
|
2655
2683
|
action: z.enum([
|
|
2656
|
-
'search', 'load', 'list',
|
|
2684
|
+
'search', 'load', 'list', 'history',
|
|
2657
2685
|
'add', 'update', 'remove',
|
|
2658
2686
|
'fork', 'push',
|
|
2659
2687
|
'attach', 'detach',
|
|
@@ -2663,8 +2691,11 @@ 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)'),
|
|
2667
|
-
|
|
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'),
|
|
2697
|
+
skill: z.string().optional().describe('[load|history] skill ID (UUID) or slug'),
|
|
2698
|
+
version: z.number().optional().describe('[history] fetch this version number\'s full snapshot (content included); omit for the reverse-chron version list'),
|
|
2668
2699
|
skillId: z.string().optional().describe('[update|remove|attach|detach|favorite|unfavorite|read_file|update_file] skill ID'),
|
|
2669
2700
|
projectId: z.string().optional().describe('[list] project to list skills for (defaults to active project; falls back to org-attached skills if no project)'),
|
|
2670
2701
|
name: z.string().optional().describe('[add|update] skill name'),
|
|
@@ -2682,7 +2713,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
|
|
|
2682
2713
|
const { action } = args;
|
|
2683
2714
|
switch (action) {
|
|
2684
2715
|
case 'search': {
|
|
2685
|
-
const { query, tags, scope = 'all', limit
|
|
2716
|
+
const { query, tags, scope = 'all', limit, offset, compact } = args;
|
|
2686
2717
|
const params = new URLSearchParams();
|
|
2687
2718
|
if (query) params.set('q', query);
|
|
2688
2719
|
if (tags?.length) params.set('tags', tags.join(','));
|
|
@@ -2691,17 +2722,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
|
|
|
2691
2722
|
const endpoint = query ? '/api/skills/search' : '/api/skills';
|
|
2692
2723
|
const result = await api('GET', `${endpoint}${qs ? '?' + qs : ''}`);
|
|
2693
2724
|
markSearched(getSessionState().gates, 'skill');
|
|
2694
|
-
|
|
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);
|
|
2725
|
+
return ok(shapeSkillCatalog(result, { limit, offset, compact }));
|
|
2705
2726
|
}
|
|
2706
2727
|
case 'load': {
|
|
2707
2728
|
const { skill } = args;
|
|
@@ -2712,17 +2733,24 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
|
|
|
2712
2733
|
if (result?.id) getSessionState().loadedSkillIds.add(result.id);
|
|
2713
2734
|
return ok(result);
|
|
2714
2735
|
}
|
|
2736
|
+
case 'history': {
|
|
2737
|
+
// Skill version git-log. Omit `version` for the list; pass it for a
|
|
2738
|
+
// single version's full snapshot (content for view/diff).
|
|
2739
|
+
const { skillId, skill: slugArg, version, org } = args;
|
|
2740
|
+
const extra = org ? { 'X-Drafted-Org': org } : {};
|
|
2741
|
+
let id = skillId;
|
|
2742
|
+
if (!id && slugArg) { const s = await api('GET', `/api/skills/slug/${slugArg}`, undefined, extra); id = s.id; }
|
|
2743
|
+
if (!id) throw new Error('skillId or skill (slug) required for action=history');
|
|
2744
|
+
if (version != null) return ok(await api('GET', `/api/skills/${id}/versions/${version}`, undefined, extra));
|
|
2745
|
+
return ok(await api('GET', `/api/skills/${id}/versions`, undefined, extra));
|
|
2746
|
+
}
|
|
2715
2747
|
case 'list': {
|
|
2716
2748
|
if (args.scope || args.tags?.length) {
|
|
2717
2749
|
const params = new URLSearchParams();
|
|
2718
2750
|
params.set('scope', args.scope || 'all');
|
|
2719
2751
|
if (args.tags?.length) params.set('tags', args.tags.join(','));
|
|
2720
2752
|
const result = await api('GET', `/api/skills?${params.toString()}`);
|
|
2721
|
-
|
|
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);
|
|
2753
|
+
return ok(shapeSkillCatalog(result, { limit: args.limit, offset: args.offset, compact: args.compact }));
|
|
2726
2754
|
}
|
|
2727
2755
|
|
|
2728
2756
|
// 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.
|
|
3
|
+
"version": "1.11.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": [
|