Add sync-to-DB and delete-from-DB actions for skills
- POST /api/admin/skills/{slug}/sync — read SKILL.md from disk, insert/update DB
- DELETE /api/admin/skills/{slug} — remove skill from DB (keeps disk files)
- UI: Sync/Re-sync and Delete buttons per skill in the skills list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1293,6 +1293,16 @@ async function loadSkillList() {
|
||||
const updatedStr = s.updated_at ? new Date(s.updated_at).toLocaleDateString('he-IL') : '—';
|
||||
const inDb = s.db_markdown_chars > 0;
|
||||
const onDisk = s.disk_exists;
|
||||
// Action buttons
|
||||
const actions = [];
|
||||
if (onDisk && !inDb) {
|
||||
actions.push(`<button class="btn btn-sm btn-primary" onclick="syncSkill('${esc(s.slug)}')">Sync to DB</button>`);
|
||||
} else if (onDisk && inDb) {
|
||||
actions.push(`<button class="btn btn-sm btn-secondary" onclick="syncSkill('${esc(s.slug)}')">Re-sync</button>`);
|
||||
}
|
||||
if (inDb) {
|
||||
actions.push(`<button class="btn btn-sm btn-outline" style="color:#e94560;border-color:#e94560" onclick="deleteSkill('${esc(s.slug)}')">Delete from DB</button>`);
|
||||
}
|
||||
return `
|
||||
<div class="skill-item">
|
||||
<span class="skill-icon">🔌</span>
|
||||
@@ -1309,6 +1319,7 @@ async function loadSkillList() {
|
||||
${inDb ? '<span class="badge-ok">DB</span>' : '<span class="badge-warn">No DB</span>'}
|
||||
${onDisk ? '<span class="badge-ok">Disk</span>' : '<span class="badge-warn">No Disk</span>'}
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;flex-shrink:0">${actions.join('')}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
} catch (e) {
|
||||
@@ -1376,6 +1387,37 @@ async function installSkill(file) {
|
||||
}
|
||||
}
|
||||
|
||||
async function syncSkill(slug) {
|
||||
try {
|
||||
const res = await fetch(API + '/admin/skills/' + encodeURIComponent(slug) + '/sync', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
toast(data.detail || 'Sync failed', 'error');
|
||||
return;
|
||||
}
|
||||
toast(`${data.action}: ${slug} (${data.markdown_chars.toLocaleString()} chars, ${data.file_inventory.length} files)`, 'success');
|
||||
loadSkillList();
|
||||
} catch (e) {
|
||||
toast('Network error', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteSkill(slug) {
|
||||
if (!confirm(`Delete "${slug}" from DB? Files on disk will NOT be removed.`)) return;
|
||||
try {
|
||||
const res = await fetch(API + '/admin/skills/' + encodeURIComponent(slug), { method: 'DELETE' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
toast(data.detail || 'Delete failed', 'error');
|
||||
return;
|
||||
}
|
||||
toast(`Deleted: ${slug}`, 'success');
|
||||
loadSkillList();
|
||||
} catch (e) {
|
||||
toast('Network error', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function confirmRestart() {
|
||||
if (!confirm('Restart Paperclip?')) return;
|
||||
restartPaperclip();
|
||||
|
||||
Reference in New Issue
Block a user