antri_cli 1.44.0 → 1.45.0
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/cli/banner.d.ts.map +1 -1
- package/dist/cli/banner.js +16 -1
- package/dist/cli/banner.js.map +1 -1
- package/dist/cli/prompt.d.ts.map +1 -1
- package/dist/cli/prompt.js +8 -0
- package/dist/cli/prompt.js.map +1 -1
- package/dist/cli/shortcuts.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +39 -16
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/cloud/auth.d.ts.map +1 -1
- package/dist/cloud/auth.js +1 -3
- package/dist/cloud/auth.js.map +1 -1
- package/dist/cloud/firestore.d.ts.map +1 -1
- package/dist/cloud/firestore.js +49 -16
- package/dist/cloud/firestore.js.map +1 -1
- package/dist/core/agent.d.ts +2 -2
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +49 -26
- package/dist/core/agent.js.map +1 -1
- package/dist/core/config.d.ts +1 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +9 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +83 -15
- package/dist/core/tools.js.map +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/app.js +287 -31
- package/dist/desktop/public/index.html +55 -10
- package/dist/desktop/public/style.css +214 -28
- package/dist/desktop/server.d.ts.map +1 -1
- package/dist/desktop/server.js +60 -4
- package/dist/desktop/server.js.map +1 -1
- package/dist/memory/manager.d.ts.map +1 -1
- package/dist/memory/manager.js +4 -0
- package/dist/memory/manager.js.map +1 -1
- package/dist/mobile/server.d.ts.map +1 -1
- package/dist/mobile/server.js +8 -2
- package/dist/mobile/server.js.map +1 -1
- package/dist/profiles/profileManager.d.ts +17 -4
- package/dist/profiles/profileManager.d.ts.map +1 -1
- package/dist/profiles/profileManager.js +202 -12
- package/dist/profiles/profileManager.js.map +1 -1
- package/dist/skills/skillManager.d.ts +62 -0
- package/dist/skills/skillManager.d.ts.map +1 -0
- package/dist/skills/skillManager.js +645 -0
- package/dist/skills/skillManager.js.map +1 -0
- package/package.json +1 -1
|
@@ -8,6 +8,12 @@ let activePaletteMatches = [];
|
|
|
8
8
|
let paletteSelectedIndex = 0;
|
|
9
9
|
let activePaletteMode = null; // 'slash' | 'file' | null
|
|
10
10
|
|
|
11
|
+
// Skills State
|
|
12
|
+
let allSkillsList = [];
|
|
13
|
+
let selectedSkillId = null;
|
|
14
|
+
let currentSkillCategory = 'all';
|
|
15
|
+
let skillSearchQuery = '';
|
|
16
|
+
|
|
11
17
|
// Initialize on Load
|
|
12
18
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
13
19
|
await loadStatus();
|
|
@@ -17,6 +23,23 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
17
23
|
await loadMemory();
|
|
18
24
|
});
|
|
19
25
|
|
|
26
|
+
// Toast Notification
|
|
27
|
+
function showToast(message, isError = false) {
|
|
28
|
+
let toast = document.getElementById('antri-toast');
|
|
29
|
+
if (!toast) {
|
|
30
|
+
toast = document.createElement('div');
|
|
31
|
+
toast.id = 'antri-toast';
|
|
32
|
+
toast.className = 'antri-toast';
|
|
33
|
+
document.body.appendChild(toast);
|
|
34
|
+
}
|
|
35
|
+
toast.textContent = message;
|
|
36
|
+
toast.style.background = isError ? '#991b1b' : '#1c1917';
|
|
37
|
+
toast.classList.add('show');
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
toast.classList.remove('show');
|
|
40
|
+
}, 2500);
|
|
41
|
+
}
|
|
42
|
+
|
|
20
43
|
// Load System Status & Config
|
|
21
44
|
async function loadStatus() {
|
|
22
45
|
try {
|
|
@@ -369,7 +392,7 @@ function handleInputKey(event) {
|
|
|
369
392
|
}
|
|
370
393
|
}
|
|
371
394
|
|
|
372
|
-
// Chat Prompt Submission with
|
|
395
|
+
// Chat Prompt Submission with Real-Time Word-by-Word Side-by-Side Streaming
|
|
373
396
|
async function submitPrompt() {
|
|
374
397
|
const input = document.getElementById('prompt-input');
|
|
375
398
|
let prompt = input.value.trim();
|
|
@@ -408,7 +431,7 @@ async function submitPrompt() {
|
|
|
408
431
|
|
|
409
432
|
const sendBtn = document.getElementById('send-btn');
|
|
410
433
|
sendBtn.disabled = true;
|
|
411
|
-
sendBtn.textContent = '
|
|
434
|
+
sendBtn.textContent = 'Streaming...';
|
|
412
435
|
|
|
413
436
|
try {
|
|
414
437
|
const response = await fetch('/api/chat', {
|
|
@@ -425,7 +448,7 @@ async function submitPrompt() {
|
|
|
425
448
|
const { done, value } = await reader.read();
|
|
426
449
|
if (done) break;
|
|
427
450
|
|
|
428
|
-
const chunk = decoder.decode(value);
|
|
451
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
429
452
|
const lines = chunk.split('\n');
|
|
430
453
|
|
|
431
454
|
for (const line of lines) {
|
|
@@ -440,8 +463,9 @@ async function submitPrompt() {
|
|
|
440
463
|
// Tool call badge
|
|
441
464
|
const toolBadge = document.createElement('div');
|
|
442
465
|
toolBadge.className = 'tool-badge-pill';
|
|
443
|
-
toolBadge.textContent =
|
|
466
|
+
toolBadge.textContent = `• Tool: ${data.name}`;
|
|
444
467
|
assistantMsgEl.insertBefore(toolBadge, contentEl);
|
|
468
|
+
scrollToBottom();
|
|
445
469
|
}
|
|
446
470
|
} catch (e) {}
|
|
447
471
|
}
|
|
@@ -569,7 +593,9 @@ async function startGoalLoop() {
|
|
|
569
593
|
}
|
|
570
594
|
}
|
|
571
595
|
|
|
572
|
-
//
|
|
596
|
+
// ==========================================
|
|
597
|
+
// Thinking Profile Management
|
|
598
|
+
// ==========================================
|
|
573
599
|
async function loadProfiles() {
|
|
574
600
|
try {
|
|
575
601
|
const res = await fetch('/api/profiles');
|
|
@@ -625,6 +651,31 @@ async function createProfile() {
|
|
|
625
651
|
|
|
626
652
|
input.value = '';
|
|
627
653
|
await loadProfiles();
|
|
654
|
+
showToast(`Profile '${name}' created.`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
async function handleProfileImport(event) {
|
|
658
|
+
const files = event.target.files;
|
|
659
|
+
if (!files || files.length === 0) return;
|
|
660
|
+
|
|
661
|
+
for (const file of files) {
|
|
662
|
+
const reader = new FileReader();
|
|
663
|
+
reader.onload = async (e) => {
|
|
664
|
+
const content = e.target.result;
|
|
665
|
+
const res = await fetch('/api/profile/import', {
|
|
666
|
+
method: 'POST',
|
|
667
|
+
headers: { 'Content-Type': 'application/json' },
|
|
668
|
+
body: JSON.stringify({ name: file.name, content }),
|
|
669
|
+
});
|
|
670
|
+
const data = await res.json();
|
|
671
|
+
if (data.success) {
|
|
672
|
+
await loadProfiles();
|
|
673
|
+
showToast(`Profile '${file.name}' imported successfully.`);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
reader.readAsText(file);
|
|
677
|
+
}
|
|
678
|
+
event.target.value = '';
|
|
628
679
|
}
|
|
629
680
|
|
|
630
681
|
async function saveActiveProfile() {
|
|
@@ -634,50 +685,255 @@ async function saveActiveProfile() {
|
|
|
634
685
|
headers: { 'Content-Type': 'application/json' },
|
|
635
686
|
body: JSON.stringify({ content }),
|
|
636
687
|
});
|
|
637
|
-
|
|
688
|
+
showToast('Profile saved successfully.');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function exportActiveProfile() {
|
|
692
|
+
const activeTitle = document.getElementById('active-profile-title').textContent || 'profile.md';
|
|
693
|
+
const content = document.getElementById('profile-editor').value;
|
|
694
|
+
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8;' });
|
|
695
|
+
const url = URL.createObjectURL(blob);
|
|
696
|
+
const link = document.createElement('a');
|
|
697
|
+
link.setAttribute('href', url);
|
|
698
|
+
link.setAttribute('download', activeTitle);
|
|
699
|
+
document.body.appendChild(link);
|
|
700
|
+
link.click();
|
|
701
|
+
document.body.removeChild(link);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
async function deleteActiveProfile() {
|
|
705
|
+
const activeTitle = (document.getElementById('active-profile-title').textContent || '').replace('.md', '');
|
|
706
|
+
if (activeTitle === 'profile_1') {
|
|
707
|
+
alert('Cannot delete default profile_1.');
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (!confirm(`Are you sure you want to delete profile '${activeTitle}.md'?`)) return;
|
|
711
|
+
|
|
712
|
+
const res = await fetch('/api/profile/delete', {
|
|
713
|
+
method: 'POST',
|
|
714
|
+
headers: { 'Content-Type': 'application/json' },
|
|
715
|
+
body: JSON.stringify({ name: activeTitle }),
|
|
716
|
+
});
|
|
717
|
+
const data = await res.json();
|
|
718
|
+
if (data.success) {
|
|
719
|
+
await loadProfiles();
|
|
720
|
+
showToast(`Profile '${activeTitle}' deleted.`);
|
|
721
|
+
}
|
|
638
722
|
}
|
|
639
723
|
|
|
640
724
|
async function onProfileChange(name) {
|
|
641
725
|
await selectProfile(name);
|
|
642
726
|
}
|
|
643
727
|
|
|
644
|
-
//
|
|
728
|
+
// ==========================================
|
|
729
|
+
// Markdown Skills Studio Management
|
|
730
|
+
// ==========================================
|
|
645
731
|
async function loadSkills() {
|
|
646
732
|
try {
|
|
647
733
|
const res = await fetch('/api/skills');
|
|
648
734
|
const data = await res.json();
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
card.innerHTML = `
|
|
656
|
-
<h4>${t.name}</h4>
|
|
657
|
-
<p>${t.description}</p>
|
|
658
|
-
<button class="skill-btn" onclick="testSkill('${t.name}')">Dry-Run Skill</button>
|
|
659
|
-
`;
|
|
660
|
-
grid.appendChild(card);
|
|
661
|
-
});
|
|
735
|
+
allSkillsList = data.markdownSkills || [];
|
|
736
|
+
renderSkillList();
|
|
737
|
+
|
|
738
|
+
if (!selectedSkillId && allSkillsList.length > 0) {
|
|
739
|
+
selectSkill(allSkillsList[0].id);
|
|
740
|
+
}
|
|
662
741
|
} catch (err) {
|
|
663
742
|
console.error('Failed to load skills:', err);
|
|
664
743
|
}
|
|
665
744
|
}
|
|
666
745
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
746
|
+
function setSkillCategory(category) {
|
|
747
|
+
currentSkillCategory = category;
|
|
748
|
+
document.querySelectorAll('.category-pill').forEach((btn) => {
|
|
749
|
+
btn.classList.toggle('active', btn.textContent.toLowerCase() === category.toLowerCase());
|
|
750
|
+
});
|
|
751
|
+
renderSkillList();
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function filterSkills(query) {
|
|
755
|
+
skillSearchQuery = (query || '').toLowerCase().trim();
|
|
756
|
+
renderSkillList();
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function renderSkillList() {
|
|
760
|
+
const listContainer = document.getElementById('skills-catalog-list');
|
|
761
|
+
if (!listContainer) return;
|
|
762
|
+
listContainer.innerHTML = '';
|
|
763
|
+
|
|
764
|
+
let filtered = allSkillsList;
|
|
765
|
+
if (currentSkillCategory !== 'all') {
|
|
766
|
+
if (currentSkillCategory === 'Core') {
|
|
767
|
+
filtered = filtered.filter((s) => s.isCore);
|
|
768
|
+
} else if (currentSkillCategory === 'Custom') {
|
|
769
|
+
filtered = filtered.filter((s) => !s.isCore);
|
|
770
|
+
} else {
|
|
771
|
+
filtered = filtered.filter((s) => s.category && s.category.toLowerCase() === currentSkillCategory.toLowerCase());
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (skillSearchQuery) {
|
|
776
|
+
filtered = filtered.filter(
|
|
777
|
+
(s) =>
|
|
778
|
+
s.name.toLowerCase().includes(skillSearchQuery) ||
|
|
779
|
+
s.description.toLowerCase().includes(skillSearchQuery) ||
|
|
780
|
+
(s.triggers && s.triggers.some((t) => t.includes(skillSearchQuery)))
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (filtered.length === 0) {
|
|
785
|
+
listContainer.innerHTML = '<div class="empty-state">No skills match the search criteria.</div>';
|
|
786
|
+
return;
|
|
678
787
|
}
|
|
788
|
+
|
|
789
|
+
filtered.forEach((skill) => {
|
|
790
|
+
const card = document.createElement('div');
|
|
791
|
+
card.className = `skill-item-card ${skill.id === selectedSkillId ? 'active' : ''}`;
|
|
792
|
+
card.innerHTML = `
|
|
793
|
+
<div class="skill-item-header">
|
|
794
|
+
<span class="skill-item-name">${skill.name}</span>
|
|
795
|
+
<span class="skill-type-tag ${skill.isCore ? 'core' : 'custom'}">${skill.isCore ? 'Core' : 'Custom'}</span>
|
|
796
|
+
</div>
|
|
797
|
+
<div class="skill-item-desc">${skill.description}</div>
|
|
798
|
+
<div class="skill-item-footer">
|
|
799
|
+
<span class="skill-category-badge">${skill.category}</span>
|
|
800
|
+
<span class="skill-version-tag">v${skill.version}</span>
|
|
801
|
+
</div>
|
|
802
|
+
`;
|
|
803
|
+
card.onclick = () => selectSkill(skill.id);
|
|
804
|
+
listContainer.appendChild(card);
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function selectSkill(skillId) {
|
|
809
|
+
selectedSkillId = skillId;
|
|
810
|
+
const skill = allSkillsList.find((s) => s.id === skillId);
|
|
811
|
+
if (!skill) return;
|
|
812
|
+
|
|
813
|
+
document.getElementById('active-skill-title').textContent = `${skill.name} (${skill.id}.md)`;
|
|
814
|
+
document.getElementById('active-skill-meta').textContent = `Category: ${skill.category} · Author: ${skill.author} · Version: ${skill.version} · Triggers: ${skill.triggers.join(', ') || 'Auto'}`;
|
|
815
|
+
document.getElementById('skill-editor').value = skill.content || skill.instructions;
|
|
816
|
+
|
|
817
|
+
// Toggle Delete button (allow delete only on custom skills)
|
|
818
|
+
const deleteBtn = document.getElementById('btn-delete-skill');
|
|
819
|
+
if (deleteBtn) {
|
|
820
|
+
deleteBtn.style.display = skill.isCore ? 'none' : 'inline-flex';
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
renderSkillList();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
async function saveCurrentSkill() {
|
|
827
|
+
if (!selectedSkillId) return;
|
|
828
|
+
const content = document.getElementById('skill-editor').value;
|
|
829
|
+
|
|
830
|
+
const res = await fetch('/api/skill/save', {
|
|
831
|
+
method: 'POST',
|
|
832
|
+
headers: { 'Content-Type': 'application/json' },
|
|
833
|
+
body: JSON.stringify({ id: selectedSkillId, content }),
|
|
834
|
+
});
|
|
835
|
+
const data = await res.json();
|
|
836
|
+
if (data.success) {
|
|
837
|
+
await loadSkills();
|
|
838
|
+
selectSkill(selectedSkillId);
|
|
839
|
+
showToast('Skill markdown saved successfully.');
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
async function createNewSkillPrompt() {
|
|
844
|
+
const name = prompt('Enter name for the new skill (e.g., "fastapi_specialist"):');
|
|
845
|
+
if (!name) return;
|
|
846
|
+
const description = prompt('Enter a short description for what this skill does:', 'Specialist guidelines and heuristics.');
|
|
847
|
+
|
|
848
|
+
const res = await fetch('/api/skill/create', {
|
|
849
|
+
method: 'POST',
|
|
850
|
+
headers: { 'Content-Type': 'application/json' },
|
|
851
|
+
body: JSON.stringify({ name, description, category: 'Custom' }),
|
|
852
|
+
});
|
|
853
|
+
const data = await res.json();
|
|
854
|
+
if (data.success && data.skill) {
|
|
855
|
+
await loadSkills();
|
|
856
|
+
selectSkill(data.skill.id);
|
|
857
|
+
showToast(`Skill '${data.skill.name}' created!`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
async function handleSkillImport(event) {
|
|
862
|
+
const files = event.target.files;
|
|
863
|
+
if (!files || files.length === 0) return;
|
|
864
|
+
|
|
865
|
+
for (const file of files) {
|
|
866
|
+
const reader = new FileReader();
|
|
867
|
+
reader.onload = async (e) => {
|
|
868
|
+
const content = e.target.result;
|
|
869
|
+
const res = await fetch('/api/skill/import', {
|
|
870
|
+
method: 'POST',
|
|
871
|
+
headers: { 'Content-Type': 'application/json' },
|
|
872
|
+
body: JSON.stringify({ name: file.name, content }),
|
|
873
|
+
});
|
|
874
|
+
const data = await res.json();
|
|
875
|
+
if (data.success && data.skill) {
|
|
876
|
+
await loadSkills();
|
|
877
|
+
selectSkill(data.skill.id);
|
|
878
|
+
showToast(`Skill '${data.skill.name}' imported successfully.`);
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
reader.readAsText(file);
|
|
882
|
+
}
|
|
883
|
+
event.target.value = '';
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function exportCurrentSkill() {
|
|
887
|
+
if (!selectedSkillId) return;
|
|
888
|
+
const skill = allSkillsList.find((s) => s.id === selectedSkillId);
|
|
889
|
+
const content = document.getElementById('skill-editor').value;
|
|
890
|
+
const filename = `${skill ? skill.id : 'skill'}.md`;
|
|
891
|
+
|
|
892
|
+
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8;' });
|
|
893
|
+
const url = URL.createObjectURL(blob);
|
|
894
|
+
const link = document.createElement('a');
|
|
895
|
+
link.setAttribute('href', url);
|
|
896
|
+
link.setAttribute('download', filename);
|
|
897
|
+
document.body.appendChild(link);
|
|
898
|
+
link.click();
|
|
899
|
+
document.body.removeChild(link);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
async function deleteCurrentSkill() {
|
|
903
|
+
if (!selectedSkillId) return;
|
|
904
|
+
const skill = allSkillsList.find((s) => s.id === selectedSkillId);
|
|
905
|
+
if (skill && skill.isCore) {
|
|
906
|
+
alert('Cannot delete built-in core skills.');
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (!confirm(`Are you sure you want to delete skill '${skill ? skill.name : selectedSkillId}'?`)) return;
|
|
910
|
+
|
|
911
|
+
const res = await fetch('/api/skill/delete', {
|
|
912
|
+
method: 'POST',
|
|
913
|
+
headers: { 'Content-Type': 'application/json' },
|
|
914
|
+
body: JSON.stringify({ id: selectedSkillId }),
|
|
915
|
+
});
|
|
916
|
+
const data = await res.json();
|
|
917
|
+
if (data.success) {
|
|
918
|
+
selectedSkillId = null;
|
|
919
|
+
await loadSkills();
|
|
920
|
+
showToast('Skill deleted.');
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function activateCurrentSkillInChat() {
|
|
925
|
+
if (!selectedSkillId) return;
|
|
926
|
+
const skill = allSkillsList.find((s) => s.id === selectedSkillId);
|
|
927
|
+
if (!skill) return;
|
|
928
|
+
|
|
929
|
+
showTab('chat');
|
|
930
|
+
const input = document.getElementById('prompt-input');
|
|
931
|
+
input.value = `[Apply Skill: ${skill.name}] `;
|
|
932
|
+
input.focus();
|
|
933
|
+
showToast(`Skill '${skill.name}' loaded into prompt.`);
|
|
679
934
|
}
|
|
680
935
|
|
|
936
|
+
// Memory Loader
|
|
681
937
|
async function loadMemory() {
|
|
682
938
|
try {
|
|
683
939
|
const res = await fetch('/api/memory');
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<header class="app-header">
|
|
16
16
|
<div class="brand-zone">
|
|
17
17
|
<span class="logo-mark">ANTRI</span>
|
|
18
|
-
<span class="version-tag">v1.
|
|
18
|
+
<span class="version-tag">v1.45.0</span>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<!-- Mode Toggle -->
|
|
@@ -269,11 +269,13 @@
|
|
|
269
269
|
<div class="panel-header">
|
|
270
270
|
<div>
|
|
271
271
|
<h2>Thinking Profile Studio</h2>
|
|
272
|
-
<p>Manage user preferences, coding styles, and automatically extracted notes
|
|
272
|
+
<p>Manage user preferences, coding styles, and automatically extracted notes in <code>~/.antri/profiles/</code>.</p>
|
|
273
273
|
</div>
|
|
274
274
|
<div class="debate-controls">
|
|
275
|
+
<input type="file" id="profile-import-input" accept=".md,.txt" style="display:none;" onchange="handleProfileImport(event)" />
|
|
275
276
|
<input type="text" id="new-profile-name" placeholder="New profile name (e.g. backend_architect)" />
|
|
276
|
-
<button class="action-btn" onclick="createProfile()"
|
|
277
|
+
<button class="action-btn" onclick="createProfile()">+ Create</button>
|
|
278
|
+
<button class="action-btn-secondary" onclick="document.getElementById('profile-import-input').click()">📁 Import .md</button>
|
|
277
279
|
</div>
|
|
278
280
|
</div>
|
|
279
281
|
|
|
@@ -283,25 +285,68 @@
|
|
|
283
285
|
</div>
|
|
284
286
|
<div class="profile-editor-container">
|
|
285
287
|
<div class="editor-header">
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
+
<div>
|
|
289
|
+
<h3 id="active-profile-title">profile_1.md</h3>
|
|
290
|
+
<span class="editor-subtitle">Editing active thinking profile instructions</span>
|
|
291
|
+
</div>
|
|
292
|
+
<div class="editor-action-group">
|
|
293
|
+
<button class="btn-sm-action" onclick="exportActiveProfile()">⬇️ Export</button>
|
|
294
|
+
<button class="btn-sm-action btn-danger" onclick="deleteActiveProfile()">🗑️ Delete</button>
|
|
295
|
+
<button class="save-btn" onclick="saveActiveProfile()">💾 Save Profile</button>
|
|
296
|
+
</div>
|
|
288
297
|
</div>
|
|
289
298
|
<textarea id="profile-editor" class="profile-textarea" placeholder="Profile markdown content..."></textarea>
|
|
290
299
|
</div>
|
|
291
300
|
</div>
|
|
292
301
|
</section>
|
|
293
302
|
|
|
294
|
-
<!-- TAB 5:
|
|
303
|
+
<!-- TAB 5: Markdown Skills Studio -->
|
|
295
304
|
<section id="tab-skills" class="tab-panel">
|
|
296
305
|
<div class="panel-header">
|
|
297
306
|
<div>
|
|
298
|
-
<h2>
|
|
299
|
-
<p>
|
|
307
|
+
<h2>Markdown (.md) Skill Studio</h2>
|
|
308
|
+
<p>Specialist capability directives loaded from <code>~/.antri/skills/</code>. Add or import new skills as Markdown files.</p>
|
|
309
|
+
</div>
|
|
310
|
+
<div class="debate-controls">
|
|
311
|
+
<input type="file" id="skill-import-input" accept=".md,.txt" style="display:none;" onchange="handleSkillImport(event)" />
|
|
312
|
+
<input type="text" id="skill-search-input" placeholder="Search skills by name, category, or trigger..." oninput="filterSkills(this.value)" />
|
|
313
|
+
<button class="action-btn" onclick="createNewSkillPrompt()">+ Create Skill</button>
|
|
314
|
+
<button class="action-btn-secondary" onclick="document.getElementById('skill-import-input').click()">📁 Import .md</button>
|
|
300
315
|
</div>
|
|
301
316
|
</div>
|
|
302
317
|
|
|
303
|
-
<div class="
|
|
304
|
-
<!--
|
|
318
|
+
<div class="skill-studio-layout">
|
|
319
|
+
<!-- Left: Skills List & Filters -->
|
|
320
|
+
<div class="skill-list-sidebar">
|
|
321
|
+
<div class="skill-category-filters" id="skill-category-filters">
|
|
322
|
+
<button class="category-pill active" onclick="setSkillCategory('all')">All</button>
|
|
323
|
+
<button class="category-pill" onclick="setSkillCategory('Core')">Core</button>
|
|
324
|
+
<button class="category-pill" onclick="setSkillCategory('Custom')">Custom</button>
|
|
325
|
+
<button class="category-pill" onclick="setSkillCategory('Engineering')">Engineering</button>
|
|
326
|
+
<button class="category-pill" onclick="setSkillCategory('Architecture')">Architecture</button>
|
|
327
|
+
<button class="category-pill" onclick="setSkillCategory('Security')">Security</button>
|
|
328
|
+
</div>
|
|
329
|
+
<div class="skill-items-container" id="skills-catalog-list">
|
|
330
|
+
<!-- Populated dynamically -->
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<!-- Right: Live Skill Markdown Editor -->
|
|
335
|
+
<div class="skill-editor-container">
|
|
336
|
+
<div class="editor-header">
|
|
337
|
+
<div>
|
|
338
|
+
<h3 id="active-skill-title">Select a Skill</h3>
|
|
339
|
+
<span id="active-skill-meta" class="editor-subtitle">Click any skill on the left to inspect or edit instructions</span>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="editor-action-group">
|
|
342
|
+
<button class="btn-sm-action" id="btn-activate-skill" onclick="activateCurrentSkillInChat()">⚡ Activate in Chat</button>
|
|
343
|
+
<button class="btn-sm-action" id="btn-export-skill" onclick="exportCurrentSkill()">⬇️ Export</button>
|
|
344
|
+
<button class="btn-sm-action btn-danger" id="btn-delete-skill" onclick="deleteCurrentSkill()">🗑️ Delete</button>
|
|
345
|
+
<button class="save-btn" id="btn-save-skill" onclick="saveCurrentSkill()">💾 Save Skill</button>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
<textarea id="skill-editor" class="profile-textarea" placeholder="Skill markdown content (# Skill Title, instructions, heuristics...)" spellcheck="false"></textarea>
|
|
349
|
+
</div>
|
|
305
350
|
</div>
|
|
306
351
|
</section>
|
|
307
352
|
|