@orderful/droid 0.3.0 → 0.4.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.
@@ -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: 'uninstall', label: 'Uninstall', variant: 'danger' },
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
- : [{ id: 'install', label: 'Install', variant: 'primary' }];
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({ command }: { command: Command | null }) {
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 installed = isSkillInstalled(command.skillName);
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
- {installed && <Text color={colors.success}> · installed</Text>}
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
+ {' '}&quot;{trigger}&quot;
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
- const skill = skills[selectedIndex];
883
- const installed = skill ? isSkillInstalled(skill.name) : false;
884
- const maxActions = installed ? 1 : 0;
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
- if (installed && selectedAction === 0) {
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
- // Configure
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
- <Box paddingX={1}>
1045
- <Text color={colors.textDim}>Coming soon</Text>
1046
- </Box>
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' && <CommandDetails command={selectedCommand} />}
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
- <Box paddingLeft={2} flexGrow={1}>
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 */}