@orderful/droid 0.10.5 → 0.11.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/.claude/CLAUDE.md +13 -6
- package/CHANGELOG.md +13 -0
- package/README.md +23 -24
- package/dist/bin/droid.js +9 -9
- package/dist/bin/droid.js.map +1 -1
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +24 -23
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/setup.d.ts +3 -3
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +32 -28
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +60 -53
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +213 -319
- package/dist/commands/tui.js.map +1 -1
- package/dist/commands/uninstall.d.ts +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +15 -6
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts +2 -2
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +9 -9
- package/dist/commands/update.js.map +1 -1
- package/dist/lib/agents.d.ts +11 -10
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +52 -54
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +42 -5
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/platforms.d.ts +41 -0
- package/dist/lib/platforms.d.ts.map +1 -0
- package/dist/lib/platforms.js +52 -0
- package/dist/lib/platforms.js.map +1 -0
- package/dist/lib/skills.d.ts +19 -11
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +125 -99
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/tools.d.ts +30 -0
- package/dist/lib/tools.d.ts.map +1 -0
- package/dist/lib/tools.js +116 -0
- package/dist/lib/tools.js.map +1 -0
- package/dist/lib/types.d.ts +45 -2
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +24 -5
- package/dist/lib/types.js.map +1 -1
- package/dist/tools/brain/TOOL.yaml +27 -0
- package/dist/tools/coach/TOOL.yaml +21 -0
- package/dist/tools/code-review/TOOL.yaml +18 -0
- package/dist/tools/comments/TOOL.yaml +27 -0
- package/dist/tools/project/TOOL.yaml +26 -0
- package/package.json +2 -2
- package/src/bin/droid.ts +9 -9
- package/src/commands/install.ts +24 -23
- package/src/commands/setup.test.ts +2 -2
- package/src/commands/setup.ts +33 -29
- package/src/commands/skills.ts +63 -64
- package/src/commands/tui.tsx +432 -578
- package/src/commands/uninstall.ts +17 -6
- package/src/commands/update.ts +10 -10
- package/src/lib/agents.ts +58 -58
- package/src/lib/config.test.ts +0 -10
- package/src/lib/config.ts +47 -5
- package/src/lib/platforms.ts +59 -0
- package/src/lib/skills.test.ts +53 -28
- package/src/lib/skills.ts +134 -101
- package/src/lib/tools.ts +140 -0
- package/src/lib/types.test.ts +15 -7
- package/src/lib/types.ts +63 -2
- package/src/tools/brain/TOOL.yaml +27 -0
- package/src/tools/coach/TOOL.yaml +21 -0
- package/src/tools/code-review/TOOL.yaml +18 -0
- package/src/tools/comments/TOOL.yaml +27 -0
- package/src/tools/project/TOOL.yaml +26 -0
- package/dist/agents/README.md +0 -137
- package/src/agents/README.md +0 -137
- /package/dist/{skills → tools}/README.md +0 -0
- /package/dist/{skills → tools}/brain/commands/README.md +0 -0
- /package/dist/{skills → tools}/brain/commands/brain.md +0 -0
- /package/dist/{skills → tools}/brain/commands/scratchpad.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/SKILL.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/SKILL.yaml +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/references/metadata.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/references/naming.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/references/templates.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain/references/workflows.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain-obsidian/SKILL.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain-obsidian/SKILL.yaml +0 -0
- /package/dist/{skills → tools/brain/skills}/brain-obsidian/references/templates.md +0 -0
- /package/dist/{skills → tools/brain/skills}/brain-obsidian/references/workflows.md +0 -0
- /package/dist/{skills → tools}/coach/commands/README.md +0 -0
- /package/dist/{skills → tools}/coach/commands/coach.md +0 -0
- /package/dist/{skills → tools/coach/skills}/coach/SKILL.md +0 -0
- /package/dist/{skills → tools/coach/skills}/coach/SKILL.yaml +0 -0
- /package/dist/{skills → tools}/code-review/agents/edi-standards-reviewer/AGENT.md +0 -0
- /package/dist/{skills → tools}/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -0
- /package/dist/{skills → tools}/code-review/agents/error-handling-reviewer/AGENT.md +0 -0
- /package/dist/{skills → tools}/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -0
- /package/dist/{skills → tools}/code-review/agents/test-coverage-analyzer/AGENT.md +0 -0
- /package/dist/{skills → tools}/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -0
- /package/dist/{skills → tools}/code-review/agents/type-reviewer/AGENT.md +0 -0
- /package/dist/{skills → tools}/code-review/agents/type-reviewer/AGENT.yaml +0 -0
- /package/dist/{skills → tools}/code-review/commands/code-review.md +0 -0
- /package/dist/{skills → tools/code-review/skills}/code-review/SKILL.md +0 -0
- /package/dist/{skills → tools/code-review/skills}/code-review/SKILL.yaml +0 -0
- /package/dist/{skills → tools}/comments/commands/README.md +0 -0
- /package/dist/{skills → tools}/comments/commands/comments.md +0 -0
- /package/dist/{skills → tools/comments/skills}/comments/SKILL.md +0 -0
- /package/dist/{skills → tools/comments/skills}/comments/SKILL.yaml +0 -0
- /package/dist/{skills → tools}/project/commands/README.md +0 -0
- /package/dist/{skills → tools}/project/commands/project.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/SKILL.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/SKILL.yaml +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/changelog.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/creating.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/loading.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/templates.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/updating.md +0 -0
- /package/dist/{skills → tools/project/skills}/project/references/versioning.md +0 -0
- /package/src/{skills → tools}/README.md +0 -0
- /package/src/{skills → tools}/brain/commands/README.md +0 -0
- /package/src/{skills → tools}/brain/commands/brain.md +0 -0
- /package/src/{skills → tools}/brain/commands/scratchpad.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain/SKILL.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain/SKILL.yaml +0 -0
- /package/src/{skills → tools/brain/skills}/brain/references/metadata.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain/references/naming.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain/references/templates.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain/references/workflows.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain-obsidian/SKILL.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain-obsidian/SKILL.yaml +0 -0
- /package/src/{skills → tools/brain/skills}/brain-obsidian/references/templates.md +0 -0
- /package/src/{skills → tools/brain/skills}/brain-obsidian/references/workflows.md +0 -0
- /package/src/{skills → tools}/coach/commands/README.md +0 -0
- /package/src/{skills → tools}/coach/commands/coach.md +0 -0
- /package/src/{skills → tools/coach/skills}/coach/SKILL.md +0 -0
- /package/src/{skills → tools/coach/skills}/coach/SKILL.yaml +0 -0
- /package/src/{skills → tools}/code-review/agents/edi-standards-reviewer/AGENT.md +0 -0
- /package/src/{skills → tools}/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -0
- /package/src/{skills → tools}/code-review/agents/error-handling-reviewer/AGENT.md +0 -0
- /package/src/{skills → tools}/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -0
- /package/src/{skills → tools}/code-review/agents/test-coverage-analyzer/AGENT.md +0 -0
- /package/src/{skills → tools}/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -0
- /package/src/{skills → tools}/code-review/agents/type-reviewer/AGENT.md +0 -0
- /package/src/{skills → tools}/code-review/agents/type-reviewer/AGENT.yaml +0 -0
- /package/src/{skills → tools}/code-review/commands/code-review.md +0 -0
- /package/src/{skills → tools/code-review/skills}/code-review/SKILL.md +0 -0
- /package/src/{skills → tools/code-review/skills}/code-review/SKILL.yaml +0 -0
- /package/src/{skills → tools}/comments/commands/README.md +0 -0
- /package/src/{skills → tools}/comments/commands/comments.md +0 -0
- /package/src/{skills → tools/comments/skills}/comments/SKILL.md +0 -0
- /package/src/{skills → tools/comments/skills}/comments/SKILL.yaml +0 -0
- /package/src/{skills → tools}/project/commands/README.md +0 -0
- /package/src/{skills → tools}/project/commands/project.md +0 -0
- /package/src/{skills → tools/project/skills}/project/SKILL.md +0 -0
- /package/src/{skills → tools/project/skills}/project/SKILL.yaml +0 -0
- /package/src/{skills → tools/project/skills}/project/references/changelog.md +0 -0
- /package/src/{skills → tools/project/skills}/project/references/creating.md +0 -0
- /package/src/{skills → tools/project/skills}/project/references/loading.md +0 -0
- /package/src/{skills → tools/project/skills}/project/references/templates.md +0 -0
- /package/src/{skills → tools/project/skills}/project/references/updating.md +0 -0
- /package/src/{skills → tools/project/skills}/project/references/versioning.md +0 -0
package/dist/commands/tui.js
CHANGED
|
@@ -3,13 +3,13 @@ import { render, Box, Text, useInput, useApp } from 'ink';
|
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
4
|
import { useState, useMemo } from 'react';
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
|
-
import { existsSync,
|
|
6
|
+
import { existsSync, readFileSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
|
-
import { getBundledSkills,
|
|
9
|
-
import {
|
|
8
|
+
import { getBundledSkills, installSkill, uninstallSkill, updateSkill, } from '../lib/skills.js';
|
|
9
|
+
import { getBundledTools, getBundledToolsDir, isToolInstalled, getToolUpdateStatus, getInstalledToolVersion } from '../lib/tools.js';
|
|
10
10
|
import { configExists, loadConfig, saveConfig, loadSkillOverrides, saveSkillOverrides } from '../lib/config.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { configurePlatformPermissions } from './setup.js';
|
|
12
|
+
import { Platform, BuiltInOutput, ConfigOptionType } from '../lib/types.js';
|
|
13
13
|
import { getVersion, getUpdateInfo, runUpdate } from '../lib/version.js';
|
|
14
14
|
import { getRandomQuote } from '../lib/quotes.js';
|
|
15
15
|
const colors = {
|
|
@@ -21,87 +21,61 @@ const colors = {
|
|
|
21
21
|
textDim: '#6a6a6a',
|
|
22
22
|
success: '#4ade80',
|
|
23
23
|
error: '#f87171',
|
|
24
|
+
// Component type badges
|
|
25
|
+
skill: '#ec4899', // pink/magenta
|
|
26
|
+
command: '#22d3ee', // cyan
|
|
27
|
+
agent: '#fbbf24', // yellow/amber
|
|
24
28
|
};
|
|
25
|
-
function
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Find first H1 header
|
|
52
|
-
let headerMatch = null;
|
|
53
|
-
for (let i = startLine; i < lines.length; i++) {
|
|
54
|
-
headerMatch = lines[i]?.match(/^#\s+\/(\S+)\s*-?\s*(.*)/);
|
|
55
|
-
if (headerMatch)
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
if (!headerMatch)
|
|
59
|
-
continue;
|
|
60
|
-
const usage = [];
|
|
61
|
-
let inUsage = false;
|
|
62
|
-
for (const line of lines) {
|
|
63
|
-
if (line.startsWith('## Usage'))
|
|
64
|
-
inUsage = true;
|
|
65
|
-
else if (line.startsWith('## ') && inUsage)
|
|
66
|
-
break;
|
|
67
|
-
else if (inUsage && line.startsWith('/'))
|
|
68
|
-
usage.push(line.trim());
|
|
69
|
-
}
|
|
70
|
-
commands.push({
|
|
71
|
-
name: headerMatch[1],
|
|
72
|
-
description: headerMatch[2] || '',
|
|
73
|
-
skillName,
|
|
74
|
-
usage,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
29
|
+
function Badge({ type, label, isSelected = false, dimmed = false, }) {
|
|
30
|
+
const color = colors[type];
|
|
31
|
+
const displayLabel = label || type.charAt(0).toUpperCase() + type.slice(1);
|
|
32
|
+
if (dimmed) {
|
|
33
|
+
return (_jsx(Text, { color: colors.textDim, children: displayLabel }));
|
|
34
|
+
}
|
|
35
|
+
return (_jsx(Text, { backgroundColor: isSelected ? color : undefined, color: isSelected ? '#000000' : color, bold: isSelected, children: isSelected ? ` ${displayLabel} ` : displayLabel }));
|
|
36
|
+
}
|
|
37
|
+
function ComponentBadges({ tool, compact = false }) {
|
|
38
|
+
const hasSkills = tool.includes.skills.length > 0;
|
|
39
|
+
const hasCommands = tool.includes.commands.length > 0;
|
|
40
|
+
const hasAgents = tool.includes.agents.length > 0;
|
|
41
|
+
if (compact) {
|
|
42
|
+
// Show colored squares for list view (single char with spacing)
|
|
43
|
+
const parts = [];
|
|
44
|
+
if (hasSkills)
|
|
45
|
+
parts.push('skill');
|
|
46
|
+
if (hasCommands)
|
|
47
|
+
parts.push('command');
|
|
48
|
+
if (hasAgents)
|
|
49
|
+
parts.push('agent');
|
|
50
|
+
return (_jsx(Box, { flexDirection: "row", children: parts.map((type, i) => (_jsxs(Text, { children: [_jsx(Text, { backgroundColor: colors[type], children: " " }), i < parts.length - 1 && ' '] }, type))) }));
|
|
77
51
|
}
|
|
78
|
-
return commands;
|
|
52
|
+
return (_jsxs(Box, { flexDirection: "row", children: [hasSkills && (_jsx(Box, { marginRight: 1, children: _jsx(Text, { backgroundColor: colors.skill, color: "#000000", bold: true, children: ` ${tool.includes.skills.length} skill${tool.includes.skills.length > 1 ? 's' : ''} ` }) })), hasCommands && (_jsx(Box, { marginRight: 1, children: _jsx(Text, { backgroundColor: colors.command, color: "#000000", bold: true, children: ` ${tool.includes.commands.length} cmd${tool.includes.commands.length > 1 ? 's' : ''} ` }) })), hasAgents && (_jsx(Box, { marginRight: 1, children: _jsx(Text, { backgroundColor: colors.agent, color: "#000000", bold: true, children: ` ${tool.includes.agents.length} agent${tool.includes.agents.length > 1 ? 's' : ''} ` }) }))] }));
|
|
79
53
|
}
|
|
80
|
-
function
|
|
54
|
+
function detectInstalledPlatforms() {
|
|
81
55
|
const installed = new Set();
|
|
82
56
|
try {
|
|
83
57
|
execSync('claude --version', { stdio: 'ignore' });
|
|
84
|
-
installed.add(
|
|
58
|
+
installed.add(Platform.ClaudeCode);
|
|
85
59
|
}
|
|
86
60
|
catch {
|
|
87
61
|
// Claude Code not found
|
|
88
62
|
}
|
|
89
63
|
try {
|
|
90
64
|
execSync('opencode --version', { stdio: 'ignore' });
|
|
91
|
-
installed.add(
|
|
65
|
+
installed.add(Platform.OpenCode);
|
|
92
66
|
}
|
|
93
67
|
catch {
|
|
94
68
|
// OpenCode not found
|
|
95
69
|
}
|
|
96
70
|
return installed;
|
|
97
71
|
}
|
|
98
|
-
function
|
|
99
|
-
const installed =
|
|
100
|
-
if (installed.has(
|
|
101
|
-
return
|
|
102
|
-
if (installed.has(
|
|
103
|
-
return
|
|
104
|
-
return
|
|
72
|
+
function getDefaultPlatform() {
|
|
73
|
+
const installed = detectInstalledPlatforms();
|
|
74
|
+
if (installed.has(Platform.ClaudeCode))
|
|
75
|
+
return Platform.ClaudeCode;
|
|
76
|
+
if (installed.has(Platform.OpenCode))
|
|
77
|
+
return Platform.OpenCode;
|
|
78
|
+
return Platform.ClaudeCode;
|
|
105
79
|
}
|
|
106
80
|
function getOutputOptions() {
|
|
107
81
|
const options = [
|
|
@@ -116,50 +90,6 @@ function getOutputOptions() {
|
|
|
116
90
|
}
|
|
117
91
|
return options;
|
|
118
92
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Check if an agent belongs to a skill bundle (whether installed or not)
|
|
121
|
-
* Returns the skill name if so, null otherwise
|
|
122
|
-
*/
|
|
123
|
-
function getAgentBundledSkill(agentName) {
|
|
124
|
-
const skillsDir = getBundledSkillsDir();
|
|
125
|
-
if (!existsSync(skillsDir))
|
|
126
|
-
return null;
|
|
127
|
-
const skillDirs = readdirSync(skillsDir, { withFileTypes: true })
|
|
128
|
-
.filter((d) => d.isDirectory())
|
|
129
|
-
.map((d) => d.name);
|
|
130
|
-
for (const skillName of skillDirs) {
|
|
131
|
-
const agentDir = join(skillsDir, skillName, 'agents', agentName);
|
|
132
|
-
if (existsSync(agentDir)) {
|
|
133
|
-
return skillName;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Find the source path for an agent's AGENT.md
|
|
140
|
-
* Checks both standalone agents dir and skill-bundled agents
|
|
141
|
-
*/
|
|
142
|
-
function findAgentSourcePath(agentName) {
|
|
143
|
-
// Check standalone agents first
|
|
144
|
-
const standalonePath = join(getBundledAgentsDir(), agentName, 'AGENT.md');
|
|
145
|
-
if (existsSync(standalonePath)) {
|
|
146
|
-
return standalonePath;
|
|
147
|
-
}
|
|
148
|
-
// Check skill-bundled agents
|
|
149
|
-
const skillsDir = getBundledSkillsDir();
|
|
150
|
-
if (existsSync(skillsDir)) {
|
|
151
|
-
const skillDirs = readdirSync(skillsDir, { withFileTypes: true })
|
|
152
|
-
.filter((d) => d.isDirectory())
|
|
153
|
-
.map((d) => d.name);
|
|
154
|
-
for (const skillName of skillDirs) {
|
|
155
|
-
const skillAgentPath = join(skillsDir, skillName, 'agents', agentName, 'AGENT.md');
|
|
156
|
-
if (existsSync(skillAgentPath)) {
|
|
157
|
-
return skillAgentPath;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
93
|
function WelcomeScreen({ onContinue, onUpdate, onExit, updateInfo, isUpdating }) {
|
|
164
94
|
const [selectedButton, setSelectedButton] = useState(0);
|
|
165
95
|
const welcomeQuote = useMemo(() => getRandomQuote(), []);
|
|
@@ -195,18 +125,18 @@ function WelcomeScreen({ onContinue, onUpdate, onExit, updateInfo, isUpdating })
|
|
|
195
125
|
return (_jsx(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: _jsxs(Box, { flexDirection: "column", alignItems: "center", borderStyle: "single", borderColor: hasUpdate ? '#eab308' : colors.border, paddingX: 4, paddingY: 1, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }), _jsx(Text, { color: colors.text, children: "droid" }), _jsxs(Text, { color: colors.textDim, children: [" v", updateInfo.currentVersion] })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "\u2551 " }), _jsx(Text, { color: hasUpdate ? '#eab308' : colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: hasUpdate ? '#eab308' : colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " \u2551 " }), _jsx(Text, { color: colors.textMuted, children: "Droid, teaching your AI new tricks" })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D " }), _jsx(Text, { color: colors.textDim, children: "github.com/Orderful/droid" })] })] }), hasUpdate ? (_jsxs(_Fragment, { children: [_jsxs(Box, { marginTop: 2, marginBottom: 1, flexDirection: "column", alignItems: "center", children: [_jsx(Text, { color: "#eab308", italic: true, children: "\"The odds of functioning optimally without this" }), _jsx(Text, { color: "#eab308", italic: true, children: "update are approximately 3,720 to 1.\"" })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.textMuted, children: ["v", updateInfo.currentVersion, " \u2192 v", updateInfo.latestVersion] }) }), isUpdating ? (_jsx(Text, { color: "#eab308", children: "Updating..." })) : (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { backgroundColor: selectedButton === 0 ? '#eab308' : colors.bgSelected, color: selectedButton === 0 ? '#000000' : colors.textMuted, bold: selectedButton === 0, children: [' ', "Update", ' '] }), _jsx(Text, { children: " " }), _jsxs(Text, { backgroundColor: selectedButton === 1 ? colors.bgSelected : undefined, color: selectedButton === 1 ? colors.text : colors.textMuted, bold: selectedButton === 1, children: [' ', "Skip for now", ' '] })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 select \u00B7 enter" }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 2, marginBottom: 1, children: _jsx(Text, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: ` ${welcomeQuote} ` }) }), _jsx(Text, { color: colors.textDim, children: "press enter" })] }))] }) }));
|
|
196
126
|
}
|
|
197
127
|
function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
198
|
-
const [step, setStep] = useState('
|
|
199
|
-
const [
|
|
128
|
+
const [step, setStep] = useState('platform');
|
|
129
|
+
const [platform, setPlatform] = useState(initialConfig?.platform || getDefaultPlatform());
|
|
200
130
|
const [userMention, setUserMention] = useState(initialConfig?.user_mention || '@user');
|
|
201
131
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
202
132
|
const [error, setError] = useState(null);
|
|
203
133
|
// Cache detection results once on mount (avoids re-running execSync on every render)
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
{ label: 'Claude Code', value:
|
|
207
|
-
{ label: 'OpenCode', value:
|
|
134
|
+
const installedPlatforms = useMemo(() => detectInstalledPlatforms(), []);
|
|
135
|
+
const platformOptions = useMemo(() => [
|
|
136
|
+
{ label: 'Claude Code', value: Platform.ClaudeCode },
|
|
137
|
+
{ label: 'OpenCode', value: Platform.OpenCode },
|
|
208
138
|
], []);
|
|
209
|
-
const steps = ['
|
|
139
|
+
const steps = ['platform', 'user_mention', 'confirm'];
|
|
210
140
|
const stepIndex = steps.indexOf(step);
|
|
211
141
|
const totalSteps = steps.length - 1; // Don't count confirm as a step
|
|
212
142
|
const handleUserMentionSubmit = () => {
|
|
@@ -219,14 +149,14 @@ function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
|
219
149
|
// Handle escape during text input (only intercept escape, nothing else)
|
|
220
150
|
useInput((input, key) => {
|
|
221
151
|
if (key.escape) {
|
|
222
|
-
setStep('
|
|
152
|
+
setStep('platform');
|
|
223
153
|
setSelectedIndex(0);
|
|
224
154
|
}
|
|
225
155
|
}, { isActive: step === 'user_mention' });
|
|
226
156
|
// Handle all input for non-text-input steps
|
|
227
157
|
useInput((input, key) => {
|
|
228
158
|
if (key.escape) {
|
|
229
|
-
if (step === '
|
|
159
|
+
if (step === 'platform') {
|
|
230
160
|
onSkip();
|
|
231
161
|
}
|
|
232
162
|
else if (step === 'confirm') {
|
|
@@ -234,63 +164,60 @@ function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
|
234
164
|
}
|
|
235
165
|
return;
|
|
236
166
|
}
|
|
237
|
-
if (step === '
|
|
167
|
+
if (step === 'platform') {
|
|
238
168
|
if (key.upArrow)
|
|
239
169
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
240
170
|
if (key.downArrow)
|
|
241
|
-
setSelectedIndex((prev) => Math.min(
|
|
171
|
+
setSelectedIndex((prev) => Math.min(platformOptions.length - 1, prev + 1));
|
|
242
172
|
if (key.return) {
|
|
243
|
-
|
|
173
|
+
setPlatform(platformOptions[selectedIndex].value);
|
|
244
174
|
setStep('user_mention');
|
|
245
175
|
}
|
|
246
176
|
}
|
|
247
177
|
else if (step === 'confirm') {
|
|
248
178
|
if (key.return) {
|
|
179
|
+
const existingConfig = loadConfig();
|
|
249
180
|
const config = {
|
|
250
|
-
...
|
|
251
|
-
|
|
181
|
+
...existingConfig,
|
|
182
|
+
platform: platform,
|
|
252
183
|
user_mention: userMention,
|
|
253
184
|
output_preference: BuiltInOutput.Terminal, // Default to terminal
|
|
254
185
|
};
|
|
255
186
|
saveConfig(config);
|
|
256
|
-
|
|
187
|
+
configurePlatformPermissions(platform);
|
|
257
188
|
onComplete();
|
|
258
189
|
}
|
|
259
190
|
}
|
|
260
191
|
}, { isActive: step !== 'user_mention' });
|
|
261
192
|
const renderHeader = () => (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "[" }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: "] " }), _jsx(Text, { color: colors.text, bold: true, children: "droid setup" }), _jsxs(Text, { color: colors.textDim, children: [" \u00B7 Step ", Math.min(stepIndex + 1, totalSteps), " of ", totalSteps] })] }) }));
|
|
262
|
-
if (step === '
|
|
263
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [renderHeader(), _jsx(Text, { color: colors.text, children: "Which
|
|
193
|
+
if (step === 'platform') {
|
|
194
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [renderHeader(), _jsx(Text, { color: colors.text, children: "Which platform are you using?" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: platformOptions.map((option, index) => (_jsxs(Text, { children: [_jsxs(Text, { color: colors.textDim, children: [index === selectedIndex ? '>' : ' ', " "] }), _jsx(Text, { color: index === selectedIndex ? colors.text : colors.textMuted, children: option.label }), installedPlatforms.has(option.value) && _jsx(Text, { color: colors.success, children: " (detected)" })] }, option.value))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2191\u2193 select \u00B7 enter next \u00B7 esc skip" }) })] }));
|
|
264
195
|
}
|
|
265
196
|
if (step === 'user_mention') {
|
|
266
197
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [renderHeader(), _jsx(Text, { color: colors.text, children: "What @mention should be used for you?" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.textDim, children: '> ' }), _jsx(TextInput, { value: userMention, onChange: setUserMention, onSubmit: handleUserMentionSubmit, placeholder: "@user" })] }), error && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.error, children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "enter next \u00B7 esc back" }) })] }));
|
|
267
198
|
}
|
|
268
199
|
// Confirm step
|
|
269
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [renderHeader(), _jsx(Text, { color: colors.text, bold: true, children: "Review your settings" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "
|
|
200
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [renderHeader(), _jsx(Text, { color: colors.text, bold: true, children: "Review your settings" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "Platform: " }), _jsx(Text, { color: colors.text, children: platform === Platform.ClaudeCode ? 'Claude Code' : 'OpenCode' })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "Your @mention: " }), _jsx(Text, { color: colors.text, children: userMention })] })] }), _jsx(Box, { marginTop: 2, children: _jsxs(Text, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: [' ', "Save", ' '] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "enter save \u00B7 esc back" }) })] }));
|
|
270
201
|
}
|
|
271
202
|
function TabBar({ tabs, activeTab }) {
|
|
272
203
|
return (_jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: tabs.map((tab) => (_jsxs(Text, { backgroundColor: tab.id === activeTab ? colors.primary : undefined, color: tab.id === activeTab ? '#ffffff' : colors.textMuted, bold: tab.id === activeTab, wrap: "truncate", children: [' ', tab.label, ' '] }, tab.id))) }));
|
|
273
204
|
}
|
|
274
|
-
function
|
|
275
|
-
const installed =
|
|
276
|
-
const
|
|
277
|
-
const updateStatus =
|
|
278
|
-
return (_jsx(Box, { paddingX: 1, backgroundColor: isActive ? colors.bgSelected : undefined, children: _jsxs(Text, { children: [_jsxs(Text, { color: colors.textDim, children: [isSelected ? '>' : ' ', " "] }), _jsx(Text, { color: isSelected || isActive ? colors.text : colors.textMuted, children:
|
|
205
|
+
function ToolItem({ tool, isSelected, isActive, }) {
|
|
206
|
+
const installed = isToolInstalled(tool.name);
|
|
207
|
+
const installedVersion = getInstalledToolVersion(tool.name);
|
|
208
|
+
const updateStatus = getToolUpdateStatus(tool.name);
|
|
209
|
+
return (_jsx(Box, { paddingX: 1, backgroundColor: isActive ? colors.bgSelected : undefined, children: _jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { color: colors.textDim, children: [isSelected ? '>' : ' ', " "] }), _jsx(Text, { color: isSelected || isActive ? colors.text : colors.textMuted, children: tool.name }), installed && installedVersion && _jsxs(Text, { color: colors.textDim, children: [" v", installedVersion] }), installed && _jsx(Text, { color: colors.success, children: " \u2713" }), updateStatus.hasUpdate && _jsx(Text, { color: colors.primary, children: " \u2191" }), _jsx(Text, { children: " " }), tool.includes.skills.length > 0 && _jsx(Text, { color: colors.skill, children: "\u25CF " }), tool.includes.commands.length > 0 && _jsx(Text, { color: colors.command, children: "\u25CF " }), tool.includes.agents.length > 0 && _jsx(Text, { color: colors.agent, children: "\u25CF" })] }) }));
|
|
279
210
|
}
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
function SkillDetails({ skill, isFocused, selectedAction, }) {
|
|
285
|
-
if (!skill) {
|
|
286
|
-
return (_jsx(Box, { paddingLeft: 2, flexGrow: 1, children: _jsx(Text, { color: colors.textDim, children: "Select a skill" }) }));
|
|
211
|
+
function ToolDetails({ tool, isFocused, selectedAction, }) {
|
|
212
|
+
if (!tool) {
|
|
213
|
+
return (_jsx(Box, { paddingLeft: 2, flexGrow: 1, children: _jsx(Text, { color: colors.textDim, children: "Select a tool" }) }));
|
|
287
214
|
}
|
|
288
|
-
const installed =
|
|
289
|
-
const
|
|
290
|
-
const
|
|
215
|
+
const installed = isToolInstalled(tool.name);
|
|
216
|
+
const installedVersion = getInstalledToolVersion(tool.name);
|
|
217
|
+
const updateStatus = getToolUpdateStatus(tool.name);
|
|
291
218
|
const actions = installed
|
|
292
219
|
? [
|
|
293
|
-
{ id: '
|
|
220
|
+
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
294
221
|
...(updateStatus.hasUpdate
|
|
295
222
|
? [{ id: 'update', label: `Update (${updateStatus.bundledVersion})`, variant: 'primary' }]
|
|
296
223
|
: []),
|
|
@@ -298,59 +225,10 @@ function SkillDetails({ skill, isFocused, selectedAction, }) {
|
|
|
298
225
|
{ id: 'uninstall', label: 'Uninstall', variant: 'danger' },
|
|
299
226
|
]
|
|
300
227
|
: [
|
|
301
|
-
{ id: '
|
|
228
|
+
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
302
229
|
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
303
230
|
];
|
|
304
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsx(Text, { color: colors.text, bold: true, children:
|
|
305
|
-
.trim()
|
|
306
|
-
.split('\n')
|
|
307
|
-
.slice(0, 3)
|
|
308
|
-
.map((line, j) => (_jsxs(Text, { color: colors.textDim, children: [' ', line] }, j)))] }, i)))] })), isFocused && (_jsx(Box, { flexDirection: "row", marginTop: 1, children: actions.map((action, index) => (_jsxs(Text, { backgroundColor: selectedAction === index
|
|
309
|
-
? action.variant === 'danger'
|
|
310
|
-
? colors.error
|
|
311
|
-
: colors.primary
|
|
312
|
-
: colors.bgSelected, color: selectedAction === index ? '#ffffff' : colors.textMuted, bold: selectedAction === index, children: [' ', action.label, ' '] }, action.id))) }))] }));
|
|
313
|
-
}
|
|
314
|
-
function CommandDetails({ command, isFocused, selectedAction, }) {
|
|
315
|
-
if (!command) {
|
|
316
|
-
return (_jsx(Box, { paddingLeft: 2, flexGrow: 1, children: _jsx(Text, { color: colors.textDim, children: "Select a command" }) }));
|
|
317
|
-
}
|
|
318
|
-
const skillInstalled = isSkillInstalled(command.skillName);
|
|
319
|
-
// Commands belong to skills - only show View, install via skill
|
|
320
|
-
const actions = [{ id: 'view', label: 'View', variant: 'default' }];
|
|
321
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsxs(Text, { color: colors.text, bold: true, children: ["/", command.name] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: ["from ", command.skillName, skillInstalled && _jsx(Text, { color: colors.success, children: " \u00B7 installed" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textMuted, children: command.description }) }), command.usage.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.textDim, children: "Usage:" }), command.usage.map((u, i) => (_jsxs(Text, { color: colors.textMuted, children: [' ', u] }, i)))] })), isFocused && (_jsx(Box, { flexDirection: "row", marginTop: 1, children: actions.map((action, index) => (_jsxs(Text, { backgroundColor: selectedAction === index
|
|
322
|
-
? action.variant === 'danger'
|
|
323
|
-
? colors.error
|
|
324
|
-
: colors.primary
|
|
325
|
-
: colors.bgSelected, color: selectedAction === index ? '#ffffff' : colors.textMuted, bold: selectedAction === index, children: [' ', action.label, ' '] }, action.id))) }))] }));
|
|
326
|
-
}
|
|
327
|
-
function AgentItem({ agent, isSelected }) {
|
|
328
|
-
const statusDisplay = agent.status === 'alpha' ? '[alpha]' : agent.status === 'beta' ? '[beta]' : '';
|
|
329
|
-
return (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { children: [_jsxs(Text, { color: colors.textDim, children: [isSelected ? '>' : ' ', " "] }), _jsx(Text, { color: isSelected ? colors.text : colors.textMuted, children: agent.name }), _jsxs(Text, { color: colors.textDim, children: [" v", agent.version] }), statusDisplay && _jsxs(Text, { color: colors.textDim, children: [" ", statusDisplay] })] }) }));
|
|
330
|
-
}
|
|
331
|
-
function AgentDetails({ agent, isFocused, selectedAction, }) {
|
|
332
|
-
if (!agent) {
|
|
333
|
-
return (_jsx(Box, { paddingLeft: 2, flexGrow: 1, children: _jsx(Text, { color: colors.textDim, children: "Select an agent" }) }));
|
|
334
|
-
}
|
|
335
|
-
const installed = isAgentInstalled(agent.name);
|
|
336
|
-
const bundledSkill = getAgentBundledSkill(agent.name);
|
|
337
|
-
const skillInstalled = bundledSkill ? isSkillInstalled(bundledSkill) : false;
|
|
338
|
-
const statusDisplay = agent.status === 'alpha' ? '[alpha]' : agent.status === 'beta' ? '[beta]' : '';
|
|
339
|
-
const modeDisplay = agent.mode === 'primary' ? 'primary' : agent.mode === 'all' ? 'primary/subagent' : 'subagent';
|
|
340
|
-
// If agent belongs to a skill, only show View (install via skill)
|
|
341
|
-
// Otherwise show Install/Uninstall for standalone agents
|
|
342
|
-
const actions = bundledSkill
|
|
343
|
-
? [{ id: 'view', label: 'View', variant: 'default' }]
|
|
344
|
-
: installed
|
|
345
|
-
? [
|
|
346
|
-
{ id: 'view', label: 'View', variant: 'default' },
|
|
347
|
-
{ id: 'uninstall', label: 'Uninstall', variant: 'danger' },
|
|
348
|
-
]
|
|
349
|
-
: [
|
|
350
|
-
{ id: 'view', label: 'View', variant: 'default' },
|
|
351
|
-
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
352
|
-
];
|
|
353
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsx(Text, { color: colors.text, bold: true, children: agent.name }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: ["v", agent.version, statusDisplay && _jsxs(Text, { children: [" \u00B7 ", statusDisplay] }), ' · ', modeDisplay, bundledSkill && _jsxs(Text, { color: colors.textMuted, children: [" \u00B7 from ", bundledSkill] }), skillInstalled && _jsx(Text, { color: colors.success, children: " \u00B7 installed" }), !bundledSkill && installed && _jsx(Text, { color: colors.success, children: " \u00B7 installed" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textMuted, children: agent.description }) }), agent.tools && agent.tools.length > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: ["Tools: ", agent.tools.join(', ')] }) })), agent.triggers && agent.triggers.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.textDim, children: "Triggers:" }), agent.triggers.slice(0, 3).map((trigger, i) => (_jsxs(Text, { color: colors.textMuted, children: [' ', "\"", trigger, "\""] }, i)))] })), isFocused && (_jsx(Box, { flexDirection: "row", marginTop: 1, children: actions.map((action, index) => (_jsxs(Text, { backgroundColor: selectedAction === index
|
|
231
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsx(Text, { color: colors.text, bold: true, children: tool.name }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: [tool.version, tool.status && ` · ${tool.status}`, installed && _jsx(Text, { color: colors.success, children: " \u00B7 installed" }), updateStatus.hasUpdate && (_jsxs(Text, { color: colors.primary, children: [" \u00B7 update (", installedVersion, " \u2192 ", updateStatus.bundledVersion, ")"] }))] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textMuted, children: tool.description }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.textDim, children: "Includes:" }), _jsx(Box, { marginTop: 1, children: _jsx(ComponentBadges, { tool: tool }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [tool.includes.skills.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: colors.skill, children: "Skills: " }), _jsx(Text, { color: colors.textMuted, children: tool.includes.skills.map(s => s.name).join(', ') })] })), tool.includes.commands.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: colors.command, children: "Commands: " }), _jsx(Text, { color: colors.textMuted, children: tool.includes.commands.map(c => `/${c}`).join(', ') })] })), tool.includes.agents.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: colors.agent, children: "Agents: " }), _jsx(Text, { color: colors.textMuted, children: tool.includes.agents.join(', ') })] }))] })] }), isFocused && (_jsx(Box, { flexDirection: "row", marginTop: 1, children: actions.map((action, index) => (_jsxs(Text, { backgroundColor: selectedAction === index
|
|
354
232
|
? action.variant === 'danger'
|
|
355
233
|
? colors.error
|
|
356
234
|
: colors.primary
|
|
@@ -444,10 +322,88 @@ function ReadmeViewer({ title, content, onClose, }) {
|
|
|
444
322
|
const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
|
|
445
323
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: colors.text, bold: true, children: title }), _jsxs(Text, { color: colors.textDim, children: [" \u00B7 ", lines.length, " lines"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: colors.border, paddingX: 1, children: [showTopIndicator && (_jsxs(Text, { color: colors.textDim, children: ["\u2191 ", scrollOffset, " more lines"] })), visibleLines.map((line, i) => (_jsx(MarkdownLine, { line: line, inCodeBlock: lineStates[scrollOffset + i] }, scrollOffset + i))), showBottomIndicator && (_jsxs(Text, { color: colors.textDim, children: ["\u2193 ", lines.length - scrollOffset - actualContentLines, " more lines"] }))] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2191\u2193 scroll \u00B7 space/pgdn page \u00B7 esc back" }) })] }));
|
|
446
324
|
}
|
|
325
|
+
function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
326
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
327
|
+
// Build list of all explorable items
|
|
328
|
+
const items = useMemo(() => {
|
|
329
|
+
const result = [];
|
|
330
|
+
const toolDir = getBundledToolsDir();
|
|
331
|
+
// Add skills
|
|
332
|
+
for (const skill of tool.includes.skills) {
|
|
333
|
+
result.push({
|
|
334
|
+
type: 'skill',
|
|
335
|
+
name: skill.name,
|
|
336
|
+
path: join(toolDir, tool.name, 'skills', skill.name, 'SKILL.md'),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
// Add commands
|
|
340
|
+
for (const cmd of tool.includes.commands) {
|
|
341
|
+
result.push({
|
|
342
|
+
type: 'command',
|
|
343
|
+
name: `/${cmd}`,
|
|
344
|
+
path: join(toolDir, tool.name, 'commands', `${cmd}.md`),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
// Add agents
|
|
348
|
+
for (const agent of tool.includes.agents) {
|
|
349
|
+
result.push({
|
|
350
|
+
type: 'agent',
|
|
351
|
+
name: agent,
|
|
352
|
+
path: join(toolDir, tool.name, 'agents', agent, 'AGENT.md'),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return result;
|
|
356
|
+
}, [tool]);
|
|
357
|
+
useInput((input, key) => {
|
|
358
|
+
if (key.escape) {
|
|
359
|
+
onClose();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (key.leftArrow || key.upArrow) {
|
|
363
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
364
|
+
}
|
|
365
|
+
if (key.rightArrow || key.downArrow) {
|
|
366
|
+
setSelectedIndex((prev) => Math.min(items.length - 1, prev + 1));
|
|
367
|
+
}
|
|
368
|
+
if (key.return && items.length > 0) {
|
|
369
|
+
const item = items[selectedIndex];
|
|
370
|
+
if (existsSync(item.path)) {
|
|
371
|
+
const content = readFileSync(item.path, 'utf-8');
|
|
372
|
+
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
// Try YAML fallback for skills/agents
|
|
376
|
+
const yamlPath = item.path.replace('.md', '.yaml');
|
|
377
|
+
if (existsSync(yamlPath)) {
|
|
378
|
+
const content = readFileSync(yamlPath, 'utf-8');
|
|
379
|
+
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
// Group items by type for display
|
|
385
|
+
const skillItems = items.filter(i => i.type === 'skill');
|
|
386
|
+
const commandItems = items.filter(i => i.type === 'command');
|
|
387
|
+
const agentItems = items.filter(i => i.type === 'agent');
|
|
388
|
+
const getItemIndex = (item) => items.indexOf(item);
|
|
389
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "[" }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: "] " }), _jsx(Text, { color: colors.text, bold: true, children: tool.name })] }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: colors.textMuted, children: tool.description }) }), skillItems.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.skill, bold: true, children: "Skills" }), _jsx(Box, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: skillItems.map((item) => {
|
|
390
|
+
const idx = getItemIndex(item);
|
|
391
|
+
const isSelected = selectedIndex === idx;
|
|
392
|
+
return (_jsx(Box, { marginRight: 1, marginBottom: 1, children: _jsx(Text, { backgroundColor: isSelected ? colors.skill : colors.bgSelected, color: isSelected ? '#000000' : colors.skill, bold: isSelected, children: ` ${item.name} ` }) }, item.name));
|
|
393
|
+
}) })] })), commandItems.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.command, bold: true, children: "Commands" }), _jsx(Box, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: commandItems.map((item) => {
|
|
394
|
+
const idx = getItemIndex(item);
|
|
395
|
+
const isSelected = selectedIndex === idx;
|
|
396
|
+
return (_jsx(Box, { marginRight: 1, marginBottom: 1, children: _jsx(Text, { backgroundColor: isSelected ? colors.command : colors.bgSelected, color: isSelected ? '#000000' : colors.command, bold: isSelected, children: ` ${item.name} ` }) }, item.name));
|
|
397
|
+
}) })] })), agentItems.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: colors.agent, bold: true, children: "Agents" }), _jsx(Box, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: agentItems.map((item) => {
|
|
398
|
+
const idx = getItemIndex(item);
|
|
399
|
+
const isSelected = selectedIndex === idx;
|
|
400
|
+
return (_jsx(Box, { marginRight: 1, marginBottom: 1, children: _jsx(Text, { backgroundColor: isSelected ? colors.agent : colors.bgSelected, color: isSelected ? '#000000' : colors.agent, bold: isSelected, children: ` ${item.name} ` }) }, item.name));
|
|
401
|
+
}) })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 navigate \u00B7 enter view source \u00B7 esc back" }) })] }));
|
|
402
|
+
}
|
|
447
403
|
function SettingsDetails({ onEditSettings, isFocused, }) {
|
|
448
404
|
const config = loadConfig();
|
|
449
405
|
const outputOptions = getOutputOptions();
|
|
450
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsx(Text, { color: colors.text, bold: true, children: "Settings" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "
|
|
406
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [_jsx(Text, { color: colors.text, bold: true, children: "Settings" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "Platform: " }), _jsx(Text, { color: colors.text, children: config.platform === Platform.ClaudeCode ? 'Claude Code' : 'OpenCode' })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "Your @mention: " }), _jsx(Text, { color: colors.text, children: config.user_mention })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "Config: ~/.droid/config.yaml" }) }), isFocused && (_jsx(Box, { marginTop: 2, children: _jsxs(Text, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: [' ', "Edit Settings", ' '] }) })), !isFocused && (_jsx(Box, { marginTop: 2, children: _jsx(Text, { color: colors.textDim, children: "press enter to edit" }) }))] }));
|
|
451
407
|
}
|
|
452
408
|
const MAX_VISIBLE_CONFIG_ITEMS = 4; // Each config item takes ~3 lines
|
|
453
409
|
function SkillConfigScreen({ skill, onComplete, onCancel }) {
|
|
@@ -593,12 +549,10 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
|
|
|
593
549
|
function App() {
|
|
594
550
|
const { exit } = useApp();
|
|
595
551
|
const tabs = [
|
|
596
|
-
{ id: '
|
|
597
|
-
{ id: 'commands', label: 'Commands' },
|
|
598
|
-
{ id: 'agents', label: 'Agents' },
|
|
552
|
+
{ id: 'tools', label: 'Tools' },
|
|
599
553
|
{ id: 'settings', label: 'Settings' },
|
|
600
554
|
];
|
|
601
|
-
const [activeTab, setActiveTab] = useState('
|
|
555
|
+
const [activeTab, setActiveTab] = useState('tools');
|
|
602
556
|
const [tabIndex, setTabIndex] = useState(0);
|
|
603
557
|
const [view, setView] = useState('welcome');
|
|
604
558
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -608,6 +562,7 @@ function App() {
|
|
|
608
562
|
const [isEditingSettings, setIsEditingSettings] = useState(false);
|
|
609
563
|
const [readmeContent, setReadmeContent] = useState(null);
|
|
610
564
|
const [isUpdating, setIsUpdating] = useState(false);
|
|
565
|
+
const [previousView, setPreviousView] = useState('detail'); // Track where to return from readme
|
|
611
566
|
// Check for updates once on mount
|
|
612
567
|
const updateInfo = useMemo(() => getUpdateInfo(), []);
|
|
613
568
|
const handleUpdate = () => {
|
|
@@ -634,9 +589,9 @@ function App() {
|
|
|
634
589
|
}, 100);
|
|
635
590
|
};
|
|
636
591
|
const MAX_VISIBLE_ITEMS = 6;
|
|
592
|
+
const tools = getBundledTools();
|
|
593
|
+
// Keep skills for configure view (tools configure via their primary skill)
|
|
637
594
|
const skills = getBundledSkills();
|
|
638
|
-
const commands = getCommandsFromSkills();
|
|
639
|
-
const agents = getBundledAgents();
|
|
640
595
|
useInput((input, key) => {
|
|
641
596
|
if (message)
|
|
642
597
|
setMessage(null);
|
|
@@ -671,10 +626,7 @@ function App() {
|
|
|
671
626
|
setSelectedAction(0);
|
|
672
627
|
}
|
|
673
628
|
if (key.downArrow) {
|
|
674
|
-
const maxIndex = activeTab === '
|
|
675
|
-
: activeTab === 'commands' ? commands.length - 1
|
|
676
|
-
: activeTab === 'agents' ? agents.length - 1
|
|
677
|
-
: 0;
|
|
629
|
+
const maxIndex = activeTab === 'tools' ? tools.length - 1 : 0;
|
|
678
630
|
setSelectedIndex((prev) => {
|
|
679
631
|
const newIndex = Math.min(maxIndex, prev + 1);
|
|
680
632
|
// Scroll down if needed
|
|
@@ -686,13 +638,7 @@ function App() {
|
|
|
686
638
|
setSelectedAction(0);
|
|
687
639
|
}
|
|
688
640
|
if (key.return) {
|
|
689
|
-
if (activeTab === '
|
|
690
|
-
setView('detail');
|
|
691
|
-
}
|
|
692
|
-
else if (activeTab === 'commands' && commands.length > 0) {
|
|
693
|
-
setView('detail');
|
|
694
|
-
}
|
|
695
|
-
else if (activeTab === 'agents' && agents.length > 0) {
|
|
641
|
+
if (activeTab === 'tools' && tools.length > 0) {
|
|
696
642
|
setView('detail');
|
|
697
643
|
}
|
|
698
644
|
else if (activeTab === 'settings') {
|
|
@@ -711,51 +657,40 @@ function App() {
|
|
|
711
657
|
}
|
|
712
658
|
if (key.rightArrow) {
|
|
713
659
|
let maxActions = 0;
|
|
714
|
-
if (activeTab === '
|
|
715
|
-
const
|
|
716
|
-
const installed =
|
|
717
|
-
const hasUpdate =
|
|
718
|
-
//
|
|
660
|
+
if (activeTab === 'tools') {
|
|
661
|
+
const tool = tools[selectedIndex];
|
|
662
|
+
const installed = tool ? isToolInstalled(tool.name) : false;
|
|
663
|
+
const hasUpdate = tool ? getToolUpdateStatus(tool.name).hasUpdate : false;
|
|
664
|
+
// Explore, [Update], Configure, Uninstall or Explore, Install
|
|
719
665
|
maxActions = installed ? (hasUpdate ? 3 : 2) : 1;
|
|
720
666
|
}
|
|
721
|
-
else if (activeTab === 'agents') {
|
|
722
|
-
maxActions = 1; // View, Install/Uninstall
|
|
723
|
-
}
|
|
724
|
-
else if (activeTab === 'commands') {
|
|
725
|
-
const command = commands[selectedIndex];
|
|
726
|
-
// If parent skill is installed, only View is available
|
|
727
|
-
const skillInstalled = command ? isSkillInstalled(command.skillName) : false;
|
|
728
|
-
maxActions = skillInstalled ? 0 : 1;
|
|
729
|
-
}
|
|
730
667
|
setSelectedAction((prev) => Math.min(maxActions, prev + 1));
|
|
731
668
|
}
|
|
732
|
-
if (key.return && activeTab === '
|
|
733
|
-
const
|
|
734
|
-
if (
|
|
735
|
-
const installed =
|
|
736
|
-
const
|
|
737
|
-
// Build actions array to match
|
|
738
|
-
const
|
|
669
|
+
if (key.return && activeTab === 'tools') {
|
|
670
|
+
const tool = tools[selectedIndex];
|
|
671
|
+
if (tool) {
|
|
672
|
+
const installed = isToolInstalled(tool.name);
|
|
673
|
+
const toolUpdateStatus = getToolUpdateStatus(tool.name);
|
|
674
|
+
// Build actions array to match ToolDetails
|
|
675
|
+
const toolActions = installed
|
|
739
676
|
? [
|
|
740
|
-
{ id: '
|
|
741
|
-
...(
|
|
677
|
+
{ id: 'explore' },
|
|
678
|
+
...(toolUpdateStatus.hasUpdate ? [{ id: 'update' }] : []),
|
|
742
679
|
{ id: 'configure' },
|
|
743
680
|
{ id: 'uninstall' },
|
|
744
681
|
]
|
|
745
|
-
: [{ id: '
|
|
746
|
-
const actionId =
|
|
747
|
-
if (actionId === '
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const content = readFileSync(skillMdPath, 'utf-8');
|
|
751
|
-
setReadmeContent({ title: `${skill.name}/SKILL.md`, content });
|
|
752
|
-
setView('readme');
|
|
753
|
-
}
|
|
682
|
+
: [{ id: 'explore' }, { id: 'install' }];
|
|
683
|
+
const actionId = toolActions[selectedAction]?.id;
|
|
684
|
+
if (actionId === 'explore') {
|
|
685
|
+
// Open the tool explorer view
|
|
686
|
+
setView('explorer');
|
|
754
687
|
}
|
|
755
688
|
else if (actionId === 'update') {
|
|
756
|
-
|
|
689
|
+
// Update the primary skill of the tool
|
|
690
|
+
const primarySkill = tool.includes.skills.find(s => s.required)?.name || tool.name;
|
|
691
|
+
const result = updateSkill(primarySkill);
|
|
757
692
|
setMessage({
|
|
758
|
-
text: result.success ? `✓ ${
|
|
693
|
+
text: result.success ? `✓ Updated ${tool.name}` : `✗ ${result.message}`,
|
|
759
694
|
type: result.success ? 'success' : 'error',
|
|
760
695
|
});
|
|
761
696
|
if (result.success) {
|
|
@@ -766,9 +701,11 @@ function App() {
|
|
|
766
701
|
setView('configure');
|
|
767
702
|
}
|
|
768
703
|
else if (actionId === 'uninstall') {
|
|
769
|
-
|
|
704
|
+
// Uninstall the primary skill of the tool
|
|
705
|
+
const primarySkill = tool.includes.skills.find(s => s.required)?.name || tool.name;
|
|
706
|
+
const result = uninstallSkill(primarySkill);
|
|
770
707
|
setMessage({
|
|
771
|
-
text: result.success ? `✓ Uninstalled ${
|
|
708
|
+
text: result.success ? `✓ Uninstalled ${tool.name}` : `✗ ${result.message}`,
|
|
772
709
|
type: result.success ? 'success' : 'error',
|
|
773
710
|
});
|
|
774
711
|
if (result.success) {
|
|
@@ -777,14 +714,16 @@ function App() {
|
|
|
777
714
|
}
|
|
778
715
|
}
|
|
779
716
|
else if (actionId === 'install') {
|
|
780
|
-
|
|
717
|
+
// Install the primary skill of the tool
|
|
718
|
+
const primarySkill = tool.includes.skills.find(s => s.required)?.name || tool.name;
|
|
719
|
+
const result = installSkill(primarySkill);
|
|
781
720
|
setMessage({
|
|
782
|
-
text: result.success ? `✓ Installed ${
|
|
721
|
+
text: result.success ? `✓ Installed ${tool.name}` : `✗ ${result.message}`,
|
|
783
722
|
type: result.success ? 'success' : 'error',
|
|
784
723
|
});
|
|
785
724
|
if (result.success) {
|
|
786
|
-
// Check if
|
|
787
|
-
const configSchema =
|
|
725
|
+
// Check if tool has required config (options without defaults)
|
|
726
|
+
const configSchema = tool.config_schema || {};
|
|
788
727
|
const hasRequiredConfig = Object.values(configSchema).some((option) => option.default === undefined);
|
|
789
728
|
if (hasRequiredConfig) {
|
|
790
729
|
// Go to configure view for required setup
|
|
@@ -798,67 +737,13 @@ function App() {
|
|
|
798
737
|
}
|
|
799
738
|
}
|
|
800
739
|
}
|
|
801
|
-
if (key.return && activeTab === 'agents') {
|
|
802
|
-
const agent = agents[selectedIndex];
|
|
803
|
-
if (agent) {
|
|
804
|
-
const installed = isAgentInstalled(agent.name);
|
|
805
|
-
const bundledSkill = getAgentBundledSkill(agent.name);
|
|
806
|
-
if (selectedAction === 0) {
|
|
807
|
-
// View
|
|
808
|
-
const agentMdPath = findAgentSourcePath(agent.name);
|
|
809
|
-
if (agentMdPath) {
|
|
810
|
-
const content = readFileSync(agentMdPath, 'utf-8');
|
|
811
|
-
setReadmeContent({ title: `${agent.name}/AGENT.md`, content });
|
|
812
|
-
setView('readme');
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
else if (!bundledSkill && installed && selectedAction === 1) {
|
|
816
|
-
// Uninstall (only for standalone agents)
|
|
817
|
-
const result = uninstallAgent(agent.name);
|
|
818
|
-
setMessage({
|
|
819
|
-
text: result.success ? `✓ Uninstalled ${agent.name}` : `✗ ${result.message}`,
|
|
820
|
-
type: result.success ? 'success' : 'error',
|
|
821
|
-
});
|
|
822
|
-
if (result.success) {
|
|
823
|
-
setView('menu');
|
|
824
|
-
setSelectedAction(0);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
else if (!bundledSkill && !installed && selectedAction === 1) {
|
|
828
|
-
// Install (only for standalone agents)
|
|
829
|
-
const result = installAgent(agent.name);
|
|
830
|
-
setMessage({
|
|
831
|
-
text: result.success ? `✓ ${result.message}` : `✗ ${result.message}`,
|
|
832
|
-
type: result.success ? 'success' : 'error',
|
|
833
|
-
});
|
|
834
|
-
if (result.success) {
|
|
835
|
-
setView('menu');
|
|
836
|
-
setSelectedAction(0);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
if (key.return && activeTab === 'commands') {
|
|
842
|
-
const command = commands[selectedIndex];
|
|
843
|
-
if (command) {
|
|
844
|
-
// Command file: extract part after skill name (e.g., "comments check" → "check.md")
|
|
845
|
-
const cmdPart = command.name.startsWith(command.skillName + ' ')
|
|
846
|
-
? command.name.slice(command.skillName.length + 1)
|
|
847
|
-
: command.name;
|
|
848
|
-
const commandMdPath = join(getBundledSkillsDir(), command.skillName, 'commands', `${cmdPart}.md`);
|
|
849
|
-
// Only View action available - commands install via skills
|
|
850
|
-
if (selectedAction === 0 && existsSync(commandMdPath)) {
|
|
851
|
-
const content = readFileSync(commandMdPath, 'utf-8');
|
|
852
|
-
setReadmeContent({ title: `/${command.name}`, content });
|
|
853
|
-
setView('readme');
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
740
|
}
|
|
858
|
-
}, { isActive: view !== 'welcome' && view !== 'setup' && view !== 'configure' });
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
const
|
|
741
|
+
}, { isActive: view !== 'welcome' && view !== 'setup' && view !== 'configure' && view !== 'explorer' });
|
|
742
|
+
const selectedTool = activeTab === 'tools' ? tools[selectedIndex] ?? null : null;
|
|
743
|
+
// For configure view, we need the matching skill manifest
|
|
744
|
+
const selectedSkillForConfig = selectedTool
|
|
745
|
+
? skills.find(s => s.name === (selectedTool.includes.skills.find(sk => sk.required)?.name || selectedTool.name))
|
|
746
|
+
: null;
|
|
862
747
|
if (view === 'welcome') {
|
|
863
748
|
return (_jsx(WelcomeScreen, { updateInfo: updateInfo, isUpdating: isUpdating, onUpdate: handleUpdate, onExit: exit, onContinue: () => {
|
|
864
749
|
// If no config exists, show setup first
|
|
@@ -882,18 +767,27 @@ function App() {
|
|
|
882
767
|
if (view === 'readme' && readmeContent) {
|
|
883
768
|
return (_jsx(ReadmeViewer, { title: readmeContent.title, content: readmeContent.content, onClose: () => {
|
|
884
769
|
setReadmeContent(null);
|
|
770
|
+
setView(previousView);
|
|
771
|
+
} }));
|
|
772
|
+
}
|
|
773
|
+
if (view === 'explorer' && selectedTool) {
|
|
774
|
+
return (_jsx(ToolExplorer, { tool: selectedTool, onViewSource: (title, content) => {
|
|
775
|
+
setPreviousView('explorer');
|
|
776
|
+
setReadmeContent({ title, content });
|
|
777
|
+
setView('readme');
|
|
778
|
+
}, onClose: () => {
|
|
885
779
|
setView('detail');
|
|
886
780
|
} }));
|
|
887
781
|
}
|
|
888
|
-
if (view === 'configure' &&
|
|
889
|
-
return (_jsx(SkillConfigScreen, { skill:
|
|
890
|
-
setMessage({ text: `✓ Configuration saved for ${
|
|
782
|
+
if (view === 'configure' && selectedSkillForConfig) {
|
|
783
|
+
return (_jsx(SkillConfigScreen, { skill: selectedSkillForConfig, onComplete: () => {
|
|
784
|
+
setMessage({ text: `✓ Configuration saved for ${selectedTool?.name || selectedSkillForConfig.name}`, type: 'success' });
|
|
891
785
|
setView('detail');
|
|
892
786
|
}, onCancel: () => {
|
|
893
787
|
setView('detail');
|
|
894
788
|
} }));
|
|
895
789
|
}
|
|
896
|
-
return (_jsxs(Box, { flexDirection: "row", padding: 1, children: [_jsxs(Box, { flexDirection: "column", width: 44, borderStyle: "single", borderColor: colors.border, children: [_jsx(Box, { paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "[" }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: "] " }), _jsx(Text, { color: colors.textMuted, children: "droid" }), _jsxs(Text, { color: colors.textDim, children: [" v", getVersion()] })] }) }), _jsx(Box, { paddingX: 1,
|
|
790
|
+
return (_jsxs(Box, { flexDirection: "row", padding: 1, children: [_jsxs(Box, { flexDirection: "column", width: 44, borderStyle: "single", borderColor: colors.border, children: [_jsx(Box, { paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "[" }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: " " }), _jsx(Text, { color: colors.primary, children: "\u25CF" }), _jsx(Text, { color: colors.textDim, children: "] " }), _jsx(Text, { color: colors.textMuted, children: "droid" }), _jsxs(Text, { color: colors.textDim, children: [" v", getVersion()] })] }) }), _jsx(Box, { paddingX: 1, children: _jsx(Text, { color: colors.textDim, children: loadConfig().platform === Platform.ClaudeCode ? 'Claude Code' : 'OpenCode' }) }), _jsx(Box, { paddingX: 1, marginTop: 1, children: _jsx(TabBar, { tabs: tabs, activeTab: activeTab }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [activeTab === 'tools' && (_jsxs(_Fragment, { children: [scrollOffset > 0 && (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.textDim, children: ["\u2191 ", scrollOffset, " more"] }) })), tools.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS).map((tool, index) => (_jsx(ToolItem, { tool: tool, isSelected: scrollOffset + index === selectedIndex, isActive: scrollOffset + index === selectedIndex && view === 'detail' }, tool.name))), scrollOffset + MAX_VISIBLE_ITEMS < tools.length && (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.textDim, children: ["\u2193 ", tools.length - scrollOffset - MAX_VISIBLE_ITEMS, " more"] }) })), tools.length > MAX_VISIBLE_ITEMS && (_jsx(Box, { paddingX: 1, marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: [tools.length, " tools total"] }) }))] })), activeTab === 'settings' && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: colors.textDim, children: "View and edit config" }) }))] }), message && (_jsx(Box, { paddingX: 1, marginTop: 1, children: _jsx(Text, { color: message.type === 'success' ? colors.success : colors.error, children: message.text }) })), _jsx(Box, { paddingX: 1, marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: view === 'menu' ? '←→ ↑↓ enter q' : '←→ enter esc q' }) })] }), activeTab === 'tools' && (_jsx(ToolDetails, { tool: selectedTool, isFocused: view === 'detail', selectedAction: selectedAction })), activeTab === 'settings' && (_jsx(SettingsDetails, { onEditSettings: () => setView('setup'), isFocused: false }))] }));
|
|
897
791
|
}
|
|
898
792
|
export async function tuiCommand() {
|
|
899
793
|
// Enter alternate screen (fullscreen mode)
|