@orderful/droid 0.3.0 → 0.4.1
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/.claude/CLAUDE.md +41 -0
- package/.github/workflows/changeset-check.yml +30 -11
- package/CHANGELOG.md +29 -0
- package/bun.lock +2 -182
- package/dist/agents/README.md +137 -0
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +273 -16
- package/dist/commands/tui.js.map +1 -1
- package/dist/lib/agents.d.ts +53 -0
- package/dist/lib/agents.d.ts.map +1 -0
- package/dist/lib/agents.js +149 -0
- package/dist/lib/agents.js.map +1 -0
- package/dist/lib/skills.d.ts +20 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +102 -0
- package/dist/lib/skills.js.map +1 -1
- package/dist/skills/README.md +85 -0
- package/dist/skills/comments/commands/README.md +58 -0
- package/package.json +2 -4
- package/src/agents/README.md +137 -0
- package/src/commands/tui.tsx +454 -21
- package/src/lib/agents.ts +186 -0
- package/src/lib/skills.ts +125 -0
- package/src/skills/README.md +85 -0
- package/src/skills/comments/commands/README.md +58 -0
package/src/commands/tui.tsx
CHANGED
|
@@ -11,14 +11,18 @@ import {
|
|
|
11
11
|
getInstalledSkill,
|
|
12
12
|
installSkill,
|
|
13
13
|
uninstallSkill,
|
|
14
|
+
isCommandInstalled,
|
|
15
|
+
installCommand,
|
|
16
|
+
uninstallCommand,
|
|
14
17
|
} from '../lib/skills.js';
|
|
18
|
+
import { getBundledAgents, getBundledAgentsDir, isAgentInstalled, installAgent, uninstallAgent, type AgentManifest } from '../lib/agents.js';
|
|
15
19
|
import { configExists, loadConfig, saveConfig, loadSkillOverrides, saveSkillOverrides } from '../lib/config.js';
|
|
16
20
|
import { configureAIToolPermissions } from './setup.js';
|
|
17
21
|
import { AITool, BuiltInOutput, ConfigOptionType, type DroidConfig, type OutputPreference, type SkillManifest, type ConfigOption, type SkillOverrides } from '../lib/types.js';
|
|
18
22
|
import { getVersion } from '../lib/version.js';
|
|
19
23
|
|
|
20
24
|
type Tab = 'skills' | 'commands' | 'agents' | 'settings';
|
|
21
|
-
type View = 'welcome' | 'setup' | 'menu' | 'detail' | 'configure';
|
|
25
|
+
type View = 'welcome' | 'setup' | 'menu' | 'detail' | 'configure' | 'readme';
|
|
22
26
|
type SetupStep = 'ai_tool' | 'user_mention' | 'output_preference' | 'confirm';
|
|
23
27
|
|
|
24
28
|
const colors = {
|
|
@@ -444,10 +448,14 @@ function SkillDetails({
|
|
|
444
448
|
|
|
445
449
|
const actions = installed
|
|
446
450
|
? [
|
|
447
|
-
{ id: '
|
|
451
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
448
452
|
{ id: 'configure', label: 'Configure', variant: 'primary' },
|
|
453
|
+
{ id: 'uninstall', label: 'Uninstall', variant: 'danger' },
|
|
449
454
|
]
|
|
450
|
-
: [
|
|
455
|
+
: [
|
|
456
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
457
|
+
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
458
|
+
];
|
|
451
459
|
|
|
452
460
|
return (
|
|
453
461
|
<Box flexDirection="column" paddingLeft={2} flexGrow={1}>
|
|
@@ -519,7 +527,15 @@ function SkillDetails({
|
|
|
519
527
|
);
|
|
520
528
|
}
|
|
521
529
|
|
|
522
|
-
function CommandDetails({
|
|
530
|
+
function CommandDetails({
|
|
531
|
+
command,
|
|
532
|
+
isFocused,
|
|
533
|
+
selectedAction,
|
|
534
|
+
}: {
|
|
535
|
+
command: Command | null;
|
|
536
|
+
isFocused: boolean;
|
|
537
|
+
selectedAction: number;
|
|
538
|
+
}) {
|
|
523
539
|
if (!command) {
|
|
524
540
|
return (
|
|
525
541
|
<Box paddingLeft={2} flexGrow={1}>
|
|
@@ -528,7 +544,21 @@ function CommandDetails({ command }: { command: Command | null }) {
|
|
|
528
544
|
);
|
|
529
545
|
}
|
|
530
546
|
|
|
531
|
-
const
|
|
547
|
+
const skillInstalled = isSkillInstalled(command.skillName);
|
|
548
|
+
const installed = isCommandInstalled(command.name, command.skillName);
|
|
549
|
+
|
|
550
|
+
// If skill is installed, command comes with it - no standalone uninstall
|
|
551
|
+
const actions = skillInstalled
|
|
552
|
+
? [{ id: 'view', label: 'View', variant: 'default' }]
|
|
553
|
+
: installed
|
|
554
|
+
? [
|
|
555
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
556
|
+
{ id: 'uninstall', label: 'Uninstall', variant: 'danger' },
|
|
557
|
+
]
|
|
558
|
+
: [
|
|
559
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
560
|
+
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
561
|
+
];
|
|
532
562
|
|
|
533
563
|
return (
|
|
534
564
|
<Box flexDirection="column" paddingLeft={2} flexGrow={1}>
|
|
@@ -537,7 +567,8 @@ function CommandDetails({ command }: { command: Command | null }) {
|
|
|
537
567
|
<Box marginTop={1}>
|
|
538
568
|
<Text color={colors.textDim}>
|
|
539
569
|
from {command.skillName}
|
|
540
|
-
{
|
|
570
|
+
{skillInstalled && <Text color={colors.success}> · via skill</Text>}
|
|
571
|
+
{!skillInstalled && installed && <Text color={colors.success}> · installed</Text>}
|
|
541
572
|
</Text>
|
|
542
573
|
</Box>
|
|
543
574
|
|
|
@@ -555,6 +586,270 @@ function CommandDetails({ command }: { command: Command | null }) {
|
|
|
555
586
|
))}
|
|
556
587
|
</Box>
|
|
557
588
|
)}
|
|
589
|
+
|
|
590
|
+
{isFocused && (
|
|
591
|
+
<Box flexDirection="row" marginTop={1}>
|
|
592
|
+
{actions.map((action, index) => (
|
|
593
|
+
<Text
|
|
594
|
+
key={action.id}
|
|
595
|
+
backgroundColor={
|
|
596
|
+
selectedAction === index
|
|
597
|
+
? action.variant === 'danger'
|
|
598
|
+
? colors.error
|
|
599
|
+
: colors.primary
|
|
600
|
+
: colors.bgSelected
|
|
601
|
+
}
|
|
602
|
+
color={selectedAction === index ? '#ffffff' : colors.textMuted}
|
|
603
|
+
bold={selectedAction === index}
|
|
604
|
+
>
|
|
605
|
+
{' '}{action.label}{' '}
|
|
606
|
+
</Text>
|
|
607
|
+
))}
|
|
608
|
+
</Box>
|
|
609
|
+
)}
|
|
610
|
+
</Box>
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function AgentItem({ agent, isSelected }: { agent: AgentManifest; isSelected: boolean }) {
|
|
615
|
+
const statusDisplay = agent.status === 'alpha' ? '[alpha]' : agent.status === 'beta' ? '[beta]' : '';
|
|
616
|
+
|
|
617
|
+
return (
|
|
618
|
+
<Box paddingX={1}>
|
|
619
|
+
<Text>
|
|
620
|
+
<Text color={colors.textDim}>{isSelected ? '>' : ' '} </Text>
|
|
621
|
+
<Text color={isSelected ? colors.text : colors.textMuted}>{agent.name}</Text>
|
|
622
|
+
<Text color={colors.textDim}> v{agent.version}</Text>
|
|
623
|
+
{statusDisplay && <Text color={colors.textDim}> {statusDisplay}</Text>}
|
|
624
|
+
</Text>
|
|
625
|
+
</Box>
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function AgentDetails({
|
|
630
|
+
agent,
|
|
631
|
+
isFocused,
|
|
632
|
+
selectedAction,
|
|
633
|
+
}: {
|
|
634
|
+
agent: AgentManifest | null;
|
|
635
|
+
isFocused: boolean;
|
|
636
|
+
selectedAction: number;
|
|
637
|
+
}) {
|
|
638
|
+
if (!agent) {
|
|
639
|
+
return (
|
|
640
|
+
<Box paddingLeft={2} flexGrow={1}>
|
|
641
|
+
<Text color={colors.textDim}>Select an agent</Text>
|
|
642
|
+
</Box>
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const installed = isAgentInstalled(agent.name);
|
|
647
|
+
const statusDisplay = agent.status === 'alpha' ? '[alpha]' : agent.status === 'beta' ? '[beta]' : '';
|
|
648
|
+
const modeDisplay = agent.mode === 'primary' ? 'primary' : agent.mode === 'all' ? 'primary/subagent' : 'subagent';
|
|
649
|
+
|
|
650
|
+
const actions = installed
|
|
651
|
+
? [
|
|
652
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
653
|
+
{ id: 'uninstall', label: 'Uninstall', variant: 'danger' },
|
|
654
|
+
]
|
|
655
|
+
: [
|
|
656
|
+
{ id: 'view', label: 'View', variant: 'default' },
|
|
657
|
+
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
658
|
+
];
|
|
659
|
+
|
|
660
|
+
return (
|
|
661
|
+
<Box flexDirection="column" paddingLeft={2} flexGrow={1}>
|
|
662
|
+
<Text color={colors.text} bold>{agent.name}</Text>
|
|
663
|
+
|
|
664
|
+
<Box marginTop={1}>
|
|
665
|
+
<Text color={colors.textDim}>
|
|
666
|
+
v{agent.version}
|
|
667
|
+
{statusDisplay && <Text> · {statusDisplay}</Text>}
|
|
668
|
+
{' · '}{modeDisplay}
|
|
669
|
+
{installed && <Text color={colors.success}> · installed</Text>}
|
|
670
|
+
</Text>
|
|
671
|
+
</Box>
|
|
672
|
+
|
|
673
|
+
<Box marginTop={1}>
|
|
674
|
+
<Text color={colors.textMuted}>{agent.description}</Text>
|
|
675
|
+
</Box>
|
|
676
|
+
|
|
677
|
+
{agent.tools && agent.tools.length > 0 && (
|
|
678
|
+
<Box marginTop={1}>
|
|
679
|
+
<Text color={colors.textDim}>
|
|
680
|
+
Tools: {agent.tools.join(', ')}
|
|
681
|
+
</Text>
|
|
682
|
+
</Box>
|
|
683
|
+
)}
|
|
684
|
+
|
|
685
|
+
{agent.triggers && agent.triggers.length > 0 && (
|
|
686
|
+
<Box flexDirection="column" marginTop={1}>
|
|
687
|
+
<Text color={colors.textDim}>Triggers:</Text>
|
|
688
|
+
{agent.triggers.slice(0, 3).map((trigger, i) => (
|
|
689
|
+
<Text key={i} color={colors.textMuted}>
|
|
690
|
+
{' '}"{trigger}"
|
|
691
|
+
</Text>
|
|
692
|
+
))}
|
|
693
|
+
</Box>
|
|
694
|
+
)}
|
|
695
|
+
|
|
696
|
+
{isFocused && (
|
|
697
|
+
<Box flexDirection="row" marginTop={1}>
|
|
698
|
+
{actions.map((action, index) => (
|
|
699
|
+
<Text
|
|
700
|
+
key={action.id}
|
|
701
|
+
backgroundColor={
|
|
702
|
+
selectedAction === index
|
|
703
|
+
? action.variant === 'danger'
|
|
704
|
+
? colors.error
|
|
705
|
+
: colors.primary
|
|
706
|
+
: colors.bgSelected
|
|
707
|
+
}
|
|
708
|
+
color={selectedAction === index ? '#ffffff' : colors.textMuted}
|
|
709
|
+
bold={selectedAction === index}
|
|
710
|
+
>
|
|
711
|
+
{' '}{action.label}{' '}
|
|
712
|
+
</Text>
|
|
713
|
+
))}
|
|
714
|
+
</Box>
|
|
715
|
+
)}
|
|
716
|
+
</Box>
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function MarkdownLine({ line, inCodeBlock }: { line: string; inCodeBlock: boolean }) {
|
|
721
|
+
// Code block content
|
|
722
|
+
if (inCodeBlock) {
|
|
723
|
+
return <Text color="#a5d6ff">{line || ' '}</Text>;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Code block delimiter
|
|
727
|
+
if (line.startsWith('```')) {
|
|
728
|
+
return <Text color={colors.textDim}>{line}</Text>;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Headers
|
|
732
|
+
if (line.startsWith('# ')) {
|
|
733
|
+
return <Text color={colors.text} bold>{line.slice(2)}</Text>;
|
|
734
|
+
}
|
|
735
|
+
if (line.startsWith('## ')) {
|
|
736
|
+
return <Text color={colors.text} bold>{line.slice(3)}</Text>;
|
|
737
|
+
}
|
|
738
|
+
if (line.startsWith('### ')) {
|
|
739
|
+
return <Text color="#c9d1d9" bold>{line.slice(4)}</Text>;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// YAML frontmatter delimiter
|
|
743
|
+
if (line === '---') {
|
|
744
|
+
return <Text color={colors.textDim}>{line}</Text>;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// List items
|
|
748
|
+
if (line.match(/^[\s]*[-*]\s/)) {
|
|
749
|
+
return <Text color={colors.textMuted}>{line}</Text>;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Blockquotes
|
|
753
|
+
if (line.startsWith('>')) {
|
|
754
|
+
return <Text color="#8b949e" italic>{line}</Text>;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Table rows
|
|
758
|
+
if (line.includes('|')) {
|
|
759
|
+
return <Text color={colors.textMuted}>{line}</Text>;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Default
|
|
763
|
+
return <Text color={colors.textMuted}>{line || ' '}</Text>;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function ReadmeViewer({
|
|
767
|
+
title,
|
|
768
|
+
content,
|
|
769
|
+
onClose,
|
|
770
|
+
}: {
|
|
771
|
+
title: string;
|
|
772
|
+
content: string;
|
|
773
|
+
onClose: () => void;
|
|
774
|
+
}) {
|
|
775
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
776
|
+
const lines = useMemo(() => content.split('\n'), [content]);
|
|
777
|
+
const maxVisible = 20;
|
|
778
|
+
|
|
779
|
+
// Pre-compute code block state for each line
|
|
780
|
+
const lineStates = useMemo(() => {
|
|
781
|
+
const states: boolean[] = [];
|
|
782
|
+
let inCode = false;
|
|
783
|
+
for (const line of lines) {
|
|
784
|
+
if (line.startsWith('```')) {
|
|
785
|
+
states.push(false); // Delimiter itself is not "in" code block for styling
|
|
786
|
+
inCode = !inCode;
|
|
787
|
+
} else {
|
|
788
|
+
states.push(inCode);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return states;
|
|
792
|
+
}, [lines]);
|
|
793
|
+
|
|
794
|
+
// Max offset: when at end, we have top indicator + (maxVisible-1) content lines
|
|
795
|
+
// So max offset is lines.length - (maxVisible - 1) = lines.length - maxVisible + 1
|
|
796
|
+
const maxOffset = Math.max(0, lines.length - maxVisible + 1);
|
|
797
|
+
|
|
798
|
+
useInput((input, key) => {
|
|
799
|
+
if (key.escape) {
|
|
800
|
+
onClose();
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (key.upArrow) {
|
|
804
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
805
|
+
}
|
|
806
|
+
if (key.downArrow) {
|
|
807
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + 1));
|
|
808
|
+
}
|
|
809
|
+
if (key.pageDown || input === ' ') {
|
|
810
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + maxVisible));
|
|
811
|
+
}
|
|
812
|
+
if (key.pageUp) {
|
|
813
|
+
setScrollOffset((prev) => Math.max(0, prev - maxVisible));
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// Adjust visible lines based on whether indicators are shown
|
|
818
|
+
const showTopIndicator = scrollOffset > 0;
|
|
819
|
+
// Reserve space for bottom indicator if not at end
|
|
820
|
+
const contentLines = maxVisible - (showTopIndicator ? 1 : 0);
|
|
821
|
+
const endIndex = Math.min(scrollOffset + contentLines, lines.length);
|
|
822
|
+
const showBottomIndicator = endIndex < lines.length;
|
|
823
|
+
const actualContentLines = contentLines - (showBottomIndicator ? 1 : 0);
|
|
824
|
+
const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
|
|
825
|
+
|
|
826
|
+
return (
|
|
827
|
+
<Box flexDirection="column" padding={1}>
|
|
828
|
+
<Box marginBottom={1}>
|
|
829
|
+
<Text color={colors.text} bold>{title}</Text>
|
|
830
|
+
<Text color={colors.textDim}> · {lines.length} lines</Text>
|
|
831
|
+
</Box>
|
|
832
|
+
|
|
833
|
+
<Box
|
|
834
|
+
flexDirection="column"
|
|
835
|
+
borderStyle="single"
|
|
836
|
+
borderColor={colors.border}
|
|
837
|
+
paddingX={1}
|
|
838
|
+
>
|
|
839
|
+
{showTopIndicator && (
|
|
840
|
+
<Text color={colors.textDim}>↑ {scrollOffset} more lines</Text>
|
|
841
|
+
)}
|
|
842
|
+
{visibleLines.map((line, i) => (
|
|
843
|
+
<MarkdownLine key={scrollOffset + i} line={line} inCodeBlock={lineStates[scrollOffset + i]} />
|
|
844
|
+
))}
|
|
845
|
+
{showBottomIndicator && (
|
|
846
|
+
<Text color={colors.textDim}>↓ {lines.length - scrollOffset - actualContentLines} more lines</Text>
|
|
847
|
+
)}
|
|
848
|
+
</Box>
|
|
849
|
+
|
|
850
|
+
<Box marginTop={1}>
|
|
851
|
+
<Text color={colors.textDim}>↑↓ scroll · space/pgdn page · esc back</Text>
|
|
852
|
+
</Box>
|
|
558
853
|
</Box>
|
|
559
854
|
);
|
|
560
855
|
}
|
|
@@ -807,11 +1102,13 @@ function App() {
|
|
|
807
1102
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
808
1103
|
const [message, setMessage] = useState<{ text: string; type: 'success' | 'error' } | null>(null);
|
|
809
1104
|
const [isEditingSettings, setIsEditingSettings] = useState(false);
|
|
1105
|
+
const [readmeContent, setReadmeContent] = useState<{ title: string; content: string } | null>(null);
|
|
810
1106
|
|
|
811
1107
|
const MAX_VISIBLE_ITEMS = 6;
|
|
812
1108
|
|
|
813
1109
|
const skills = getBundledSkills();
|
|
814
1110
|
const commands = getCommandsFromSkills();
|
|
1111
|
+
const agents = getBundledAgents();
|
|
815
1112
|
|
|
816
1113
|
useInput((input, key) => {
|
|
817
1114
|
if (message) setMessage(null);
|
|
@@ -865,6 +1162,10 @@ function App() {
|
|
|
865
1162
|
if (key.return) {
|
|
866
1163
|
if (activeTab === 'skills' && skills.length > 0) {
|
|
867
1164
|
setView('detail');
|
|
1165
|
+
} else if (activeTab === 'commands' && commands.length > 0) {
|
|
1166
|
+
setView('detail');
|
|
1167
|
+
} else if (activeTab === 'agents' && agents.length > 0) {
|
|
1168
|
+
setView('detail');
|
|
868
1169
|
} else if (activeTab === 'settings') {
|
|
869
1170
|
setIsEditingSettings(true);
|
|
870
1171
|
setView('setup');
|
|
@@ -879,16 +1180,39 @@ function App() {
|
|
|
879
1180
|
setSelectedAction((prev) => Math.max(0, prev - 1));
|
|
880
1181
|
}
|
|
881
1182
|
if (key.rightArrow) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1183
|
+
let maxActions = 0;
|
|
1184
|
+
if (activeTab === 'skills') {
|
|
1185
|
+
const skill = skills[selectedIndex];
|
|
1186
|
+
const installed = skill ? isSkillInstalled(skill.name) : false;
|
|
1187
|
+
maxActions = installed ? 2 : 1; // View, Configure, Uninstall or View, Install
|
|
1188
|
+
} else if (activeTab === 'agents') {
|
|
1189
|
+
maxActions = 1; // View, Install/Uninstall
|
|
1190
|
+
} else if (activeTab === 'commands') {
|
|
1191
|
+
const command = commands[selectedIndex];
|
|
1192
|
+
// If parent skill is installed, only View is available
|
|
1193
|
+
const skillInstalled = command ? isSkillInstalled(command.skillName) : false;
|
|
1194
|
+
maxActions = skillInstalled ? 0 : 1;
|
|
1195
|
+
}
|
|
885
1196
|
setSelectedAction((prev) => Math.min(maxActions, prev + 1));
|
|
886
1197
|
}
|
|
887
1198
|
if (key.return && activeTab === 'skills') {
|
|
888
1199
|
const skill = skills[selectedIndex];
|
|
889
1200
|
if (skill) {
|
|
890
1201
|
const installed = isSkillInstalled(skill.name);
|
|
891
|
-
|
|
1202
|
+
// Actions: installed = [View, Configure, Uninstall], not installed = [View, Install]
|
|
1203
|
+
if (selectedAction === 0) {
|
|
1204
|
+
// View
|
|
1205
|
+
const skillMdPath = join(getBundledSkillsDir(), skill.name, 'SKILL.md');
|
|
1206
|
+
if (existsSync(skillMdPath)) {
|
|
1207
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
1208
|
+
setReadmeContent({ title: `${skill.name}/SKILL.md`, content });
|
|
1209
|
+
setView('readme');
|
|
1210
|
+
}
|
|
1211
|
+
} else if (installed && selectedAction === 1) {
|
|
1212
|
+
// Configure
|
|
1213
|
+
setView('configure');
|
|
1214
|
+
} else if (installed && selectedAction === 2) {
|
|
1215
|
+
// Uninstall
|
|
892
1216
|
const result = uninstallSkill(skill.name);
|
|
893
1217
|
setMessage({
|
|
894
1218
|
text: result.success ? `✓ Uninstalled ${skill.name}` : `✗ ${result.message}`,
|
|
@@ -898,10 +1222,8 @@ function App() {
|
|
|
898
1222
|
setView('menu');
|
|
899
1223
|
setSelectedAction(0);
|
|
900
1224
|
}
|
|
901
|
-
} else if (installed && selectedAction === 1) {
|
|
902
|
-
//
|
|
903
|
-
setView('configure');
|
|
904
|
-
} else if (!installed) {
|
|
1225
|
+
} else if (!installed && selectedAction === 1) {
|
|
1226
|
+
// Install
|
|
905
1227
|
const result = installSkill(skill.name);
|
|
906
1228
|
setMessage({
|
|
907
1229
|
text: result.success ? `✓ Installed ${skill.name}` : `✗ ${result.message}`,
|
|
@@ -914,11 +1236,91 @@ function App() {
|
|
|
914
1236
|
}
|
|
915
1237
|
}
|
|
916
1238
|
}
|
|
1239
|
+
if (key.return && activeTab === 'agents') {
|
|
1240
|
+
const agent = agents[selectedIndex];
|
|
1241
|
+
if (agent) {
|
|
1242
|
+
const installed = isAgentInstalled(agent.name);
|
|
1243
|
+
if (selectedAction === 0) {
|
|
1244
|
+
// View
|
|
1245
|
+
const agentMdPath = join(getBundledAgentsDir(), agent.name, 'AGENT.md');
|
|
1246
|
+
if (existsSync(agentMdPath)) {
|
|
1247
|
+
const content = readFileSync(agentMdPath, 'utf-8');
|
|
1248
|
+
setReadmeContent({ title: `${agent.name}/AGENT.md`, content });
|
|
1249
|
+
setView('readme');
|
|
1250
|
+
}
|
|
1251
|
+
} else if (installed && selectedAction === 1) {
|
|
1252
|
+
// Uninstall
|
|
1253
|
+
const result = uninstallAgent(agent.name);
|
|
1254
|
+
setMessage({
|
|
1255
|
+
text: result.success ? `✓ Uninstalled ${agent.name}` : `✗ ${result.message}`,
|
|
1256
|
+
type: result.success ? 'success' : 'error',
|
|
1257
|
+
});
|
|
1258
|
+
if (result.success) {
|
|
1259
|
+
setView('menu');
|
|
1260
|
+
setSelectedAction(0);
|
|
1261
|
+
}
|
|
1262
|
+
} else if (!installed && selectedAction === 1) {
|
|
1263
|
+
// Install
|
|
1264
|
+
const result = installAgent(agent.name);
|
|
1265
|
+
setMessage({
|
|
1266
|
+
text: result.success ? `✓ ${result.message}` : `✗ ${result.message}`,
|
|
1267
|
+
type: result.success ? 'success' : 'error',
|
|
1268
|
+
});
|
|
1269
|
+
if (result.success) {
|
|
1270
|
+
setView('menu');
|
|
1271
|
+
setSelectedAction(0);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (key.return && activeTab === 'commands') {
|
|
1277
|
+
const command = commands[selectedIndex];
|
|
1278
|
+
if (command) {
|
|
1279
|
+
const installed = isCommandInstalled(command.name, command.skillName);
|
|
1280
|
+
// Command file: extract part after skill name (e.g., "comments check" → "check.md")
|
|
1281
|
+
const cmdPart = command.name.startsWith(command.skillName + ' ')
|
|
1282
|
+
? command.name.slice(command.skillName.length + 1)
|
|
1283
|
+
: command.name;
|
|
1284
|
+
const commandMdPath = join(getBundledSkillsDir(), command.skillName, 'commands', `${cmdPart}.md`);
|
|
1285
|
+
|
|
1286
|
+
if (selectedAction === 0) {
|
|
1287
|
+
// View
|
|
1288
|
+
if (existsSync(commandMdPath)) {
|
|
1289
|
+
const content = readFileSync(commandMdPath, 'utf-8');
|
|
1290
|
+
setReadmeContent({ title: `/${command.name}`, content });
|
|
1291
|
+
setView('readme');
|
|
1292
|
+
}
|
|
1293
|
+
} else if (installed && selectedAction === 1) {
|
|
1294
|
+
// Uninstall
|
|
1295
|
+
const result = uninstallCommand(command.name, command.skillName);
|
|
1296
|
+
setMessage({
|
|
1297
|
+
text: result.success ? `✓ ${result.message}` : `✗ ${result.message}`,
|
|
1298
|
+
type: result.success ? 'success' : 'error',
|
|
1299
|
+
});
|
|
1300
|
+
if (result.success) {
|
|
1301
|
+
setView('menu');
|
|
1302
|
+
setSelectedAction(0);
|
|
1303
|
+
}
|
|
1304
|
+
} else if (!installed && selectedAction === 1) {
|
|
1305
|
+
// Install
|
|
1306
|
+
const result = installCommand(command.name, command.skillName);
|
|
1307
|
+
setMessage({
|
|
1308
|
+
text: result.success ? `✓ ${result.message}` : `✗ ${result.message}`,
|
|
1309
|
+
type: result.success ? 'success' : 'error',
|
|
1310
|
+
});
|
|
1311
|
+
if (result.success) {
|
|
1312
|
+
setView('menu');
|
|
1313
|
+
setSelectedAction(0);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
917
1318
|
}
|
|
918
1319
|
});
|
|
919
1320
|
|
|
920
1321
|
const selectedSkill = activeTab === 'skills' ? skills[selectedIndex] ?? null : null;
|
|
921
1322
|
const selectedCommand = activeTab === 'commands' ? commands[selectedIndex] ?? null : null;
|
|
1323
|
+
const selectedAgent = activeTab === 'agents' ? agents[selectedIndex] ?? null : null;
|
|
922
1324
|
|
|
923
1325
|
if (view === 'welcome') {
|
|
924
1326
|
return (
|
|
@@ -951,6 +1353,19 @@ function App() {
|
|
|
951
1353
|
);
|
|
952
1354
|
}
|
|
953
1355
|
|
|
1356
|
+
if (view === 'readme' && readmeContent) {
|
|
1357
|
+
return (
|
|
1358
|
+
<ReadmeViewer
|
|
1359
|
+
title={readmeContent.title}
|
|
1360
|
+
content={readmeContent.content}
|
|
1361
|
+
onClose={() => {
|
|
1362
|
+
setReadmeContent(null);
|
|
1363
|
+
setView('detail');
|
|
1364
|
+
}}
|
|
1365
|
+
/>
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
954
1369
|
if (view === 'configure' && selectedSkill) {
|
|
955
1370
|
return (
|
|
956
1371
|
<SkillConfigScreen
|
|
@@ -1041,9 +1456,27 @@ function App() {
|
|
|
1041
1456
|
)}
|
|
1042
1457
|
|
|
1043
1458
|
{activeTab === 'agents' && (
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1459
|
+
<>
|
|
1460
|
+
{scrollOffset > 0 && (
|
|
1461
|
+
<Box paddingX={1}>
|
|
1462
|
+
<Text color={colors.textDim}>↑ {scrollOffset} more</Text>
|
|
1463
|
+
</Box>
|
|
1464
|
+
)}
|
|
1465
|
+
{agents.length === 0 ? (
|
|
1466
|
+
<Box paddingX={1}>
|
|
1467
|
+
<Text color={colors.textDim}>No agents available</Text>
|
|
1468
|
+
</Box>
|
|
1469
|
+
) : (
|
|
1470
|
+
agents.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS).map((agent, index) => (
|
|
1471
|
+
<AgentItem key={agent.name} agent={agent} isSelected={scrollOffset + index === selectedIndex} />
|
|
1472
|
+
))
|
|
1473
|
+
)}
|
|
1474
|
+
{scrollOffset + MAX_VISIBLE_ITEMS < agents.length && (
|
|
1475
|
+
<Box paddingX={1}>
|
|
1476
|
+
<Text color={colors.textDim}>↓ {agents.length - scrollOffset - MAX_VISIBLE_ITEMS} more</Text>
|
|
1477
|
+
</Box>
|
|
1478
|
+
)}
|
|
1479
|
+
</>
|
|
1047
1480
|
)}
|
|
1048
1481
|
|
|
1049
1482
|
{activeTab === 'settings' && (
|
|
@@ -1066,16 +1499,16 @@ function App() {
|
|
|
1066
1499
|
<SkillDetails skill={selectedSkill} isFocused={view === 'detail'} selectedAction={selectedAction} />
|
|
1067
1500
|
)}
|
|
1068
1501
|
|
|
1069
|
-
{activeTab === 'commands' &&
|
|
1502
|
+
{activeTab === 'commands' && (
|
|
1503
|
+
<CommandDetails command={selectedCommand} isFocused={view === 'detail'} selectedAction={selectedAction} />
|
|
1504
|
+
)}
|
|
1070
1505
|
|
|
1071
1506
|
{activeTab === 'settings' && (
|
|
1072
1507
|
<SettingsDetails onEditSettings={() => setView('setup')} isFocused={false} />
|
|
1073
1508
|
)}
|
|
1074
1509
|
|
|
1075
1510
|
{activeTab === 'agents' && (
|
|
1076
|
-
<
|
|
1077
|
-
<Text color={colors.textDim}>Coming soon</Text>
|
|
1078
|
-
</Box>
|
|
1511
|
+
<AgentDetails agent={selectedAgent} isFocused={view === 'detail'} selectedAction={selectedAction} />
|
|
1079
1512
|
)}
|
|
1080
1513
|
|
|
1081
1514
|
{/* Message */}
|