drafted 1.11.12 → 1.11.13
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/agent-instructions/global.md +2 -0
- package/cli/drafted.mjs +21 -4
- package/install-mcp.sh +2 -0
- package/mcp/server.mjs +21 -4
- package/package.json +1 -1
|
@@ -29,6 +29,8 @@ Skill rules:
|
|
|
29
29
|
- Search skills before starting repeatable work: skill(action="search") or skill(action="list").
|
|
30
30
|
- Load and follow relevant skills with skill(action="load"). Read supporting skill files when needed.
|
|
31
31
|
- When the user asks to record, distill, create, save, install, or update a skill/procedure/SOP/checklist/method/protocol/playbook, create or update a Drafted skill for the org with skill tools.
|
|
32
|
+
- Every skill has a portable part — its method, SKILL.md, and any script source — and that part always belongs in Drafted, not only in a local repo. When you build a runnable skill, push the source to Drafted (skill action="push", or skill action="add" for prose) and declare how to rebuild it in the skill's `setup:` frontmatter (e.g. ["npm ci"]) so any machine or agent can regenerate it. Do not leave a reusable skill authored only as local files when Drafted is connected.
|
|
33
|
+
- Machine-specific build output (node_modules, downloaded browsers, compiled binaries) is never portable: build it into a `.skillinstall/` directory inside the skill. Drafted always strips `.skillinstall/` from a pushed bundle and skill push auto-gitignores it, so the rebuildable bundle stays local while the method and recipe stay in Drafted.
|
|
32
34
|
- Improve skills when you find a better checklist, standard, or operating method.
|
|
33
35
|
|
|
34
36
|
Project/producible rules:
|
package/cli/drafted.mjs
CHANGED
|
@@ -1828,7 +1828,7 @@ skillCmd
|
|
|
1828
1828
|
function collectSkillTree(dir) {
|
|
1829
1829
|
const root = resolve(dir);
|
|
1830
1830
|
if (!existsSync(root) || !statSync(root).isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
1831
|
-
const SKIP_DIRS = new Set(['node_modules', '.git', '.venv', 'venv', '__pycache__', 'dist', 'build', '.next', 'target', 'coverage', '.cache', '.turbo', '.gradle', 'Pods', '.terraform']);
|
|
1831
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', '.venv', 'venv', '__pycache__', 'dist', 'build', '.next', 'target', 'coverage', '.cache', '.turbo', '.gradle', 'Pods', '.terraform', '.skillinstall']);
|
|
1832
1832
|
let ignore = () => false;
|
|
1833
1833
|
const ignPath = join(root, '.skillignore');
|
|
1834
1834
|
if (existsSync(ignPath)) {
|
|
@@ -1854,9 +1854,25 @@ function collectSkillTree(dir) {
|
|
|
1854
1854
|
return out;
|
|
1855
1855
|
}
|
|
1856
1856
|
|
|
1857
|
+
// Ensure the pushed source tree's .gitignore excludes the rebuildable bundle dir,
|
|
1858
|
+
// so a skill's machine-specific build output (built into .skillinstall/ by its
|
|
1859
|
+
// `setup:` recipe) can never be committed. The server already strips .skillinstall/
|
|
1860
|
+
// from the bundle (skill-ingest DENY_DIRS) — this keeps the author's git clean too.
|
|
1861
|
+
// Idempotent; returns true only when it adds the entry.
|
|
1862
|
+
function ensureSkillInstallIgnored(dir) {
|
|
1863
|
+
try {
|
|
1864
|
+
const gi = join(dir, '.gitignore');
|
|
1865
|
+
const existing = existsSync(gi) ? readFileSync(gi, 'utf8') : '';
|
|
1866
|
+
if (existing.split(/\r?\n/).some((l) => l.trim().replace(/\/$/, '') === '.skillinstall')) return false;
|
|
1867
|
+
const body = existing && !existing.endsWith('\n') ? existing + '\n' : existing;
|
|
1868
|
+
writeFileSync(gi, body + '.skillinstall/\n');
|
|
1869
|
+
return true;
|
|
1870
|
+
} catch { return false; }
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1857
1873
|
skillCmd
|
|
1858
1874
|
.command('push')
|
|
1859
|
-
.description('Push a local source tree into a skill (bulk ingest; server
|
|
1875
|
+
.description('Push a local source tree into a Drafted skill (bulk ingest). Stores the portable bits — SKILL.md, scripts, and the setup: recipe; build output is stripped server-side. Build machine-specific output into .skillinstall/ (auto-gitignored, always stripped) and declare its rebuild in setup:.')
|
|
1860
1876
|
.option('--id <id>', 'skill id')
|
|
1861
1877
|
.option('--slug <slug>', 'skill slug (resolved to id)')
|
|
1862
1878
|
.requiredOption('--dir <dir>', 'local source tree to push')
|
|
@@ -1878,8 +1894,9 @@ skillCmd
|
|
|
1878
1894
|
});
|
|
1879
1895
|
const data = await res.json().catch(() => ({}));
|
|
1880
1896
|
if (!res.ok) { emitSkillResult(opts.format, { status: 'error', id, error: data.error || `HTTP ${res.status}` }); process.exit(1); }
|
|
1881
|
-
|
|
1882
|
-
|
|
1897
|
+
const gitignored = ensureSkillInstallIgnored(opts.dir);
|
|
1898
|
+
if (opts.format === 'json') console.log(JSON.stringify({ status: 'ok', id, hash: data.hash, count: data.count, stripped: data.stripped, gitignored, files: data.files }));
|
|
1899
|
+
else console.log(`ok\t${id}\t${data.hash}\tpushed ${data.count}${data.stripped ? `, stripped ${data.stripped}` : ''}${gitignored ? ', gitignored .skillinstall/' : ''}`);
|
|
1883
1900
|
});
|
|
1884
1901
|
|
|
1885
1902
|
program.parse();
|
package/install-mcp.sh
CHANGED
|
@@ -443,6 +443,8 @@ Skill rules:
|
|
|
443
443
|
- Search skills before starting repeatable work: skill(action="search") or skill(action="list").
|
|
444
444
|
- Load and follow relevant skills with skill(action="load"). Read supporting skill files when needed.
|
|
445
445
|
- When the user asks to record, distill, create, save, install, or update a skill/procedure/SOP/checklist/method/protocol/playbook, create or update a Drafted skill for the org with skill tools.
|
|
446
|
+
- Every skill has a portable part — its method, SKILL.md, and any script source — and that part always belongs in Drafted, not only in a local repo. When you build a runnable skill, push the source to Drafted (skill action="push", or skill action="add" for prose) and declare how to rebuild it in the skill's `setup:` frontmatter (e.g. ["npm ci"]) so any machine or agent can regenerate it. Do not leave a reusable skill authored only as local files when Drafted is connected.
|
|
447
|
+
- Machine-specific build output (node_modules, downloaded browsers, compiled binaries) is never portable: build it into a `.skillinstall/` directory inside the skill. Drafted always strips `.skillinstall/` from a pushed bundle and skill push auto-gitignores it, so the rebuildable bundle stays local while the method and recipe stay in Drafted.
|
|
446
448
|
- Improve skills when you find a better checklist, standard, or operating method.
|
|
447
449
|
|
|
448
450
|
Project/producible rules:
|
package/mcp/server.mjs
CHANGED
|
@@ -2751,7 +2751,7 @@ tool('layout', 'Auto-arrange frames using graph layout algorithm. Positions conn
|
|
|
2751
2751
|
function collectSkillTreeForPush(dir) {
|
|
2752
2752
|
const root = resolve(dir);
|
|
2753
2753
|
if (!existsSync(root) || !statSync(root).isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
2754
|
-
const SKIP = new Set(['node_modules', '.git', '.venv', 'venv', '__pycache__', 'dist', 'build', '.next', 'target', 'coverage', '.cache', '.turbo', '.gradle', 'Pods', '.terraform']);
|
|
2754
|
+
const SKIP = new Set(['node_modules', '.git', '.venv', 'venv', '__pycache__', 'dist', 'build', '.next', 'target', 'coverage', '.cache', '.turbo', '.gradle', 'Pods', '.terraform', '.skillinstall']);
|
|
2755
2755
|
const NUL = String.fromCharCode(0);
|
|
2756
2756
|
let ignore = () => false;
|
|
2757
2757
|
const ign = join(root, '.skillignore');
|
|
@@ -2778,7 +2778,22 @@ function collectSkillTreeForPush(dir) {
|
|
|
2778
2778
|
return out;
|
|
2779
2779
|
}
|
|
2780
2780
|
|
|
2781
|
-
|
|
2781
|
+
// Ensure a pushed source tree's .gitignore excludes the rebuildable bundle dir, so
|
|
2782
|
+
// a skill's machine-specific build output (built into .skillinstall/ by its `setup:`
|
|
2783
|
+
// recipe) can never be committed. The server also strips .skillinstall/ from the
|
|
2784
|
+
// bundle (skill-ingest DENY_DIRS); this keeps the author's git clean. Idempotent.
|
|
2785
|
+
function ensureSkillInstallIgnored(dir) {
|
|
2786
|
+
try {
|
|
2787
|
+
const gi = join(dir, '.gitignore');
|
|
2788
|
+
const existing = existsSync(gi) ? readFileSync(gi, 'utf8') : '';
|
|
2789
|
+
if (existing.split(/\r?\n/).some((l) => l.trim().replace(/\/$/, '') === '.skillinstall')) return false;
|
|
2790
|
+
const body = existing && !existing.endsWith('\n') ? existing + '\n' : existing;
|
|
2791
|
+
writeFileSync(gi, body + '.skillinstall/\n');
|
|
2792
|
+
return true;
|
|
2793
|
+
} catch { return false; }
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/guidelines agents can load and follow. When you build a reusable skill, author it HERE — add for prose, push to ingest a local source tree — so its method and setup: recipe live in Drafted and any machine or agent can reuse it; keep only machine-specific build output local in .skillinstall/ (always stripped on push). 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.', {
|
|
2782
2797
|
action: z.enum([
|
|
2783
2798
|
'search', 'load', 'list', 'history',
|
|
2784
2799
|
'add', 'update', 'remove',
|
|
@@ -2805,7 +2820,7 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
|
|
|
2805
2820
|
org: z.string().optional().describe('[fork|push|update] resolve/fork into this Drafted org (id or name); scopes the request without switching the session'),
|
|
2806
2821
|
setup: z.array(z.string()).optional().describe('[add|update] setup command(s) (in order) run on materialize to build a source-only skill, e.g. ["npm ci","npm run build"]'),
|
|
2807
2822
|
files: z.array(z.object({ path: z.string(), content: z.string() })).optional().describe('[push] source files to push (path + UTF-8 content); server strips artifacts + enforces caps'),
|
|
2808
|
-
dir: z.string().optional().describe('[push] local directory to push instead of files[]; walked locally (heavy dirs
|
|
2823
|
+
dir: z.string().optional().describe('[push] local directory to push instead of files[]; walked locally (heavy dirs, .skillinstall/, and .skillignore pre-filtered), server re-enforces. On push the dir\'s .gitignore is auto-updated to exclude .skillinstall/ (the rebuildable bundle).'),
|
|
2809
2824
|
deleteMissing: z.boolean().optional().describe('[push] remove stored files not present in the pushed set'),
|
|
2810
2825
|
}, async (args) => {
|
|
2811
2826
|
try {
|
|
@@ -2952,7 +2967,9 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
|
|
|
2952
2967
|
let fileList = files;
|
|
2953
2968
|
if (!fileList && dir) fileList = collectSkillTreeForPush(dir);
|
|
2954
2969
|
if (!Array.isArray(fileList) || fileList.length === 0) throw new Error('files[] (non-empty) or dir required for action=push');
|
|
2955
|
-
|
|
2970
|
+
const pushed = await api('POST', `/api/skills/${id}/files/bulk`, { files: fileList, deleteMissing: !!deleteMissing }, extra);
|
|
2971
|
+
if (dir) { try { if (ensureSkillInstallIgnored(dir)) pushed.gitignored = '.skillinstall/'; } catch { /* best-effort */ } }
|
|
2972
|
+
return ok(pushed);
|
|
2956
2973
|
}
|
|
2957
2974
|
case 'attach': {
|
|
2958
2975
|
const { skillId } = args;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.13",
|
|
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": [
|