@wendongfly/myhi 1.3.14 → 1.3.16
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/dist/chat.html +240 -0
- package/dist/index.js +1 -1
- package/dist/index.min.js +105 -103
- package/package.json +1 -1
package/dist/chat.html
CHANGED
|
@@ -334,6 +334,7 @@
|
|
|
334
334
|
<button class="sk sk-claude" onclick="doSlashCmd('/rename')">命名</button>
|
|
335
335
|
<button class="sk sk-claude" onclick="openGitSheet()" style="color:#3fb950">提交</button>
|
|
336
336
|
<button class="sk sk-claude" onclick="showMemory()">记忆</button>
|
|
337
|
+
<button class="sk sk-claude" onclick="openSkillSheet()" style="color:#9d5cf5">技能</button>
|
|
337
338
|
<span id="sk-custom-skills"></span>
|
|
338
339
|
</div>
|
|
339
340
|
</div>
|
|
@@ -451,6 +452,51 @@
|
|
|
451
452
|
</div>
|
|
452
453
|
</div>
|
|
453
454
|
|
|
455
|
+
<div id="skill-sheet" class="action-sheet">
|
|
456
|
+
<div class="action-sheet-backdrop" onclick="closeSkillSheet()"></div>
|
|
457
|
+
<div class="action-sheet-box" style="max-height:85vh;display:flex;flex-direction:column">
|
|
458
|
+
<div class="action-sheet-title" style="display:flex;align-items:center;justify-content:space-between;text-align:left">
|
|
459
|
+
<span id="skill-sheet-title">技能管理</span>
|
|
460
|
+
<div style="display:flex;gap:0.4rem">
|
|
461
|
+
<button onclick="showSkillImport()" style="background:#21262d;color:#c9d1d9;border:1px solid #30363d;border-radius:6px;padding:0.3rem 0.7rem;font-size:0.75rem;cursor:pointer">Git 安装</button>
|
|
462
|
+
<button onclick="showSkillEditor()" style="background:#7c3aed;color:#fff;border:none;border-radius:6px;padding:0.3rem 0.7rem;font-size:0.75rem;cursor:pointer">+ 新建</button>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
<!-- 技能列表视图 -->
|
|
466
|
+
<div id="skill-list-view" style="flex:1;overflow-y:auto;scrollbar-width:thin">
|
|
467
|
+
<div style="text-align:center;color:#8b949e;padding:2rem 0">加载中...</div>
|
|
468
|
+
</div>
|
|
469
|
+
<!-- 技能编辑视图 -->
|
|
470
|
+
<div id="skill-edit-view" style="display:none;flex:1;overflow-y:auto;scrollbar-width:thin;padding:0.3rem 0.2rem">
|
|
471
|
+
<div style="display:flex;flex-direction:column;gap:0.4rem">
|
|
472
|
+
<input id="skill-name" class="slash-inp" placeholder="技能名称 (如 mosaic-names)" autocomplete="off">
|
|
473
|
+
<select id="skill-scope" class="slash-inp" style="background:#0d1117;color:#c9d1d9;border:1px solid #30363d;border-radius:8px;padding:0.5rem;font-size:0.82rem">
|
|
474
|
+
<option value="user">用户级 (~/.claude/commands/)</option>
|
|
475
|
+
<option value="project">项目级 (.claude/commands/)</option>
|
|
476
|
+
</select>
|
|
477
|
+
<textarea id="skill-content" class="slash-inp" placeholder="# 技能标题 技能描述和指令内容..." rows="10" style="resize:vertical;font-family:monospace;font-size:0.78rem;line-height:1.5"></textarea>
|
|
478
|
+
</div>
|
|
479
|
+
<button class="action-sheet-cancel" style="background:#7c3aed;color:#fff;font-weight:600;margin-top:0.5rem;margin-bottom:0.4rem" onclick="saveSkill()">保存</button>
|
|
480
|
+
<button class="action-sheet-cancel" onclick="backToSkillList()">返回列表</button>
|
|
481
|
+
</div>
|
|
482
|
+
<!-- Git 安装视图 -->
|
|
483
|
+
<div id="skill-import-view" style="display:none;flex:1;overflow-y:auto;scrollbar-width:thin;padding:0.3rem 0.2rem">
|
|
484
|
+
<div style="display:flex;flex-direction:column;gap:0.4rem">
|
|
485
|
+
<input id="skill-git-url" class="slash-inp" placeholder="Git 仓库地址 (https:// 或 git@...)" autocomplete="off">
|
|
486
|
+
<select id="skill-import-scope" class="slash-inp" style="background:#0d1117;color:#c9d1d9;border:1px solid #30363d;border-radius:8px;padding:0.5rem;font-size:0.82rem">
|
|
487
|
+
<option value="user">安装到用户级 (~/.claude/commands/)</option>
|
|
488
|
+
<option value="project">安装到项目级 (.claude/commands/)</option>
|
|
489
|
+
</select>
|
|
490
|
+
</div>
|
|
491
|
+
<div id="skill-import-preview" style="display:none;margin-top:0.5rem;padding:0.5rem;background:#0d1117;border:1px solid #30363d;border-radius:8px;font-size:0.78rem;max-height:30vh;overflow-y:auto;scrollbar-width:thin"></div>
|
|
492
|
+
<button id="skill-import-scan-btn" class="action-sheet-cancel" style="background:#7c3aed;color:#fff;font-weight:600;margin-top:0.5rem;margin-bottom:0.4rem" onclick="scanGitSkills()">扫描技能</button>
|
|
493
|
+
<button id="skill-import-install-btn" class="action-sheet-cancel" style="display:none;background:#3fb950;color:#000;font-weight:600;margin-bottom:0.4rem" onclick="installGitSkills()">安装选中的技能</button>
|
|
494
|
+
<button class="action-sheet-cancel" onclick="backToSkillList()">返回列表</button>
|
|
495
|
+
</div>
|
|
496
|
+
<button id="skill-close-btn" class="action-sheet-cancel" onclick="closeSkillSheet()">关闭</button>
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
|
|
454
500
|
<div id="status-overlay">连接中...</div>
|
|
455
501
|
|
|
456
502
|
<script type="module">
|
|
@@ -1950,6 +1996,200 @@
|
|
|
1950
1996
|
// 加入会话后加载 skill
|
|
1951
1997
|
socket.on('joined', () => { if (currentSession?.mode === 'agent') loadSkills(); });
|
|
1952
1998
|
|
|
1999
|
+
// ── 技能管理 ──────────────────────────────────
|
|
2000
|
+
let _editingSkill = null; // 编辑中的技能名,null 表示新建
|
|
2001
|
+
|
|
2002
|
+
window.openSkillSheet = async function() {
|
|
2003
|
+
document.getElementById('skill-sheet').classList.add('open');
|
|
2004
|
+
document.getElementById('skill-list-view').style.display = '';
|
|
2005
|
+
document.getElementById('skill-edit-view').style.display = 'none';
|
|
2006
|
+
document.getElementById('skill-close-btn').style.display = '';
|
|
2007
|
+
await refreshSkillList();
|
|
2008
|
+
};
|
|
2009
|
+
window.closeSkillSheet = function() {
|
|
2010
|
+
document.getElementById('skill-sheet').classList.remove('open');
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
async function refreshSkillList() {
|
|
2014
|
+
const listEl = document.getElementById('skill-list-view');
|
|
2015
|
+
listEl.innerHTML = '<div style="text-align:center;color:#8b949e;padding:2rem 0">加载中...</div>';
|
|
2016
|
+
try {
|
|
2017
|
+
const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}&detail=1`);
|
|
2018
|
+
const data = await resp.json();
|
|
2019
|
+
if (!data.skills || !data.skills.length) {
|
|
2020
|
+
listEl.innerHTML = '<div style="text-align:center;color:#8b949e;padding:2rem 0">暂无技能,点击右上角 + 新建</div>';
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
listEl.innerHTML = data.skills.map(s => `
|
|
2024
|
+
<div class="action-sheet-item" style="gap:0.5rem">
|
|
2025
|
+
<div style="min-width:0;flex:1;cursor:pointer" onclick="editSkill('${escHtml(s.name)}','${escHtml(s.source)}')">
|
|
2026
|
+
<div style="color:#9d5cf5;font-weight:500">/${escHtml(s.name)}</div>
|
|
2027
|
+
<div class="desc">${escHtml(s.desc)} · ${s.source === 'project' ? '项目级' : '用户级'}</div>
|
|
2028
|
+
</div>
|
|
2029
|
+
<button onclick="deleteSkill('${escHtml(s.name)}','${escHtml(s.source)}')" style="background:none;border:none;color:#f85149;font-size:0.78rem;cursor:pointer;padding:0.3rem 0.5rem;white-space:nowrap">删除</button>
|
|
2030
|
+
</div>
|
|
2031
|
+
`).join('');
|
|
2032
|
+
} catch (e) {
|
|
2033
|
+
listEl.innerHTML = `<div style="text-align:center;color:#f85149;padding:2rem 0">加载失败: ${e.message}</div>`;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
window.showSkillEditor = function(name, scope, content) {
|
|
2038
|
+
_editingSkill = name || null;
|
|
2039
|
+
document.getElementById('skill-list-view').style.display = 'none';
|
|
2040
|
+
document.getElementById('skill-edit-view').style.display = '';
|
|
2041
|
+
document.getElementById('skill-close-btn').style.display = 'none';
|
|
2042
|
+
document.getElementById('skill-sheet-title').textContent = name ? `编辑 /${name}` : '新建技能';
|
|
2043
|
+
document.getElementById('skill-name').value = name || '';
|
|
2044
|
+
document.getElementById('skill-name').disabled = !!name;
|
|
2045
|
+
document.getElementById('skill-scope').value = scope || 'user';
|
|
2046
|
+
document.getElementById('skill-content').value = content || '# 技能标题\n\n在此输入技能指令...\n';
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
window.editSkill = async function(name, source) {
|
|
2050
|
+
try {
|
|
2051
|
+
const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}&detail=1`);
|
|
2052
|
+
const data = await resp.json();
|
|
2053
|
+
const skill = data.skills?.find(s => s.name === name && s.source === source);
|
|
2054
|
+
showSkillEditor(name, source, skill?.content || '');
|
|
2055
|
+
} catch {}
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
window.backToSkillList = function() {
|
|
2059
|
+
document.getElementById('skill-list-view').style.display = '';
|
|
2060
|
+
document.getElementById('skill-edit-view').style.display = 'none';
|
|
2061
|
+
document.getElementById('skill-import-view').style.display = 'none';
|
|
2062
|
+
document.getElementById('skill-close-btn').style.display = '';
|
|
2063
|
+
document.getElementById('skill-sheet-title').textContent = '技能管理';
|
|
2064
|
+
refreshSkillList();
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
window.saveSkill = async function() {
|
|
2068
|
+
const name = document.getElementById('skill-name').value.trim();
|
|
2069
|
+
const scope = document.getElementById('skill-scope').value;
|
|
2070
|
+
const content = document.getElementById('skill-content').value;
|
|
2071
|
+
if (!name) { addStatusMessage('请输入技能名称'); return; }
|
|
2072
|
+
if (!content.trim()) { addStatusMessage('请输入技能内容'); return; }
|
|
2073
|
+
try {
|
|
2074
|
+
const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}`, {
|
|
2075
|
+
method: 'POST',
|
|
2076
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2077
|
+
body: JSON.stringify({ name, scope, content }),
|
|
2078
|
+
});
|
|
2079
|
+
const data = await resp.json();
|
|
2080
|
+
if (data.error) { addStatusMessage('保存失败: ' + data.error); return; }
|
|
2081
|
+
addStatusMessage(`技能 /${name} 已保存`);
|
|
2082
|
+
backToSkillList();
|
|
2083
|
+
loadSkills(); // 刷新快捷栏按钮
|
|
2084
|
+
} catch (e) {
|
|
2085
|
+
addStatusMessage('保存失败: ' + e.message);
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
|
|
2089
|
+
window.deleteSkill = async function(name, source) {
|
|
2090
|
+
if (!confirm(`确定删除技能 /${name}?`)) return;
|
|
2091
|
+
try {
|
|
2092
|
+
const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}`, {
|
|
2093
|
+
method: 'DELETE',
|
|
2094
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2095
|
+
body: JSON.stringify({ name, scope: source }),
|
|
2096
|
+
});
|
|
2097
|
+
const data = await resp.json();
|
|
2098
|
+
if (data.error) { addStatusMessage('删除失败: ' + data.error); return; }
|
|
2099
|
+
addStatusMessage(`技能 /${name} 已删除`);
|
|
2100
|
+
refreshSkillList();
|
|
2101
|
+
loadSkills();
|
|
2102
|
+
} catch (e) {
|
|
2103
|
+
addStatusMessage('删除失败: ' + e.message);
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
// ── Git 安装技能 ──────────────────────────────
|
|
2108
|
+
let _gitSkills = []; // 扫描到的技能列表
|
|
2109
|
+
|
|
2110
|
+
window.showSkillImport = function() {
|
|
2111
|
+
document.getElementById('skill-list-view').style.display = 'none';
|
|
2112
|
+
document.getElementById('skill-edit-view').style.display = 'none';
|
|
2113
|
+
document.getElementById('skill-import-view').style.display = '';
|
|
2114
|
+
document.getElementById('skill-close-btn').style.display = 'none';
|
|
2115
|
+
document.getElementById('skill-sheet-title').textContent = '从 Git 安装技能';
|
|
2116
|
+
document.getElementById('skill-import-preview').style.display = 'none';
|
|
2117
|
+
document.getElementById('skill-import-install-btn').style.display = 'none';
|
|
2118
|
+
document.getElementById('skill-import-scan-btn').style.display = '';
|
|
2119
|
+
document.getElementById('skill-git-url').value = '';
|
|
2120
|
+
_gitSkills = [];
|
|
2121
|
+
};
|
|
2122
|
+
|
|
2123
|
+
window.scanGitSkills = async function() {
|
|
2124
|
+
const url = document.getElementById('skill-git-url').value.trim();
|
|
2125
|
+
if (!url) { addStatusMessage('请输入 Git 仓库地址'); return; }
|
|
2126
|
+
const btn = document.getElementById('skill-import-scan-btn');
|
|
2127
|
+
btn.textContent = '正在克隆并扫描...';
|
|
2128
|
+
btn.disabled = true;
|
|
2129
|
+
try {
|
|
2130
|
+
const resp = await fetch('/api/skills/scan-git', {
|
|
2131
|
+
method: 'POST',
|
|
2132
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2133
|
+
body: JSON.stringify({ url }),
|
|
2134
|
+
});
|
|
2135
|
+
const data = await resp.json();
|
|
2136
|
+
if (data.error) { addStatusMessage(data.error); return; }
|
|
2137
|
+
_gitSkills = data.skills || [];
|
|
2138
|
+
if (!_gitSkills.length) {
|
|
2139
|
+
addStatusMessage(data.message || '未找到技能文件');
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
const preview = document.getElementById('skill-import-preview');
|
|
2143
|
+
preview.innerHTML = _gitSkills.map((s, i) => `
|
|
2144
|
+
<label style="display:flex;align-items:flex-start;gap:0.5rem;padding:0.4rem 0;border-bottom:1px solid #21262d;cursor:pointer">
|
|
2145
|
+
<input type="checkbox" checked data-skill-idx="${i}" style="margin-top:0.15rem">
|
|
2146
|
+
<div style="min-width:0;flex:1">
|
|
2147
|
+
<div style="color:#9d5cf5;font-weight:500">/${escHtml(s.name)}</div>
|
|
2148
|
+
<div style="color:#8b949e;font-size:0.72rem">${escHtml(s.desc)}</div>
|
|
2149
|
+
</div>
|
|
2150
|
+
</label>
|
|
2151
|
+
`).join('');
|
|
2152
|
+
preview.style.display = '';
|
|
2153
|
+
document.getElementById('skill-import-install-btn').style.display = '';
|
|
2154
|
+
} catch (e) {
|
|
2155
|
+
addStatusMessage('扫描失败: ' + e.message);
|
|
2156
|
+
} finally {
|
|
2157
|
+
btn.textContent = '扫描技能';
|
|
2158
|
+
btn.disabled = false;
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
|
|
2162
|
+
window.installGitSkills = async function() {
|
|
2163
|
+
const scope = document.getElementById('skill-import-scope').value;
|
|
2164
|
+
const checkboxes = document.querySelectorAll('#skill-import-preview input[type=checkbox]');
|
|
2165
|
+
const selected = [];
|
|
2166
|
+
checkboxes.forEach(cb => {
|
|
2167
|
+
if (cb.checked) selected.push(_gitSkills[parseInt(cb.dataset.skillIdx)]);
|
|
2168
|
+
});
|
|
2169
|
+
if (!selected.length) { addStatusMessage('请至少选择一个技能'); return; }
|
|
2170
|
+
const btn = document.getElementById('skill-import-install-btn');
|
|
2171
|
+
btn.textContent = '安装中...';
|
|
2172
|
+
btn.disabled = true;
|
|
2173
|
+
let ok = 0, fail = 0;
|
|
2174
|
+
for (const s of selected) {
|
|
2175
|
+
try {
|
|
2176
|
+
const resp = await fetch(`/api/skills?sessionId=${SESSION_ID}`, {
|
|
2177
|
+
method: 'POST',
|
|
2178
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2179
|
+
body: JSON.stringify({ name: s.name, scope, content: s.content }),
|
|
2180
|
+
});
|
|
2181
|
+
const data = await resp.json();
|
|
2182
|
+
if (data.error) fail++;
|
|
2183
|
+
else ok++;
|
|
2184
|
+
} catch { fail++; }
|
|
2185
|
+
}
|
|
2186
|
+
addStatusMessage(`安装完成: ${ok} 个成功${fail ? `, ${fail} 个失败` : ''}`);
|
|
2187
|
+
btn.textContent = '安装选中的技能';
|
|
2188
|
+
btn.disabled = false;
|
|
2189
|
+
backToSkillList();
|
|
2190
|
+
loadSkills();
|
|
2191
|
+
};
|
|
2192
|
+
|
|
1953
2193
|
// ── Git 提交推送 ──────────────────────────────
|
|
1954
2194
|
const GIT_STORAGE_KEY = 'myhi-git-' + SESSION_ID;
|
|
1955
2195
|
|