mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 02:05:22 -04:00
feat(skills): import SKILL.md bundles from public GitHub URLs (#2576)
* feat(skills): import SKILL.md bundles from public GitHub URLs Supports GitHub tree/blob/raw links and skills.sh pages that resolve to GitHub. Installs SKILL.md plus sibling text assets under data/skills/imported/. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): admin-gate URL import and validate redirect hosts - require_admin on POST /api/skills/import-from-url (matches other skill admin routes) - reject cross-host redirects after httpx follow_redirects - test for redirect host validation Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): match Brain Add panel import/submit button styles - Skill URL Import: theme-io-btn + download icon (same as memory Import) - Add Skill submit: confirm-btn confirm-btn-primary Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): allow api.github.com during directory import Real imports hit the GitHub contents API after redirects; whitelist api.github.com and add regression tests. Shrink Import button with flex:none. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): align skill Import button with URL input row Match memory-add-input height (28px) in memory-add-row and center the download icon with flexbox instead of vertical-align hacks. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): cancel modal-body margin on skill Import button The skill Import button sits in .memory-add-row beside an input; the global .modal-body button { margin-top: 6px } rule only affected buttons, pushing Import down and misaligning the download icon. Reset margin-top and match Memory Import SVG markup at 28px row height. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(skills): surface GitHub API errors on URL import Pass through GitHub response messages (especially 403 rate limits) as SkillImportError instead of a generic download failure. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1818,6 +1818,35 @@ async function _showSkillSource(name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function importSkillFromUrl() {
|
||||
const input = document.getElementById('skill-import-url');
|
||||
const url = (input?.value || '').trim();
|
||||
if (!url) {
|
||||
uiModule.showError('Paste a GitHub or skills.sh URL first');
|
||||
return;
|
||||
}
|
||||
const btn = document.getElementById('skill-import-url-btn');
|
||||
if (btn) btn.disabled = true;
|
||||
try {
|
||||
const res = await fetch(`${API}/api/skills/import-from-url`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url }),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) throw new Error(data.detail || data.error || `HTTP ${res.status}`);
|
||||
if (input) input.value = '';
|
||||
await loadSkills();
|
||||
const name = data.skill?.name || 'skill';
|
||||
uiModule.showToast(`Imported ${name} (${data.files || 1} file(s))`);
|
||||
if (name) openSkill(name);
|
||||
} catch (err) {
|
||||
uiModule.showError('Import failed: ' + err.message);
|
||||
} finally {
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function addSkill() {
|
||||
const name = document.getElementById('new-skill-name')?.value.trim()
|
||||
|| document.getElementById('new-skill-title')?.value.trim();
|
||||
@@ -1866,6 +1895,10 @@ async function addSkill() {
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.getElementById('skill-import-url-btn')?.addEventListener('click', importSkillFromUrl);
|
||||
document.getElementById('skill-import-url')?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') importSkillFromUrl();
|
||||
});
|
||||
document.getElementById('add-skill-btn')?.addEventListener('click', addSkill);
|
||||
document.getElementById('skills-search')?.addEventListener('input', renderSkillsList);
|
||||
document.getElementById('skills-sort')?.addEventListener('change', (e) => {
|
||||
|
||||
Reference in New Issue
Block a user