skilld 0.4.0 → 0.4.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.
- package/README.md +19 -5
- package/dist/_chunks/detect-imports.mjs +3 -4
- package/dist/_chunks/detect-imports.mjs.map +1 -1
- package/dist/_chunks/{pool.mjs → pool2.mjs} +7 -2
- package/dist/_chunks/pool2.mjs.map +1 -0
- package/dist/_chunks/releases.mjs +361 -31
- package/dist/_chunks/releases.mjs.map +1 -1
- package/dist/_chunks/storage.mjs +241 -2
- package/dist/_chunks/storage.mjs.map +1 -1
- package/dist/_chunks/utils.d.mts +4 -3
- package/dist/_chunks/utils.d.mts.map +1 -1
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/cli.mjs +366 -41
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/retriv/index.mjs +1 -1
- package/dist/retriv/index.mjs.map +1 -1
- package/dist/sources/index.mjs +1 -2
- package/package.json +1 -1
- package/dist/_chunks/git-skills.mjs +0 -319
- package/dist/_chunks/git-skills.mjs.map +0 -1
- package/dist/_chunks/pool.mjs.map +0 -1
- package/dist/_chunks/sanitize2.mjs +0 -249
- package/dist/_chunks/sanitize2.mjs.map +0 -1
- package/dist/_chunks/sync-git.mjs +0 -69
- package/dist/_chunks/sync-git.mjs.map +0 -1
- package/dist/_chunks/sync-parallel.mjs +0 -302
- package/dist/_chunks/sync-parallel.mjs.map +0 -1
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { x as __exportAll } from "./detect-imports.mjs";
|
|
2
|
-
var sanitize_exports = /* @__PURE__ */ __exportAll({
|
|
3
|
-
processOutsideCodeBlocks: () => processOutsideCodeBlocks,
|
|
4
|
-
repairMarkdown: () => repairMarkdown,
|
|
5
|
-
sanitizeMarkdown: () => sanitizeMarkdown
|
|
6
|
-
});
|
|
7
|
-
const ZERO_WIDTH_RE = /[\u200B\u200C\uFEFF\u2060\u200D\u061C\u180E\u200E\u200F\u2028\u2029]/gu;
|
|
8
|
-
const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
|
|
9
|
-
const AGENT_DIRECTIVE_TAGS = [
|
|
10
|
-
"system",
|
|
11
|
-
"instructions",
|
|
12
|
-
"override",
|
|
13
|
-
"prompt",
|
|
14
|
-
"context",
|
|
15
|
-
"role",
|
|
16
|
-
"user-prompt",
|
|
17
|
-
"assistant",
|
|
18
|
-
"tool-use",
|
|
19
|
-
"tool-result",
|
|
20
|
-
"system-prompt",
|
|
21
|
-
"human",
|
|
22
|
-
"admin"
|
|
23
|
-
];
|
|
24
|
-
const DANGEROUS_HTML_TAGS = [
|
|
25
|
-
"script",
|
|
26
|
-
"iframe",
|
|
27
|
-
"style",
|
|
28
|
-
"meta",
|
|
29
|
-
"object",
|
|
30
|
-
"embed",
|
|
31
|
-
"form"
|
|
32
|
-
];
|
|
33
|
-
function decodeAngleBracketEntities(text) {
|
|
34
|
-
return text.replace(/</gi, "<").replace(/>/gi, ">").replace(/�*60;/g, "<").replace(/�*62;/g, ">").replace(/�*3c;/gi, "<").replace(/�*3e;/gi, ">");
|
|
35
|
-
}
|
|
36
|
-
function stripTags(text, tags) {
|
|
37
|
-
if (!tags.length) return text;
|
|
38
|
-
const tagGroup = tags.join("|");
|
|
39
|
-
const pairedRe = new RegExp(`<(${tagGroup})(\\s[^>]*)?>([\\s\\S]*?)<\\/\\1>`, "gi");
|
|
40
|
-
let result = text.replace(pairedRe, "");
|
|
41
|
-
const standaloneRe = new RegExp(`<\\/?(${tagGroup})(\\s[^>]*)?\\/?>`, "gi");
|
|
42
|
-
result = result.replace(standaloneRe, "");
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
const EXTERNAL_IMAGE_RE = /!\[([^\]]*)\]\(https?:\/\/[^)]+\)/gi;
|
|
46
|
-
const EXTERNAL_LINK_RE = /\[([^\]]*)\]\((https?:\/\/[^)]+)\)/gi;
|
|
47
|
-
const DANGEROUS_PROTOCOL_RE = /!?\[([^\]]*)\]\(\s*(javascript|data|vbscript|file)\s*:[^)]*\)/gi;
|
|
48
|
-
const DANGEROUS_PROTOCOL_ENCODED_RE = /!?\[([^\]]*)\]\(\s*(?:(?:j|%6a|%4a)(?:a|%61|%41)(?:v|%76|%56)(?:a|%61|%41)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54)|(?:d|%64|%44)(?:a|%61|%41)(?:t|%74|%54)(?:a|%61|%41)|(?:v|%76|%56)(?:b|%62|%42)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54))\s*:[^)]*\)/gi;
|
|
49
|
-
const DIRECTIVE_LINE_RE = /^[ \t]*(SYSTEM|OVERRIDE|INSTRUCTION|NOTE TO AI|IGNORE PREVIOUS|IGNORE ALL PREVIOUS|DISREGARD|FORGET ALL|NEW INSTRUCTIONS?|IMPORTANT SYSTEM|ADMIN OVERRIDE)\s*[:>].*/gim;
|
|
50
|
-
const BASE64_BLOB_RE = /^[A-Z0-9+/=]{100,}$/gim;
|
|
51
|
-
const UNICODE_ESCAPE_SPAM_RE = /(\\u[\dA-Fa-f]{4}){4,}/g;
|
|
52
|
-
function processOutsideCodeBlocks(content, fn) {
|
|
53
|
-
const lines = content.split("\n");
|
|
54
|
-
const result = [];
|
|
55
|
-
let nonCodeBuffer = [];
|
|
56
|
-
let codeBuffer = [];
|
|
57
|
-
let inCodeBlock = false;
|
|
58
|
-
let fenceChar = "";
|
|
59
|
-
let fenceLen = 0;
|
|
60
|
-
function flushNonCode() {
|
|
61
|
-
if (nonCodeBuffer.length > 0) {
|
|
62
|
-
result.push(fn(nonCodeBuffer.join("\n")));
|
|
63
|
-
nonCodeBuffer = [];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
for (const line of lines) {
|
|
67
|
-
const trimmed = line.trimStart();
|
|
68
|
-
if (!inCodeBlock) {
|
|
69
|
-
const match = trimmed.match(/^(`{3,}|~{3,})/);
|
|
70
|
-
if (match) {
|
|
71
|
-
flushNonCode();
|
|
72
|
-
inCodeBlock = true;
|
|
73
|
-
fenceChar = match[1][0];
|
|
74
|
-
fenceLen = match[1].length;
|
|
75
|
-
codeBuffer = [line];
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
nonCodeBuffer.push(line);
|
|
79
|
-
} else {
|
|
80
|
-
const match = trimmed.match(/^(`{3,}|~{3,})\s*$/);
|
|
81
|
-
if (match && match[1][0] === fenceChar && match[1].length >= fenceLen) {
|
|
82
|
-
result.push(codeBuffer.join("\n"));
|
|
83
|
-
result.push(line);
|
|
84
|
-
codeBuffer = [];
|
|
85
|
-
inCodeBlock = false;
|
|
86
|
-
fenceChar = "";
|
|
87
|
-
fenceLen = 0;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
codeBuffer.push(line);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
flushNonCode();
|
|
94
|
-
if (inCodeBlock && codeBuffer.length > 0) result.push(fn(codeBuffer.join("\n")));
|
|
95
|
-
return result.join("\n");
|
|
96
|
-
}
|
|
97
|
-
function sanitizeMarkdown(content) {
|
|
98
|
-
if (!content) return content;
|
|
99
|
-
let result = content.replace(ZERO_WIDTH_RE, "");
|
|
100
|
-
result = result.replace(HTML_COMMENT_RE, "");
|
|
101
|
-
result = stripTags(result, AGENT_DIRECTIVE_TAGS);
|
|
102
|
-
result = processOutsideCodeBlocks(result, (text) => {
|
|
103
|
-
let t = decodeAngleBracketEntities(text);
|
|
104
|
-
t = stripTags(t, [...AGENT_DIRECTIVE_TAGS, ...DANGEROUS_HTML_TAGS]);
|
|
105
|
-
t = t.replace(EXTERNAL_IMAGE_RE, "");
|
|
106
|
-
t = t.replace(EXTERNAL_LINK_RE, "$1");
|
|
107
|
-
t = t.replace(DANGEROUS_PROTOCOL_RE, "");
|
|
108
|
-
t = t.replace(DANGEROUS_PROTOCOL_ENCODED_RE, "");
|
|
109
|
-
t = t.replace(DIRECTIVE_LINE_RE, "");
|
|
110
|
-
t = t.replace(BASE64_BLOB_RE, "");
|
|
111
|
-
t = t.replace(UNICODE_ESCAPE_SPAM_RE, "");
|
|
112
|
-
return t;
|
|
113
|
-
});
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
const HEADING_NO_SPACE_RE = /^(#{1,6})([^\s#])/gm;
|
|
117
|
-
const EXCESSIVE_BLANKS_RE = /\n{4,}/g;
|
|
118
|
-
const TRAILING_WHITESPACE_RE = /[ \t]+$/gm;
|
|
119
|
-
const EMOJI_LINE_START_RE = /^\p{Extended_Pictographic}/u;
|
|
120
|
-
function closeUnclosedCodeBlocks(content) {
|
|
121
|
-
const lines = content.split("\n");
|
|
122
|
-
const result = [];
|
|
123
|
-
let inCodeBlock = false;
|
|
124
|
-
let fence = "";
|
|
125
|
-
for (const line of lines) {
|
|
126
|
-
const trimmed = line.trimStart();
|
|
127
|
-
if (!inCodeBlock) {
|
|
128
|
-
const match = trimmed.match(/^(`{3,}|~{3,})/);
|
|
129
|
-
if (match) {
|
|
130
|
-
inCodeBlock = true;
|
|
131
|
-
fence = match[1][0].repeat(match[1].length);
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
const match = trimmed.match(/^(`{3,}|~{3,})\s*$/);
|
|
135
|
-
if (match && match[1][0] === fence[0] && match[1].length >= fence.length) {
|
|
136
|
-
inCodeBlock = false;
|
|
137
|
-
fence = "";
|
|
138
|
-
} else {
|
|
139
|
-
const openMatch = trimmed.match(/^(`{3,}|~{3,})\S/);
|
|
140
|
-
if (openMatch && openMatch[1][0] === fence[0] && openMatch[1].length === fence.length) result.push(fence);
|
|
141
|
-
else if (EMOJI_LINE_START_RE.test(trimmed)) {
|
|
142
|
-
result.push(fence);
|
|
143
|
-
inCodeBlock = false;
|
|
144
|
-
fence = "";
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
result.push(line);
|
|
149
|
-
}
|
|
150
|
-
if (inCodeBlock) {
|
|
151
|
-
if (result.length > 0 && result[result.length - 1] !== "") result.push("");
|
|
152
|
-
result.push(fence);
|
|
153
|
-
}
|
|
154
|
-
return result.join("\n");
|
|
155
|
-
}
|
|
156
|
-
function cleanupCodeBlocks(content) {
|
|
157
|
-
const lines = content.split("\n");
|
|
158
|
-
const toRemove = /* @__PURE__ */ new Set();
|
|
159
|
-
let prevCodeContent;
|
|
160
|
-
let i = 0;
|
|
161
|
-
while (i < lines.length) {
|
|
162
|
-
const trimmed = lines[i].trimStart();
|
|
163
|
-
const fm = trimmed.match(/^(`{3,}|~{3,})/);
|
|
164
|
-
if (!fm) {
|
|
165
|
-
if (trimmed) prevCodeContent = void 0;
|
|
166
|
-
i++;
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
const fChar = fm[1][0];
|
|
170
|
-
const fLen = fm[1].length;
|
|
171
|
-
const openIdx = i;
|
|
172
|
-
i++;
|
|
173
|
-
let closeIdx = -1;
|
|
174
|
-
while (i < lines.length) {
|
|
175
|
-
const cm = lines[i].trimStart().match(/^(`{3,}|~{3,})\s*$/);
|
|
176
|
-
if (cm && cm[1][0] === fChar && cm[1].length >= fLen) {
|
|
177
|
-
closeIdx = i;
|
|
178
|
-
i++;
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
i++;
|
|
182
|
-
}
|
|
183
|
-
if (closeIdx === -1) continue;
|
|
184
|
-
const inner = lines.slice(openIdx + 1, closeIdx).join("\n").trim();
|
|
185
|
-
if (!inner) for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j);
|
|
186
|
-
else if (inner === prevCodeContent) for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j);
|
|
187
|
-
else prevCodeContent = inner;
|
|
188
|
-
}
|
|
189
|
-
if (!toRemove.size) return content;
|
|
190
|
-
return lines.filter((_, idx) => !toRemove.has(idx)).join("\n");
|
|
191
|
-
}
|
|
192
|
-
function closeUnclosedInlineCode(content) {
|
|
193
|
-
const lines = content.split("\n");
|
|
194
|
-
let inFence = false;
|
|
195
|
-
let fenceChar = "";
|
|
196
|
-
let fenceLen = 0;
|
|
197
|
-
return lines.map((line) => {
|
|
198
|
-
const trimmed = line.trimStart();
|
|
199
|
-
if (!inFence) {
|
|
200
|
-
const m = trimmed.match(/^(`{3,}|~{3,})/);
|
|
201
|
-
if (m) {
|
|
202
|
-
inFence = true;
|
|
203
|
-
fenceChar = m[1][0];
|
|
204
|
-
fenceLen = m[1].length;
|
|
205
|
-
return line;
|
|
206
|
-
}
|
|
207
|
-
} else {
|
|
208
|
-
const m = trimmed.match(/^(`{3,}|~{3,})\s*$/);
|
|
209
|
-
if (m && m[1][0] === fenceChar && m[1].length >= fenceLen) inFence = false;
|
|
210
|
-
return line;
|
|
211
|
-
}
|
|
212
|
-
let i = 0;
|
|
213
|
-
while (i < line.length) if (line[i] === "`") {
|
|
214
|
-
const seqStart = i;
|
|
215
|
-
while (i < line.length && line[i] === "`") i++;
|
|
216
|
-
const seqLen = i - seqStart;
|
|
217
|
-
let found = false;
|
|
218
|
-
let j = i;
|
|
219
|
-
while (j < line.length) if (line[j] === "`") {
|
|
220
|
-
const closeStart = j;
|
|
221
|
-
while (j < line.length && line[j] === "`") j++;
|
|
222
|
-
if (j - closeStart === seqLen) {
|
|
223
|
-
found = true;
|
|
224
|
-
i = j;
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
|
-
} else j++;
|
|
228
|
-
if (!found) {
|
|
229
|
-
line = `${line}${"`".repeat(seqLen)}`;
|
|
230
|
-
i = line.length;
|
|
231
|
-
}
|
|
232
|
-
} else i++;
|
|
233
|
-
return line;
|
|
234
|
-
}).join("\n");
|
|
235
|
-
}
|
|
236
|
-
function repairMarkdown(content) {
|
|
237
|
-
if (!content) return content;
|
|
238
|
-
let result = content;
|
|
239
|
-
result = closeUnclosedCodeBlocks(result);
|
|
240
|
-
result = cleanupCodeBlocks(result);
|
|
241
|
-
result = closeUnclosedInlineCode(result);
|
|
242
|
-
result = processOutsideCodeBlocks(result, (text) => text.replace(HEADING_NO_SPACE_RE, "$1 $2"));
|
|
243
|
-
result = result.replace(EXCESSIVE_BLANKS_RE, "\n\n\n");
|
|
244
|
-
result = result.replace(TRAILING_WHITESPACE_RE, "");
|
|
245
|
-
return result;
|
|
246
|
-
}
|
|
247
|
-
export { sanitizeMarkdown as n, sanitize_exports as r, repairMarkdown as t };
|
|
248
|
-
|
|
249
|
-
//# sourceMappingURL=sanitize2.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize2.mjs","names":[],"sources":["../../src/core/sanitize.ts"],"sourcesContent":["/**\n * Markdown sanitizer for prompt injection defense.\n *\n * Strips injection vectors from untrusted markdown before it reaches\n * agent-readable files (cached references, SKILL.md, search output).\n *\n * Threat model: agent instruction injection, not browser XSS.\n * Lightweight regex-based — markdown is consumed as text by AI agents.\n */\n\n/** Zero-width and invisible formatting characters used to hide text from human review */\n// eslint-disable-next-line no-misleading-character-class -- intentionally matching individual invisible chars\nconst ZERO_WIDTH_RE = /[\\u200B\\u200C\\uFEFF\\u2060\\u200D\\u061C\\u180E\\u200E\\u200F\\u2028\\u2029]/gu\n\n/** HTML comments (single-line and multi-line) */\nconst HTML_COMMENT_RE = /<!--[\\s\\S]*?-->/g\n\n/**\n * Agent directive tags — stripped globally (including inside code blocks).\n * These are never legitimate in any context; they're purely injection vectors.\n */\nconst AGENT_DIRECTIVE_TAGS = [\n 'system',\n 'instructions',\n 'override',\n 'prompt',\n 'context',\n 'role',\n 'user-prompt',\n 'assistant',\n 'tool-use',\n 'tool-result',\n 'system-prompt',\n 'human',\n 'admin',\n]\n\n/**\n * Dangerous HTML tags — stripped only outside fenced code blocks.\n * May appear legitimately in code examples (e.g. `<script setup>` in Vue docs).\n */\nconst DANGEROUS_HTML_TAGS = [\n 'script',\n 'iframe',\n 'style',\n 'meta',\n 'object',\n 'embed',\n 'form',\n]\n/**\n * Decode HTML entity-encoded angle brackets so tag stripping catches encoded variants.\n * Only decodes < and > (named, decimal, hex) — minimal to avoid false positives.\n */\nfunction decodeAngleBracketEntities(text: string): string {\n return text\n .replace(/</gi, '<')\n .replace(/>/gi, '>')\n .replace(/�*60;/g, '<')\n .replace(/�*62;/g, '>')\n .replace(/�*3c;/gi, '<')\n .replace(/�*3e;/gi, '>')\n}\n\n/** Strip paired and standalone instances of the given tag names */\nfunction stripTags(text: string, tags: string[]): string {\n if (!tags.length)\n return text\n const tagGroup = tags.join('|')\n // First strip paired tags with content between them\n const pairedRe = new RegExp(`<(${tagGroup})(\\\\s[^>]*)?>([\\\\s\\\\S]*?)<\\\\/\\\\1>`, 'gi')\n let result = text.replace(pairedRe, '')\n // Then strip any remaining standalone open/close/self-closing tags\n const standaloneRe = new RegExp(`<\\\\/?(${tagGroup})(\\\\s[^>]*)?\\\\/?>`, 'gi')\n result = result.replace(standaloneRe, '')\n return result\n}\n\n/** External image markdown:  or  */\nconst EXTERNAL_IMAGE_RE = /!\\[([^\\]]*)\\]\\(https?:\\/\\/[^)]+\\)/gi\n\n/**\n * External link markdown: [text](https://...) or [text](http://...)\n * Preserves relative links and anchors.\n */\nconst EXTERNAL_LINK_RE = /\\[([^\\]]*)\\]\\((https?:\\/\\/[^)]+)\\)/gi\n\n/** Dangerous URI protocols in links/images — match entire [text](protocol:...) */\nconst DANGEROUS_PROTOCOL_RE = /!?\\[([^\\]]*)\\]\\(\\s*(javascript|data|vbscript|file)\\s*:[^)]*\\)/gi\nconst DANGEROUS_PROTOCOL_ENCODED_RE = /!?\\[([^\\]]*)\\]\\(\\s*(?:(?:j|%6a|%4a)(?:a|%61|%41)(?:v|%76|%56)(?:a|%61|%41)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54)|(?:d|%64|%44)(?:a|%61|%41)(?:t|%74|%54)(?:a|%61|%41)|(?:v|%76|%56)(?:b|%62|%42)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54))\\s*:[^)]*\\)/gi\n\n/** Directive-style lines that look like agent instructions */\nconst DIRECTIVE_LINE_RE = /^[ \\t]*(SYSTEM|OVERRIDE|INSTRUCTION|NOTE TO AI|IGNORE PREVIOUS|IGNORE ALL PREVIOUS|DISREGARD|FORGET ALL|NEW INSTRUCTIONS?|IMPORTANT SYSTEM|ADMIN OVERRIDE)\\s*[:>].*/gim\n\n/** Base64 blob: 100+ chars of pure base64 alphabet on a single line */\nconst BASE64_BLOB_RE = /^[A-Z0-9+/=]{100,}$/gim\n\n/** Unicode escape spam: 4+ consecutive \\uXXXX sequences */\nconst UNICODE_ESCAPE_SPAM_RE = /(\\\\u[\\dA-Fa-f]{4}){4,}/g\n\n/**\n * Process content outside of fenced code blocks.\n * Uses a line-by-line state machine to properly track fence boundaries,\n * handling nested fences, mismatched lengths, and mixed backtick/tilde fences.\n * Unclosed fences are treated as non-code for security (prevents bypass via malformed fences).\n */\nexport function processOutsideCodeBlocks(content: string, fn: (text: string) => string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let nonCodeBuffer: string[] = []\n let codeBuffer: string[] = []\n let inCodeBlock = false\n let fenceChar = ''\n let fenceLen = 0\n\n function flushNonCode() {\n if (nonCodeBuffer.length > 0) {\n result.push(fn(nonCodeBuffer.join('\\n')))\n nonCodeBuffer = []\n }\n }\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n flushNonCode()\n inCodeBlock = true\n fenceChar = match[1][0]!\n fenceLen = match[1].length\n codeBuffer = [line]\n continue\n }\n nonCodeBuffer.push(line)\n }\n else {\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fenceChar && match[1].length >= fenceLen) {\n // Properly closed — emit code block as-is\n result.push(codeBuffer.join('\\n'))\n result.push(line)\n codeBuffer = []\n inCodeBlock = false\n fenceChar = ''\n fenceLen = 0\n continue\n }\n codeBuffer.push(line)\n }\n }\n\n flushNonCode()\n\n // Unclosed fence: treat as non-code so sanitization still applies\n if (inCodeBlock && codeBuffer.length > 0) {\n result.push(fn(codeBuffer.join('\\n')))\n }\n\n return result.join('\\n')\n}\n\n/**\n * Sanitize markdown content to strip prompt injection vectors.\n * Applied at every markdown emission point (cache writes, SKILL.md, search output).\n */\nexport function sanitizeMarkdown(content: string): string {\n if (!content)\n return content\n\n // Layer 1: Strip zero-width characters (global, including in code blocks)\n let result = content.replace(ZERO_WIDTH_RE, '')\n\n // Layer 2: Strip HTML comments (global, including in code blocks)\n result = result.replace(HTML_COMMENT_RE, '')\n\n // Layer 3a: Strip agent directive tags globally (never legitimate, even in code blocks)\n result = stripTags(result, AGENT_DIRECTIVE_TAGS)\n\n // Layers 3b-8: Only outside fenced code blocks\n result = processOutsideCodeBlocks(result, (text) => {\n // Layer 3b: Decode entities + strip remaining dangerous tags (HTML + entity-encoded agent directives)\n let t = decodeAngleBracketEntities(text)\n t = stripTags(t, [...AGENT_DIRECTIVE_TAGS, ...DANGEROUS_HTML_TAGS])\n\n // Layer 4: Strip external images (exfil via query params)\n t = t.replace(EXTERNAL_IMAGE_RE, '')\n\n // Layer 5: Convert external links to plain text\n t = t.replace(EXTERNAL_LINK_RE, '$1')\n\n // Layer 6: Strip dangerous protocols (raw and URL-encoded)\n t = t.replace(DANGEROUS_PROTOCOL_RE, '')\n t = t.replace(DANGEROUS_PROTOCOL_ENCODED_RE, '')\n\n // Layer 7: Strip directive-style lines\n t = t.replace(DIRECTIVE_LINE_RE, '')\n\n // Layer 8: Strip encoded payloads\n t = t.replace(BASE64_BLOB_RE, '')\n t = t.replace(UNICODE_ESCAPE_SPAM_RE, '')\n\n return t\n })\n\n return result\n}\n\n// --- Markdown repair ---\n\n/** Heading missing space after #: `##Heading` → `## Heading` */\nconst HEADING_NO_SPACE_RE = /^(#{1,6})([^\\s#])/gm\n\n/** 3+ consecutive blank lines → 2 */\nconst EXCESSIVE_BLANKS_RE = /\\n{4,}/g\n\n/** Trailing whitespace on lines (preserve intentional double-space line breaks) */\nconst TRAILING_WHITESPACE_RE = /[ \\t]+$/gm\n\n/** Emoji at start of line inside a code block — LLM forgot to close the block */\nconst EMOJI_LINE_START_RE = /^\\p{Extended_Pictographic}/u\n\n/**\n * Close unclosed fenced code blocks.\n * Walks line-by-line tracking open/close state.\n */\nfunction closeUnclosedCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let inCodeBlock = false\n let fence = ''\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n inCodeBlock = true\n fence = match[1][0]!.repeat(match[1].length)\n }\n }\n else {\n // Check for closing fence (same char, at least same length)\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fence[0] && match[1].length >= fence.length) {\n inCodeBlock = false\n fence = ''\n }\n else {\n // New fence opener inside unclosed block (same char, same length, with lang tag)\n // LLMs commonly forget to close a code block before starting a new one\n const openMatch = trimmed.match(/^(`{3,}|~{3,})\\S/)\n if (openMatch && openMatch[1][0] === fence[0] && openMatch[1].length === fence.length) {\n result.push(fence)\n // fence char/length stays the same since both match\n }\n // Emoji at line start → LLM forgot to close code block before markdown content\n else if (EMOJI_LINE_START_RE.test(trimmed)) {\n result.push(fence)\n inCodeBlock = false\n fence = ''\n }\n }\n }\n result.push(line)\n }\n\n // If still inside a code block, close it\n if (inCodeBlock) {\n // Ensure trailing newline before closing fence\n if (result.length > 0 && result[result.length - 1] !== '')\n result.push('')\n result.push(fence)\n }\n\n return result.join('\\n')\n}\n\n/**\n * Remove empty code blocks and deduplicate consecutive identical code blocks.\n * Empty blocks arise when emoji/fence recovery leaves orphaned fences.\n * Duplicate blocks arise when LLMs repeat the same code example.\n */\nfunction cleanupCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const toRemove = new Set<number>()\n let prevCodeContent: string | undefined\n let i = 0\n\n while (i < lines.length) {\n const trimmed = lines[i]!.trimStart()\n const fm = trimmed.match(/^(`{3,}|~{3,})/)\n if (!fm) {\n // Non-blank text between code blocks resets dedup tracking\n if (trimmed)\n prevCodeContent = undefined\n i++\n continue\n }\n\n const fChar = fm[1][0]!\n const fLen = fm[1].length\n const openIdx = i\n i++\n\n let closeIdx = -1\n while (i < lines.length) {\n const ct = lines[i]!.trimStart()\n const cm = ct.match(/^(`{3,}|~{3,})\\s*$/)\n if (cm && cm[1][0] === fChar && cm[1].length >= fLen) {\n closeIdx = i\n i++\n break\n }\n i++\n }\n\n if (closeIdx === -1)\n continue\n\n const inner = lines.slice(openIdx + 1, closeIdx).join('\\n').trim()\n\n if (!inner) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else if (inner === prevCodeContent) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else {\n prevCodeContent = inner\n }\n }\n\n if (!toRemove.size)\n return content\n return lines.filter((_, idx) => !toRemove.has(idx)).join('\\n')\n}\n\n/**\n * Close unclosed inline code spans.\n * Scans each line for unmatched backtick(s) and appends closing backtick(s).\n * Tracks fenced code blocks internally to handle any fence length.\n */\nfunction closeUnclosedInlineCode(content: string): string {\n const lines = content.split('\\n')\n let inFence = false\n let fenceChar = ''\n let fenceLen = 0\n\n return lines.map((line) => {\n const trimmed = line.trimStart()\n if (!inFence) {\n const m = trimmed.match(/^(`{3,}|~{3,})/)\n if (m) {\n inFence = true\n fenceChar = m[1][0]!\n fenceLen = m[1].length\n return line\n }\n }\n else {\n const m = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (m && m[1][0] === fenceChar && m[1].length >= fenceLen) {\n inFence = false\n }\n return line\n }\n\n // Outside fenced code blocks — fix unclosed inline backticks\n let i = 0\n while (i < line.length) {\n if (line[i] === '`') {\n const seqStart = i\n while (i < line.length && line[i] === '`') i++\n const seqLen = i - seqStart\n let found = false\n let j = i\n while (j < line.length) {\n if (line[j] === '`') {\n const closeStart = j\n while (j < line.length && line[j] === '`') j++\n if (j - closeStart === seqLen) {\n found = true\n i = j\n break\n }\n }\n else {\n j++\n }\n }\n if (!found) {\n line = `${line}${'`'.repeat(seqLen)}`\n i = line.length\n }\n }\n else {\n i++\n }\n }\n return line\n }).join('\\n')\n}\n\n/**\n * Repair broken markdown syntax.\n * Fixes common issues in fetched documentation:\n * - Unclosed fenced code blocks\n * - Unclosed inline code spans\n * - Missing space after heading # markers\n * - Excessive consecutive blank lines\n * - Trailing whitespace\n */\nexport function repairMarkdown(content: string): string {\n if (!content)\n return content\n\n let result = content\n\n // Fix unclosed fenced code blocks (must run before other line-level fixes)\n result = closeUnclosedCodeBlocks(result)\n\n // Remove empty and duplicate code blocks (artifacts from fence recovery)\n result = cleanupCodeBlocks(result)\n\n // Fix unclosed inline code spans\n result = closeUnclosedInlineCode(result)\n\n // Fix heading spacing (only outside code blocks)\n result = processOutsideCodeBlocks(result, text =>\n text.replace(HEADING_NO_SPACE_RE, '$1 $2'))\n\n // Normalize excessive blank lines\n result = result.replace(EXCESSIVE_BLANKS_RE, '\\n\\n\\n')\n\n // Strip trailing whitespace\n result = result.replace(TRAILING_WHITESPACE_RE, '')\n\n return result\n}\n"],"mappings":"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"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { b as targets } from "./detect-imports.mjs";
|
|
2
|
-
import { t as CACHE_DIR } from "./config.mjs";
|
|
3
|
-
import { n as sanitizeMarkdown } from "./sanitize2.mjs";
|
|
4
|
-
import "./storage.mjs";
|
|
5
|
-
import "../cache/index.mjs";
|
|
6
|
-
import { t as fetchGitSkills } from "./git-skills.mjs";
|
|
7
|
-
import "../agent/index.mjs";
|
|
8
|
-
import { S as registerProject, _ as writeLock, y as timedSpinner } from "../cli.mjs";
|
|
9
|
-
import { dirname, join } from "pathe";
|
|
10
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
-
import * as p from "@clack/prompts";
|
|
12
|
-
async function syncGitSkills(opts) {
|
|
13
|
-
const { source, agent, global: isGlobal, yes } = opts;
|
|
14
|
-
const cwd = process.cwd();
|
|
15
|
-
const agentConfig = targets[agent];
|
|
16
|
-
const baseDir = isGlobal ? join(CACHE_DIR, "skills") : join(cwd, agentConfig.skillsDir);
|
|
17
|
-
const label = source.type === "local" ? source.localPath : `${source.owner}/${source.repo}`;
|
|
18
|
-
const spin = timedSpinner();
|
|
19
|
-
spin.start(`Fetching skills from ${label}`);
|
|
20
|
-
const { skills, commitSha } = await fetchGitSkills(source, (msg) => spin.message(msg));
|
|
21
|
-
if (skills.length === 0) {
|
|
22
|
-
spin.stop(`No skills found in ${label}`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
spin.stop(`Found ${skills.length} skill(s) in ${label}`);
|
|
26
|
-
let selected = skills;
|
|
27
|
-
if (source.skillPath) selected = skills;
|
|
28
|
-
else if (skills.length > 1 && !yes) {
|
|
29
|
-
const choices = await p.multiselect({
|
|
30
|
-
message: `Select skills to install from ${label}`,
|
|
31
|
-
options: skills.map((s) => ({
|
|
32
|
-
label: s.name,
|
|
33
|
-
value: s.name,
|
|
34
|
-
hint: s.description || s.path
|
|
35
|
-
})),
|
|
36
|
-
initialValues: skills.map((s) => s.name)
|
|
37
|
-
});
|
|
38
|
-
if (p.isCancel(choices)) return;
|
|
39
|
-
const selectedNames = new Set(choices);
|
|
40
|
-
selected = skills.filter((s) => selectedNames.has(s.name));
|
|
41
|
-
}
|
|
42
|
-
mkdirSync(baseDir, { recursive: true });
|
|
43
|
-
for (const skill of selected) {
|
|
44
|
-
const skillDir = join(baseDir, skill.name);
|
|
45
|
-
mkdirSync(skillDir, { recursive: true });
|
|
46
|
-
writeFileSync(join(skillDir, "SKILL.md"), sanitizeMarkdown(skill.content));
|
|
47
|
-
if (skill.files.length > 0) for (const f of skill.files) {
|
|
48
|
-
const filePath = join(skillDir, ".skilld", f.path);
|
|
49
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
50
|
-
writeFileSync(filePath, f.content);
|
|
51
|
-
}
|
|
52
|
-
const sourceType = source.type === "local" ? "local" : source.type;
|
|
53
|
-
writeLock(baseDir, skill.name, {
|
|
54
|
-
source: sourceType,
|
|
55
|
-
repo: source.type === "local" ? source.localPath : `${source.owner}/${source.repo}`,
|
|
56
|
-
path: skill.path || void 0,
|
|
57
|
-
ref: source.ref || "main",
|
|
58
|
-
commit: commitSha,
|
|
59
|
-
syncedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
60
|
-
generator: "external"
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
if (!isGlobal) registerProject(cwd);
|
|
64
|
-
const names = selected.map((s) => `\x1B[36m${s.name}\x1B[0m`).join(", ");
|
|
65
|
-
p.log.success(`Installed ${names}`);
|
|
66
|
-
}
|
|
67
|
-
export { syncGitSkills };
|
|
68
|
-
|
|
69
|
-
//# sourceMappingURL=sync-git.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-git.mjs","names":["agents"],"sources":["../../src/commands/sync-git.ts"],"sourcesContent":["/**\n * Git skill sync — install pre-authored skills from git repos\n *\n * Mirrors the shipped skills pattern: pre-authored SKILL.md files\n * copied directly, no doc resolution or LLM generation.\n */\n\nimport type { AgentType } from '../agent'\nimport type { GitSkillSource } from '../sources/git-skills'\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { dirname, join } from 'pathe'\nimport { agents } from '../agent'\nimport { CACHE_DIR } from '../cache'\nimport { registerProject } from '../core/config'\nimport { timedSpinner } from '../core/formatting'\nimport { writeLock } from '../core/lockfile'\nimport { sanitizeMarkdown } from '../core/sanitize'\nimport { fetchGitSkills } from '../sources/git-skills'\n\nexport interface GitSyncOptions {\n source: GitSkillSource\n global: boolean\n agent: AgentType\n yes: boolean\n}\n\nexport async function syncGitSkills(opts: GitSyncOptions): Promise<void> {\n const { source, agent, global: isGlobal, yes } = opts\n const cwd = process.cwd()\n const agentConfig = agents[agent]\n const baseDir = isGlobal\n ? join(CACHE_DIR, 'skills')\n : join(cwd, agentConfig.skillsDir)\n\n const label = source.type === 'local'\n ? source.localPath!\n : `${source.owner}/${source.repo}`\n\n const spin = timedSpinner()\n spin.start(`Fetching skills from ${label}`)\n\n const { skills, commitSha } = await fetchGitSkills(source, msg => spin.message(msg))\n\n if (skills.length === 0) {\n spin.stop(`No skills found in ${label}`)\n return\n }\n\n spin.stop(`Found ${skills.length} skill(s) in ${label}`)\n\n // Select skills to install\n let selected = skills\n\n if (source.skillPath) {\n // Direct path: auto-select the matched skill\n selected = skills\n }\n else if (skills.length > 1 && !yes) {\n const choices = await p.multiselect({\n message: `Select skills to install from ${label}`,\n options: skills.map(s => ({\n label: s.name,\n value: s.name,\n hint: s.description || s.path,\n })),\n initialValues: skills.map(s => s.name),\n })\n\n if (p.isCancel(choices))\n return\n\n const selectedNames = new Set(choices)\n selected = skills.filter(s => selectedNames.has(s.name))\n }\n\n // Install each selected skill\n mkdirSync(baseDir, { recursive: true })\n\n for (const skill of selected) {\n const skillDir = join(baseDir, skill.name)\n mkdirSync(skillDir, { recursive: true })\n\n // Sanitize and write SKILL.md\n writeFileSync(join(skillDir, 'SKILL.md'), sanitizeMarkdown(skill.content))\n\n // Write supporting files to .skilld/ subdir\n if (skill.files.length > 0) {\n for (const f of skill.files) {\n const filePath = join(skillDir, '.skilld', f.path)\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, f.content)\n }\n }\n\n // Write lockfile entry\n const sourceType = source.type === 'local' ? 'local' : source.type\n writeLock(baseDir, skill.name, {\n source: sourceType,\n repo: source.type === 'local' ? source.localPath : `${source.owner}/${source.repo}`,\n path: skill.path || undefined,\n ref: source.ref || 'main',\n commit: commitSha,\n syncedAt: new Date().toISOString().split('T')[0],\n generator: 'external',\n })\n }\n\n if (!isGlobal)\n registerProject(cwd)\n\n const names = selected.map(s => `\\x1B[36m${s.name}\\x1B[0m`).join(', ')\n p.log.success(`Installed ${names}`)\n}\n"],"mappings":";;;;;;;;;;;AA2BA,eAAsB,cAAc,MAAqC;CACvE,MAAM,EAAE,QAAQ,OAAO,QAAQ,UAAU,QAAQ;CACjD,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,cAAcA,QAAO;CAC3B,MAAM,UAAU,WACZ,KAAK,WAAW,SAAS,GACzB,KAAK,KAAK,YAAY,UAAU;CAEpC,MAAM,QAAQ,OAAO,SAAS,UAC1B,OAAO,YACP,GAAG,OAAO,MAAM,GAAG,OAAO;CAE9B,MAAM,OAAO,cAAc;AAC3B,MAAK,MAAM,wBAAwB,QAAQ;CAE3C,MAAM,EAAE,QAAQ,cAAc,MAAM,eAAe,SAAQ,QAAO,KAAK,QAAQ,IAAI,CAAC;AAEpF,KAAI,OAAO,WAAW,GAAG;AACvB,OAAK,KAAK,sBAAsB,QAAQ;AACxC;;AAGF,MAAK,KAAK,SAAS,OAAO,OAAO,eAAe,QAAQ;CAGxD,IAAI,WAAW;AAEf,KAAI,OAAO,UAET,YAAW;UAEJ,OAAO,SAAS,KAAK,CAAC,KAAK;EAClC,MAAM,UAAU,MAAM,EAAE,YAAY;GAClC,SAAS,iCAAiC;GAC1C,SAAS,OAAO,KAAI,OAAM;IACxB,OAAO,EAAE;IACT,OAAO,EAAE;IACT,MAAM,EAAE,eAAe,EAAE;IAC1B,EAAE;GACH,eAAe,OAAO,KAAI,MAAK,EAAE,KAAA;GAClC,CAAC;AAEF,MAAI,EAAE,SAAS,QAAQ,CACrB;EAEF,MAAM,gBAAgB,IAAI,IAAI,QAAQ;AACtC,aAAW,OAAO,QAAO,MAAK,cAAc,IAAI,EAAE,KAAK,CAAC;;AAI1D,WAAU,SAAS,EAAE,WAAW,MAAM,CAAC;AAEvC,MAAK,MAAM,SAAS,UAAU;EAC5B,MAAM,WAAW,KAAK,SAAS,MAAM,KAAK;AAC1C,YAAU,UAAU,EAAE,WAAW,MAAM,CAAC;AAGxC,gBAAc,KAAK,UAAU,WAAW,EAAE,iBAAiB,MAAM,QAAQ,CAAC;AAG1E,MAAI,MAAM,MAAM,SAAS,EACvB,MAAK,MAAM,KAAK,MAAM,OAAO;GAC3B,MAAM,WAAW,KAAK,UAAU,WAAW,EAAE,KAAK;AAClD,aAAU,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,iBAAc,UAAU,EAAE,QAAQ;;EAKtC,MAAM,aAAa,OAAO,SAAS,UAAU,UAAU,OAAO;AAC9D,YAAU,SAAS,MAAM,MAAM;GAC7B,QAAQ;GACR,MAAM,OAAO,SAAS,UAAU,OAAO,YAAY,GAAG,OAAO,MAAM,GAAG,OAAO;GAC7E,MAAM,MAAM,QAAQ,KAAA;GACpB,KAAK,OAAO,OAAO;GACnB,QAAQ;GACR,2BAAU,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC;GAC9C,WAAW;GACZ,CAAC;;AAGJ,KAAI,CAAC,SACH,iBAAgB,IAAI;CAEtB,MAAM,QAAQ,SAAS,KAAI,MAAK,WAAW,EAAE,KAAK,SAAS,CAAC,KAAK,KAAK;AACtE,GAAE,IAAI,QAAQ,aAAa,QAAQ"}
|