@orderful/droid 0.14.0 → 0.16.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 +31 -9
- package/CHANGELOG.md +35 -0
- package/dist/bin/droid.js +8 -8
- package/dist/bin/droid.js.map +1 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/install.js +3 -3
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +3 -3
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills.js +4 -4
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/tui/components/Badge.d.ts +13 -0
- package/dist/commands/tui/components/Badge.d.ts.map +1 -0
- package/dist/commands/tui/components/Badge.js +29 -0
- package/dist/commands/tui/components/Badge.js.map +1 -0
- package/dist/commands/tui/components/Markdown.d.ts +5 -0
- package/dist/commands/tui/components/Markdown.d.ts.map +1 -0
- package/dist/commands/tui/components/Markdown.js +42 -0
- package/dist/commands/tui/components/Markdown.js.map +1 -0
- package/dist/commands/tui/components/SettingsDetails.d.ts +5 -0
- package/dist/commands/tui/components/SettingsDetails.d.ts.map +1 -0
- package/dist/commands/tui/components/SettingsDetails.js +11 -0
- package/dist/commands/tui/components/SettingsDetails.js.map +1 -0
- package/dist/commands/tui/components/TabBar.d.ts +10 -0
- package/dist/commands/tui/components/TabBar.d.ts.map +1 -0
- package/dist/commands/tui/components/TabBar.js +7 -0
- package/dist/commands/tui/components/TabBar.js.map +1 -0
- package/dist/commands/tui/components/ToolDetails.d.ts +8 -0
- package/dist/commands/tui/components/ToolDetails.d.ts.map +1 -0
- package/dist/commands/tui/components/ToolDetails.js +35 -0
- package/dist/commands/tui/components/ToolDetails.js.map +1 -0
- package/dist/commands/tui/components/ToolItem.d.ts +9 -0
- package/dist/commands/tui/components/ToolItem.d.ts.map +1 -0
- package/dist/commands/tui/components/ToolItem.js +11 -0
- package/dist/commands/tui/components/ToolItem.js.map +1 -0
- package/dist/commands/tui/constants.d.ts +16 -0
- package/dist/commands/tui/constants.d.ts.map +1 -0
- package/dist/commands/tui/constants.js +17 -0
- package/dist/commands/tui/constants.js.map +1 -0
- package/dist/commands/tui/hooks/useAppUpdate.d.ts +13 -0
- package/dist/commands/tui/hooks/useAppUpdate.d.ts.map +1 -0
- package/dist/commands/tui/hooks/useAppUpdate.js +52 -0
- package/dist/commands/tui/hooks/useAppUpdate.js.map +1 -0
- package/dist/commands/tui/hooks/useToolUpdates.d.ts +22 -0
- package/dist/commands/tui/hooks/useToolUpdates.d.ts.map +1 -0
- package/dist/commands/tui/hooks/useToolUpdates.js +77 -0
- package/dist/commands/tui/hooks/useToolUpdates.js.map +1 -0
- package/dist/commands/tui/types.d.ts +5 -0
- package/dist/commands/tui/types.d.ts.map +1 -0
- package/dist/commands/tui/types.js +2 -0
- package/dist/commands/tui/types.js.map +1 -0
- package/dist/commands/tui/views/ReadmeViewer.d.ts +7 -0
- package/dist/commands/tui/views/ReadmeViewer.d.ts.map +1 -0
- package/dist/commands/tui/views/ReadmeViewer.js +56 -0
- package/dist/commands/tui/views/ReadmeViewer.js.map +1 -0
- package/dist/commands/tui/views/SetupScreen.d.ts +8 -0
- package/dist/commands/tui/views/SetupScreen.d.ts.map +1 -0
- package/dist/commands/tui/views/SetupScreen.js +114 -0
- package/dist/commands/tui/views/SetupScreen.js.map +1 -0
- package/dist/commands/tui/views/SkillConfigScreen.d.ts +8 -0
- package/dist/commands/tui/views/SkillConfigScreen.d.ts.map +1 -0
- package/dist/commands/tui/views/SkillConfigScreen.js +148 -0
- package/dist/commands/tui/views/SkillConfigScreen.js.map +1 -0
- package/dist/commands/tui/views/ToolExplorer.d.ts +8 -0
- package/dist/commands/tui/views/ToolExplorer.d.ts.map +1 -0
- package/dist/commands/tui/views/ToolExplorer.js +86 -0
- package/dist/commands/tui/views/ToolExplorer.js.map +1 -0
- package/dist/commands/tui/views/ToolUpdatePrompt.d.ts +10 -0
- package/dist/commands/tui/views/ToolUpdatePrompt.d.ts.map +1 -0
- package/dist/commands/tui/views/ToolUpdatePrompt.js +38 -0
- package/dist/commands/tui/views/ToolUpdatePrompt.js.map +1 -0
- package/dist/commands/tui/views/WelcomeScreen.d.ts +11 -0
- package/dist/commands/tui/views/WelcomeScreen.d.ts.map +1 -0
- package/dist/commands/tui/views/WelcomeScreen.js +46 -0
- package/dist/commands/tui/views/WelcomeScreen.js.map +1 -0
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +54 -756
- package/dist/commands/tui.js.map +1 -1
- package/dist/commands/uninstall.js +2 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/agents.d.ts +7 -7
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +63 -41
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/platforms.d.ts +1 -1
- package/dist/lib/platforms.d.ts.map +1 -1
- package/dist/lib/platforms.js +1 -1
- package/dist/lib/platforms.js.map +1 -1
- package/dist/lib/skill-config.d.ts +1 -1
- package/dist/lib/skill-config.d.ts.map +1 -1
- package/dist/lib/skill-config.js +2 -2
- package/dist/lib/skill-config.js.map +1 -1
- package/dist/lib/skills.d.ts +2 -1
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +45 -12
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/tools.d.ts +1 -1
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/lib/tools.js +3 -3
- package/dist/lib/tools.js.map +1 -1
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/README.md +79 -50
- package/dist/tools/brain/TOOL.yaml +1 -1
- package/dist/tools/brain/skills/brain/SKILL.md +1 -0
- package/dist/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
- package/dist/tools/coach/TOOL.yaml +1 -1
- package/dist/tools/coach/skills/coach/SKILL.md +1 -0
- package/{src/tools/code-review/agents/edi-standards-reviewer/AGENT.md → dist/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
- package/dist/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
- package/{src/tools/code-review/agents/test-coverage-analyzer/AGENT.md → dist/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
- package/dist/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
- package/dist/tools/code-review/skills/code-review/SKILL.md +1 -0
- package/dist/tools/comments/TOOL.yaml +2 -2
- package/dist/tools/comments/skills/comments/SKILL.md +1 -0
- package/dist/tools/droid/skills/droid/SKILL.md +1 -0
- package/dist/tools/project/skills/project/SKILL.md +1 -0
- package/package.json +3 -3
- package/src/bin/droid.ts +8 -8
- package/src/commands/config.ts +1 -1
- package/src/commands/install.ts +3 -3
- package/src/commands/setup.test.ts +1 -1
- package/src/commands/setup.ts +3 -3
- package/src/commands/skills.ts +4 -4
- package/src/commands/tui/components/Badge.tsx +86 -0
- package/src/commands/tui/components/Markdown.tsx +48 -0
- package/src/commands/tui/components/SettingsDetails.tsx +70 -0
- package/src/commands/tui/components/TabBar.tsx +26 -0
- package/src/commands/tui/components/ToolDetails.tsx +117 -0
- package/src/commands/tui/components/ToolItem.tsx +39 -0
- package/src/commands/tui/constants.ts +17 -0
- package/src/commands/tui/hooks/useAppUpdate.ts +67 -0
- package/src/commands/tui/hooks/useToolUpdates.ts +110 -0
- package/src/commands/tui/types.ts +4 -0
- package/src/commands/tui/views/ReadmeViewer.tsx +93 -0
- package/src/commands/tui/views/SetupScreen.tsx +242 -0
- package/src/commands/tui/views/SkillConfigScreen.tsx +278 -0
- package/src/commands/tui/views/ToolExplorer.tsx +190 -0
- package/src/commands/tui/views/ToolUpdatePrompt.tsx +109 -0
- package/src/commands/tui/views/WelcomeScreen.tsx +149 -0
- package/src/commands/tui.tsx +65 -1588
- package/src/commands/uninstall.ts +2 -2
- package/src/commands/update.ts +1 -1
- package/src/index.ts +4 -4
- package/src/lib/agents.ts +68 -45
- package/src/lib/config.ts +1 -1
- package/src/lib/platforms.ts +1 -1
- package/src/lib/skill-config.ts +2 -2
- package/src/lib/skills.test.ts +28 -33
- package/src/lib/skills.ts +49 -12
- package/src/lib/tools.ts +3 -3
- package/src/lib/types.test.ts +1 -1
- package/src/lib/types.ts +5 -0
- package/src/lib/version.test.ts +1 -1
- package/src/tools/README.md +79 -50
- package/src/tools/brain/TOOL.yaml +1 -1
- package/src/tools/brain/skills/brain/SKILL.md +1 -0
- package/src/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
- package/src/tools/coach/TOOL.yaml +1 -1
- package/src/tools/coach/skills/coach/SKILL.md +1 -0
- package/{dist/tools/code-review/agents/edi-standards-reviewer/AGENT.md → src/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
- package/src/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
- package/{dist/tools/code-review/agents/test-coverage-analyzer/AGENT.md → src/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
- package/src/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
- package/src/tools/code-review/skills/code-review/SKILL.md +1 -0
- package/src/tools/comments/TOOL.yaml +2 -2
- package/src/tools/comments/skills/comments/SKILL.md +1 -0
- package/src/tools/droid/skills/droid/SKILL.md +1 -0
- package/src/tools/project/skills/project/SKILL.md +1 -0
- package/dist/tools/brain/skills/brain/SKILL.yaml +0 -29
- package/dist/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
- package/dist/tools/coach/skills/coach/SKILL.yaml +0 -25
- package/dist/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
- package/dist/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
- package/dist/tools/code-review/skills/code-review/SKILL.yaml +0 -19
- package/dist/tools/comments/skills/comments/SKILL.yaml +0 -50
- package/dist/tools/droid/skills/droid/SKILL.yaml +0 -7
- package/dist/tools/project/skills/project/SKILL.yaml +0 -30
- package/src/tools/brain/skills/brain/SKILL.yaml +0 -29
- package/src/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
- package/src/tools/coach/skills/coach/SKILL.yaml +0 -25
- package/src/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
- package/src/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
- package/src/tools/code-review/skills/code-review/SKILL.yaml +0 -19
- package/src/tools/comments/skills/comments/SKILL.yaml +0 -50
- package/src/tools/droid/skills/droid/SKILL.yaml +0 -7
- package/src/tools/project/skills/project/SKILL.yaml +0 -30
package/dist/commands/tui.js
CHANGED
|
@@ -1,595 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { render, Box, Text, useInput, useApp } from 'ink';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { getBundledSkills, installSkill, uninstallSkill, updateSkill, } from '../lib/skills';
|
|
5
|
+
import { getBundledTools, isToolInstalled, getToolUpdateStatus } from '../lib/tools';
|
|
6
|
+
import { configExists, loadConfig } from '../lib/config';
|
|
7
|
+
import { Platform } from '../lib/types';
|
|
8
|
+
import { getVersion } from '../lib/version';
|
|
9
|
+
import { colors, MAX_VISIBLE_ITEMS } from './tui/constants';
|
|
10
|
+
import { TabBar } from './tui/components/TabBar';
|
|
11
|
+
import { ToolItem } from './tui/components/ToolItem';
|
|
12
|
+
import { ToolDetails } from './tui/components/ToolDetails';
|
|
13
|
+
import { SettingsDetails } from './tui/components/SettingsDetails';
|
|
14
|
+
import { WelcomeScreen } from './tui/views/WelcomeScreen';
|
|
15
|
+
import { ToolUpdatePrompt } from './tui/views/ToolUpdatePrompt';
|
|
16
|
+
import { SetupScreen } from './tui/views/SetupScreen';
|
|
17
|
+
import { ReadmeViewer } from './tui/views/ReadmeViewer';
|
|
18
|
+
import { ToolExplorer } from './tui/views/ToolExplorer';
|
|
19
|
+
import { SkillConfigScreen } from './tui/views/SkillConfigScreen';
|
|
20
|
+
import { useAppUpdate } from './tui/hooks/useAppUpdate';
|
|
21
|
+
import { useToolUpdates } from './tui/hooks/useToolUpdates';
|
|
15
22
|
// Module-level variable to store exit message (printed after leaving alternate screen)
|
|
16
23
|
let exitMessage = null;
|
|
17
|
-
const colors = {
|
|
18
|
-
primary: '#6366f1',
|
|
19
|
-
bgSelected: '#2d2d2d',
|
|
20
|
-
border: '#3a3a3a',
|
|
21
|
-
text: '#e8e8e8',
|
|
22
|
-
textMuted: '#999999',
|
|
23
|
-
textDim: '#6a6a6a',
|
|
24
|
-
success: '#4ade80',
|
|
25
|
-
error: '#f87171',
|
|
26
|
-
// Component type badges
|
|
27
|
-
skill: '#ec4899', // pink/magenta
|
|
28
|
-
command: '#22d3ee', // cyan
|
|
29
|
-
agent: '#fbbf24', // yellow/amber
|
|
30
|
-
};
|
|
31
|
-
function Badge({ type, label, isSelected = false, dimmed = false, }) {
|
|
32
|
-
const color = colors[type];
|
|
33
|
-
const displayLabel = label || type.charAt(0).toUpperCase() + type.slice(1);
|
|
34
|
-
if (dimmed) {
|
|
35
|
-
return (_jsx(Text, { color: colors.textDim, children: displayLabel }));
|
|
36
|
-
}
|
|
37
|
-
return (_jsx(Text, { backgroundColor: isSelected ? color : undefined, color: isSelected ? '#000000' : color, bold: isSelected, children: isSelected ? ` ${displayLabel} ` : displayLabel }));
|
|
38
|
-
}
|
|
39
|
-
function ComponentBadges({ tool, compact = false }) {
|
|
40
|
-
const hasSkills = tool.includes.skills.length > 0;
|
|
41
|
-
const hasCommands = tool.includes.commands.length > 0;
|
|
42
|
-
const hasAgents = tool.includes.agents.length > 0;
|
|
43
|
-
if (compact) {
|
|
44
|
-
// Show colored squares for list view (single char with spacing)
|
|
45
|
-
const parts = [];
|
|
46
|
-
if (hasSkills)
|
|
47
|
-
parts.push('skill');
|
|
48
|
-
if (hasCommands)
|
|
49
|
-
parts.push('command');
|
|
50
|
-
if (hasAgents)
|
|
51
|
-
parts.push('agent');
|
|
52
|
-
return (_jsx(Box, { flexDirection: "row", children: parts.map((type, i) => (_jsxs(Text, { children: [_jsx(Text, { backgroundColor: colors[type], children: " " }), i < parts.length - 1 && ' '] }, type))) }));
|
|
53
|
-
}
|
|
54
|
-
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' : ''} ` }) }))] }));
|
|
55
|
-
}
|
|
56
|
-
function detectInstalledPlatforms() {
|
|
57
|
-
const installed = new Set();
|
|
58
|
-
try {
|
|
59
|
-
execSync('claude --version', { stdio: 'ignore' });
|
|
60
|
-
installed.add(Platform.ClaudeCode);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
// Claude Code not found
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
execSync('opencode --version', { stdio: 'ignore' });
|
|
67
|
-
installed.add(Platform.OpenCode);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
// OpenCode not found
|
|
71
|
-
}
|
|
72
|
-
return installed;
|
|
73
|
-
}
|
|
74
|
-
function getDefaultPlatform() {
|
|
75
|
-
const installed = detectInstalledPlatforms();
|
|
76
|
-
if (installed.has(Platform.ClaudeCode))
|
|
77
|
-
return Platform.ClaudeCode;
|
|
78
|
-
if (installed.has(Platform.OpenCode))
|
|
79
|
-
return Platform.OpenCode;
|
|
80
|
-
return Platform.ClaudeCode;
|
|
81
|
-
}
|
|
82
|
-
function getOutputOptions() {
|
|
83
|
-
const options = [
|
|
84
|
-
{ label: 'Terminal (display in CLI)', value: BuiltInOutput.Terminal },
|
|
85
|
-
{ label: 'Editor ($EDITOR)', value: BuiltInOutput.Editor },
|
|
86
|
-
];
|
|
87
|
-
const skills = getBundledSkills();
|
|
88
|
-
for (const skill of skills) {
|
|
89
|
-
if (skill.provides_output) {
|
|
90
|
-
options.push({ label: `${skill.name} (${skill.description})`, value: skill.name });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return options;
|
|
94
|
-
}
|
|
95
|
-
function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isUpdating }) {
|
|
96
|
-
const [selectedButton, setSelectedButton] = useState(0);
|
|
97
|
-
const welcomeQuote = useMemo(() => getRandomQuote(), []);
|
|
98
|
-
useInput((input, key) => {
|
|
99
|
-
if (isUpdating)
|
|
100
|
-
return;
|
|
101
|
-
if (updateInfo.hasUpdate) {
|
|
102
|
-
if (key.leftArrow) {
|
|
103
|
-
setSelectedButton((prev) => Math.max(0, prev - 1));
|
|
104
|
-
}
|
|
105
|
-
if (key.rightArrow) {
|
|
106
|
-
setSelectedButton((prev) => Math.min(2, prev + 1));
|
|
107
|
-
}
|
|
108
|
-
if (key.return) {
|
|
109
|
-
if (selectedButton === 0) {
|
|
110
|
-
onUpdate();
|
|
111
|
-
}
|
|
112
|
-
else if (selectedButton === 1) {
|
|
113
|
-
onAlways();
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
onContinue();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (input === 'q') {
|
|
120
|
-
onExit();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
if (key.return) {
|
|
125
|
-
onContinue();
|
|
126
|
-
}
|
|
127
|
-
if (input === 'q') {
|
|
128
|
-
onExit();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
const hasUpdate = updateInfo.hasUpdate && updateInfo.latestVersion;
|
|
133
|
-
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 ? '#eab308' : colors.bgSelected, color: selectedButton === 1 ? '#000000' : colors.textMuted, bold: selectedButton === 1, children: [' ', "Always", ' '] }), _jsx(Text, { children: " " }), _jsxs(Text, { backgroundColor: selectedButton === 2 ? colors.bgSelected : undefined, color: selectedButton === 2 ? colors.text : colors.textMuted, bold: selectedButton === 2, children: [' ', "Skip", ' '] })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 select \u00B7 enter \u00B7 \"Always\" enables auto-update" }) })] })) : (_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" })] }))] }) }));
|
|
134
|
-
}
|
|
135
|
-
function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdating }) {
|
|
136
|
-
const [selectedButton, setSelectedButton] = useState(0);
|
|
137
|
-
useInput((input, key) => {
|
|
138
|
-
if (isUpdating)
|
|
139
|
-
return;
|
|
140
|
-
if (key.leftArrow) {
|
|
141
|
-
setSelectedButton((prev) => Math.max(0, prev - 1));
|
|
142
|
-
}
|
|
143
|
-
if (key.rightArrow) {
|
|
144
|
-
setSelectedButton((prev) => Math.min(2, prev + 1));
|
|
145
|
-
}
|
|
146
|
-
if (key.return) {
|
|
147
|
-
if (selectedButton === 0) {
|
|
148
|
-
onUpdateAll();
|
|
149
|
-
}
|
|
150
|
-
else if (selectedButton === 1) {
|
|
151
|
-
onAlways();
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
onSkip();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (input === 'q') {
|
|
158
|
-
onSkip();
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
const buttons = [
|
|
162
|
-
{ label: 'Update All', action: onUpdateAll },
|
|
163
|
-
{ label: 'Always', action: onAlways },
|
|
164
|
-
{ label: 'Skip', action: onSkip },
|
|
165
|
-
];
|
|
166
|
-
return (_jsx(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: _jsxs(Box, { flexDirection: "column", alignItems: "center", borderStyle: "single", borderColor: colors.primary, 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: "Tool Updates" })] }), _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: "\u2551 " }), _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: " \u2551 " }), _jsxs(Text, { color: colors.textMuted, children: [toolUpdates.length, " tool", toolUpdates.length > 1 ? 's have' : ' has', " updates available"] })] }), _jsx(Text, { children: _jsx(Text, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D" }) })] }), _jsxs(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [toolUpdates.slice(0, 5).map((tool) => (_jsxs(Text, { color: colors.textMuted, children: [_jsx(Text, { color: colors.primary, children: "\u2191" }), " ", tool.name, _jsxs(Text, { color: colors.textDim, children: [" ", tool.installedVersion, " \u2192 ", tool.bundledVersion] })] }, tool.name))), toolUpdates.length > 5 && (_jsxs(Text, { color: colors.textDim, children: ["...and ", toolUpdates.length - 5, " more"] }))] }), isUpdating ? (_jsx(Text, { color: colors.primary, children: "Updating tools..." })) : (_jsx(Box, { flexDirection: "row", children: buttons.map((button, index) => (_jsxs(Text, { backgroundColor: selectedButton === index ? colors.primary : colors.bgSelected, color: selectedButton === index ? '#000000' : colors.textMuted, bold: selectedButton === index, children: [' ', button.label, ' '] }, button.label))) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 select \u00B7 enter \u00B7 \"Always\" enables auto-update" }) })] }) }));
|
|
167
|
-
}
|
|
168
|
-
function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
169
|
-
const [step, setStep] = useState('platform');
|
|
170
|
-
const [platform, setPlatform] = useState(initialConfig?.platform || getDefaultPlatform());
|
|
171
|
-
const [userMention, setUserMention] = useState(initialConfig?.user_mention || '@user');
|
|
172
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
173
|
-
const [error, setError] = useState(null);
|
|
174
|
-
// Cache detection results once on mount (avoids re-running execSync on every render)
|
|
175
|
-
const installedPlatforms = useMemo(() => detectInstalledPlatforms(), []);
|
|
176
|
-
const platformOptions = useMemo(() => [
|
|
177
|
-
{ label: 'Claude Code', value: Platform.ClaudeCode },
|
|
178
|
-
{ label: 'OpenCode', value: Platform.OpenCode },
|
|
179
|
-
], []);
|
|
180
|
-
const steps = ['platform', 'user_mention', 'confirm'];
|
|
181
|
-
const stepIndex = steps.indexOf(step);
|
|
182
|
-
const totalSteps = steps.length - 1; // Don't count confirm as a step
|
|
183
|
-
const handleUserMentionSubmit = () => {
|
|
184
|
-
// Auto-add @ prefix if missing
|
|
185
|
-
const mention = userMention.startsWith('@') ? userMention : `@${userMention}`;
|
|
186
|
-
setUserMention(mention);
|
|
187
|
-
setError(null);
|
|
188
|
-
setStep('confirm');
|
|
189
|
-
};
|
|
190
|
-
// Handle escape during text input (only intercept escape, nothing else)
|
|
191
|
-
useInput((input, key) => {
|
|
192
|
-
if (key.escape) {
|
|
193
|
-
setStep('platform');
|
|
194
|
-
setSelectedIndex(0);
|
|
195
|
-
}
|
|
196
|
-
}, { isActive: step === 'user_mention' });
|
|
197
|
-
// Handle all input for non-text-input steps
|
|
198
|
-
useInput((input, key) => {
|
|
199
|
-
if (key.escape) {
|
|
200
|
-
if (step === 'platform') {
|
|
201
|
-
onSkip();
|
|
202
|
-
}
|
|
203
|
-
else if (step === 'confirm') {
|
|
204
|
-
setStep('user_mention');
|
|
205
|
-
}
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (step === 'platform') {
|
|
209
|
-
if (key.upArrow)
|
|
210
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
211
|
-
if (key.downArrow)
|
|
212
|
-
setSelectedIndex((prev) => Math.min(platformOptions.length - 1, prev + 1));
|
|
213
|
-
if (key.return) {
|
|
214
|
-
setPlatform(platformOptions[selectedIndex].value);
|
|
215
|
-
setStep('user_mention');
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else if (step === 'confirm') {
|
|
219
|
-
if (key.return) {
|
|
220
|
-
const existingConfig = loadConfig();
|
|
221
|
-
const config = {
|
|
222
|
-
...existingConfig,
|
|
223
|
-
platform: platform,
|
|
224
|
-
user_mention: userMention,
|
|
225
|
-
output_preference: BuiltInOutput.Terminal, // Default to terminal
|
|
226
|
-
};
|
|
227
|
-
saveConfig(config);
|
|
228
|
-
configurePlatformPermissions(platform);
|
|
229
|
-
onComplete();
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}, { isActive: step !== 'user_mention' });
|
|
233
|
-
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] })] }) }));
|
|
234
|
-
if (step === 'platform') {
|
|
235
|
-
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" }) })] }));
|
|
236
|
-
}
|
|
237
|
-
if (step === 'user_mention') {
|
|
238
|
-
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" }) })] }));
|
|
239
|
-
}
|
|
240
|
-
// Confirm step
|
|
241
|
-
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" }) })] }));
|
|
242
|
-
}
|
|
243
|
-
function TabBar({ tabs, activeTab }) {
|
|
244
|
-
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))) }));
|
|
245
|
-
}
|
|
246
|
-
function ToolItem({ tool, isSelected, isActive, wasAutoUpdated, }) {
|
|
247
|
-
const installed = isToolInstalled(tool.name);
|
|
248
|
-
const installedVersion = getInstalledToolVersion(tool.name);
|
|
249
|
-
const updateStatus = getToolUpdateStatus(tool.name);
|
|
250
|
-
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" }), wasAutoUpdated && _jsx(Text, { color: colors.success, children: " \u2191" }), updateStatus.hasUpdate && !wasAutoUpdated && _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" })] }) }));
|
|
251
|
-
}
|
|
252
|
-
function ToolDetails({ tool, isFocused, selectedAction, }) {
|
|
253
|
-
if (!tool) {
|
|
254
|
-
return (_jsx(Box, { paddingLeft: 2, flexGrow: 1, children: _jsx(Text, { color: colors.textDim, children: "Select a tool" }) }));
|
|
255
|
-
}
|
|
256
|
-
const installed = isToolInstalled(tool.name);
|
|
257
|
-
const installedVersion = getInstalledToolVersion(tool.name);
|
|
258
|
-
const updateStatus = getToolUpdateStatus(tool.name);
|
|
259
|
-
const isSystemTool = tool.system === true;
|
|
260
|
-
const actions = installed
|
|
261
|
-
? [
|
|
262
|
-
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
263
|
-
...(updateStatus.hasUpdate
|
|
264
|
-
? [{ id: 'update', label: `Update (${updateStatus.bundledVersion})`, variant: 'primary' }]
|
|
265
|
-
: []),
|
|
266
|
-
{ id: 'configure', label: 'Configure', variant: 'default' },
|
|
267
|
-
// System tools can't be uninstalled
|
|
268
|
-
...(!isSystemTool ? [{ id: 'uninstall', label: 'Uninstall', variant: 'danger' }] : []),
|
|
269
|
-
]
|
|
270
|
-
: [
|
|
271
|
-
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
272
|
-
// System tools auto-install, but show Install for manual trigger if needed
|
|
273
|
-
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
274
|
-
];
|
|
275
|
-
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
|
|
276
|
-
? action.variant === 'danger'
|
|
277
|
-
? colors.error
|
|
278
|
-
: colors.primary
|
|
279
|
-
: colors.bgSelected, color: selectedAction === index ? '#ffffff' : colors.textMuted, bold: selectedAction === index, children: [' ', action.label, ' '] }, action.id))) }))] }));
|
|
280
|
-
}
|
|
281
|
-
function MarkdownLine({ line, inCodeBlock }) {
|
|
282
|
-
// Code block content
|
|
283
|
-
if (inCodeBlock) {
|
|
284
|
-
return _jsx(Text, { color: "#a5d6ff", children: line || ' ' });
|
|
285
|
-
}
|
|
286
|
-
// Code block delimiter
|
|
287
|
-
if (line.startsWith('```')) {
|
|
288
|
-
return _jsx(Text, { color: colors.textDim, children: line });
|
|
289
|
-
}
|
|
290
|
-
// Headers
|
|
291
|
-
if (line.startsWith('# ')) {
|
|
292
|
-
return _jsx(Text, { color: colors.text, bold: true, children: line.slice(2) });
|
|
293
|
-
}
|
|
294
|
-
if (line.startsWith('## ')) {
|
|
295
|
-
return _jsx(Text, { color: colors.text, bold: true, children: line.slice(3) });
|
|
296
|
-
}
|
|
297
|
-
if (line.startsWith('### ')) {
|
|
298
|
-
return _jsx(Text, { color: "#c9d1d9", bold: true, children: line.slice(4) });
|
|
299
|
-
}
|
|
300
|
-
// YAML frontmatter delimiter
|
|
301
|
-
if (line === '---') {
|
|
302
|
-
return _jsx(Text, { color: colors.textDim, children: line });
|
|
303
|
-
}
|
|
304
|
-
// List items
|
|
305
|
-
if (line.match(/^[\s]*[-*]\s/)) {
|
|
306
|
-
return _jsx(Text, { color: colors.textMuted, children: line });
|
|
307
|
-
}
|
|
308
|
-
// Blockquotes
|
|
309
|
-
if (line.startsWith('>')) {
|
|
310
|
-
return _jsx(Text, { color: "#8b949e", italic: true, children: line });
|
|
311
|
-
}
|
|
312
|
-
// Table rows
|
|
313
|
-
if (line.includes('|')) {
|
|
314
|
-
return _jsx(Text, { color: colors.textMuted, children: line });
|
|
315
|
-
}
|
|
316
|
-
// Default
|
|
317
|
-
return _jsx(Text, { color: colors.textMuted, children: line || ' ' });
|
|
318
|
-
}
|
|
319
|
-
function ReadmeViewer({ title, content, onClose, }) {
|
|
320
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
321
|
-
const lines = useMemo(() => content.split('\n'), [content]);
|
|
322
|
-
const maxVisible = 20;
|
|
323
|
-
// Pre-compute code block state for each line
|
|
324
|
-
const lineStates = useMemo(() => {
|
|
325
|
-
const states = [];
|
|
326
|
-
let inCode = false;
|
|
327
|
-
for (const line of lines) {
|
|
328
|
-
if (line.startsWith('```')) {
|
|
329
|
-
states.push(false); // Delimiter itself is not "in" code block for styling
|
|
330
|
-
inCode = !inCode;
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
states.push(inCode);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return states;
|
|
337
|
-
}, [lines]);
|
|
338
|
-
// Max offset: when at end, we have top indicator + (maxVisible-1) content lines
|
|
339
|
-
// So max offset is lines.length - (maxVisible - 1) = lines.length - maxVisible + 1
|
|
340
|
-
const maxOffset = Math.max(0, lines.length - maxVisible + 1);
|
|
341
|
-
useInput((input, key) => {
|
|
342
|
-
if (key.escape) {
|
|
343
|
-
onClose();
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
if (key.upArrow) {
|
|
347
|
-
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
348
|
-
}
|
|
349
|
-
if (key.downArrow) {
|
|
350
|
-
setScrollOffset((prev) => Math.min(maxOffset, prev + 1));
|
|
351
|
-
}
|
|
352
|
-
if (key.pageDown || input === ' ') {
|
|
353
|
-
setScrollOffset((prev) => Math.min(maxOffset, prev + maxVisible));
|
|
354
|
-
}
|
|
355
|
-
if (key.pageUp) {
|
|
356
|
-
setScrollOffset((prev) => Math.max(0, prev - maxVisible));
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
// Adjust visible lines based on whether indicators are shown
|
|
360
|
-
const showTopIndicator = scrollOffset > 0;
|
|
361
|
-
// Reserve space for bottom indicator if not at end
|
|
362
|
-
const contentLines = maxVisible - (showTopIndicator ? 1 : 0);
|
|
363
|
-
const endIndex = Math.min(scrollOffset + contentLines, lines.length);
|
|
364
|
-
const showBottomIndicator = endIndex < lines.length;
|
|
365
|
-
const actualContentLines = contentLines - (showBottomIndicator ? 1 : 0);
|
|
366
|
-
const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
|
|
367
|
-
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" }) })] }));
|
|
368
|
-
}
|
|
369
|
-
function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
370
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
371
|
-
// Build list of all explorable items
|
|
372
|
-
const items = useMemo(() => {
|
|
373
|
-
const result = [];
|
|
374
|
-
const toolDir = getBundledToolsDir();
|
|
375
|
-
// Add skills
|
|
376
|
-
for (const skill of tool.includes.skills) {
|
|
377
|
-
result.push({
|
|
378
|
-
type: 'skill',
|
|
379
|
-
name: skill.name,
|
|
380
|
-
path: join(toolDir, tool.name, 'skills', skill.name, 'SKILL.md'),
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
// Add commands
|
|
384
|
-
for (const cmd of tool.includes.commands) {
|
|
385
|
-
result.push({
|
|
386
|
-
type: 'command',
|
|
387
|
-
name: `/${cmd}`,
|
|
388
|
-
path: join(toolDir, tool.name, 'commands', `${cmd}.md`),
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
// Add agents
|
|
392
|
-
for (const agent of tool.includes.agents) {
|
|
393
|
-
result.push({
|
|
394
|
-
type: 'agent',
|
|
395
|
-
name: agent,
|
|
396
|
-
path: join(toolDir, tool.name, 'agents', agent, 'AGENT.md'),
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
return result;
|
|
400
|
-
}, [tool]);
|
|
401
|
-
useInput((input, key) => {
|
|
402
|
-
if (key.escape) {
|
|
403
|
-
onClose();
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
if (key.leftArrow || key.upArrow) {
|
|
407
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
408
|
-
}
|
|
409
|
-
if (key.rightArrow || key.downArrow) {
|
|
410
|
-
setSelectedIndex((prev) => Math.min(items.length - 1, prev + 1));
|
|
411
|
-
}
|
|
412
|
-
if (key.return && items.length > 0) {
|
|
413
|
-
const item = items[selectedIndex];
|
|
414
|
-
if (existsSync(item.path)) {
|
|
415
|
-
const content = readFileSync(item.path, 'utf-8');
|
|
416
|
-
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
// Try YAML fallback for skills/agents
|
|
420
|
-
const yamlPath = item.path.replace('.md', '.yaml');
|
|
421
|
-
if (existsSync(yamlPath)) {
|
|
422
|
-
const content = readFileSync(yamlPath, 'utf-8');
|
|
423
|
-
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
// Group items by type for display
|
|
429
|
-
const skillItems = items.filter(i => i.type === 'skill');
|
|
430
|
-
const commandItems = items.filter(i => i.type === 'command');
|
|
431
|
-
const agentItems = items.filter(i => i.type === 'agent');
|
|
432
|
-
const getItemIndex = (item) => items.indexOf(item);
|
|
433
|
-
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) => {
|
|
434
|
-
const idx = getItemIndex(item);
|
|
435
|
-
const isSelected = selectedIndex === idx;
|
|
436
|
-
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));
|
|
437
|
-
}) })] })), 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) => {
|
|
438
|
-
const idx = getItemIndex(item);
|
|
439
|
-
const isSelected = selectedIndex === idx;
|
|
440
|
-
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));
|
|
441
|
-
}) })] })), 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) => {
|
|
442
|
-
const idx = getItemIndex(item);
|
|
443
|
-
const isSelected = selectedIndex === idx;
|
|
444
|
-
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));
|
|
445
|
-
}) })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 navigate \u00B7 enter view source \u00B7 esc back" }) })] }));
|
|
446
|
-
}
|
|
447
|
-
function SettingsDetails({ onEditSettings, isFocused, onToggleAutoUpdate, selectedSetting, }) {
|
|
448
|
-
const config = loadConfig();
|
|
449
|
-
const autoUpdateConfig = getAutoUpdateConfig();
|
|
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: "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 })] })] }), _jsxs(Box, { flexDirection: "column", marginTop: 2, children: [_jsx(Text, { color: colors.text, bold: true, children: "Auto-Update" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: isFocused && selectedSetting === 0 ? '> ' : ' ' }), _jsxs(Text, { color: isFocused && selectedSetting === 0 ? colors.text : colors.textMuted, children: ["[", autoUpdateConfig.tools ? 'x' : ' ', "] Auto-update tools"] })] }) }), _jsx(Text, { color: colors.textDim, children: " Update tools automatically when droid starts" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.textDim, children: isFocused && selectedSetting === 1 ? '> ' : ' ' }), _jsxs(Text, { color: isFocused && selectedSetting === 1 ? colors.text : colors.textMuted, children: ["[", autoUpdateConfig.app ? 'x' : ' ', "] Auto-update app"] })] }) }), _jsx(Text, { color: colors.textDim, children: " Update droid automatically when a new version is available" })] }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { color: colors.textDim, children: "Config: ~/.droid/config.yaml" }) }), isFocused && (_jsx(Box, { marginTop: 2, children: _jsxs(Text, { backgroundColor: selectedSetting === 2 ? colors.primary : colors.bgSelected, color: selectedSetting === 2 ? '#ffffff' : colors.textMuted, bold: selectedSetting === 2, children: [' ', "Edit Profile", ' '] }) })), isFocused && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2191\u2193 select \u00B7 enter toggle/edit \u00B7 esc back" }) })), !isFocused && (_jsx(Box, { marginTop: 2, children: _jsx(Text, { color: colors.textDim, children: "press enter to configure" }) }))] }));
|
|
451
|
-
}
|
|
452
|
-
const MAX_VISIBLE_CONFIG_ITEMS = 4; // Each config item takes ~3 lines
|
|
453
|
-
function SkillConfigScreen({ skill, onComplete, onCancel }) {
|
|
454
|
-
const configSchema = skill.config_schema || {};
|
|
455
|
-
const configKeys = Object.keys(configSchema);
|
|
456
|
-
const initialOverrides = useMemo(() => loadSkillOverrides(skill.name), [skill.name]);
|
|
457
|
-
// Initialize values from saved overrides or defaults
|
|
458
|
-
const [values, setValues] = useState(() => {
|
|
459
|
-
const initial = {};
|
|
460
|
-
for (const key of configKeys) {
|
|
461
|
-
const option = configSchema[key];
|
|
462
|
-
initial[key] = initialOverrides[key] ?? option.default ?? (option.type === ConfigOptionType.Boolean ? false : '');
|
|
463
|
-
}
|
|
464
|
-
return initial;
|
|
465
|
-
});
|
|
466
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
467
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
468
|
-
const [editingField, setEditingField] = useState(null);
|
|
469
|
-
const [editValue, setEditValue] = useState('');
|
|
470
|
-
const [editingSelect, setEditingSelect] = useState(null);
|
|
471
|
-
const [selectOptionIndex, setSelectOptionIndex] = useState(0);
|
|
472
|
-
// Total items = config keys + Save button
|
|
473
|
-
const totalItems = configKeys.length + 1;
|
|
474
|
-
const handleSave = () => {
|
|
475
|
-
saveSkillOverrides(skill.name, values);
|
|
476
|
-
onComplete();
|
|
477
|
-
};
|
|
478
|
-
const handleSubmitEdit = () => {
|
|
479
|
-
if (editingField) {
|
|
480
|
-
setValues((prev) => ({ ...prev, [editingField]: editValue }));
|
|
481
|
-
setEditingField(null);
|
|
482
|
-
setEditValue('');
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
// Handle text input for string fields
|
|
486
|
-
useInput((input, key) => {
|
|
487
|
-
if (key.escape) {
|
|
488
|
-
setEditingField(null);
|
|
489
|
-
setEditValue('');
|
|
490
|
-
}
|
|
491
|
-
}, { isActive: editingField !== null });
|
|
492
|
-
// Handle select field editing
|
|
493
|
-
useInput((input, key) => {
|
|
494
|
-
if (!editingSelect)
|
|
495
|
-
return;
|
|
496
|
-
const option = configSchema[editingSelect];
|
|
497
|
-
if (!option?.options)
|
|
498
|
-
return;
|
|
499
|
-
if (key.escape) {
|
|
500
|
-
setEditingSelect(null);
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
if (key.leftArrow || key.upArrow) {
|
|
504
|
-
setSelectOptionIndex((prev) => Math.max(0, prev - 1));
|
|
505
|
-
}
|
|
506
|
-
if (key.rightArrow || key.downArrow) {
|
|
507
|
-
setSelectOptionIndex((prev) => Math.min(option.options.length - 1, prev + 1));
|
|
508
|
-
}
|
|
509
|
-
if (key.return) {
|
|
510
|
-
setValues((prev) => ({ ...prev, [editingSelect]: option.options[selectOptionIndex] }));
|
|
511
|
-
setEditingSelect(null);
|
|
512
|
-
}
|
|
513
|
-
}, { isActive: editingSelect !== null });
|
|
514
|
-
// Handle navigation and actions when not editing
|
|
515
|
-
useInput((input, key) => {
|
|
516
|
-
if (key.escape) {
|
|
517
|
-
onCancel();
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
if (key.upArrow) {
|
|
521
|
-
setSelectedIndex((prev) => {
|
|
522
|
-
const newIndex = Math.max(0, prev - 1);
|
|
523
|
-
// Scroll up if needed
|
|
524
|
-
if (newIndex < scrollOffset) {
|
|
525
|
-
setScrollOffset(newIndex);
|
|
526
|
-
}
|
|
527
|
-
return newIndex;
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
if (key.downArrow) {
|
|
531
|
-
// +1 for the Save button at the end
|
|
532
|
-
setSelectedIndex((prev) => {
|
|
533
|
-
const newIndex = Math.min(totalItems - 1, prev + 1);
|
|
534
|
-
// Scroll down if needed
|
|
535
|
-
if (newIndex >= scrollOffset + MAX_VISIBLE_CONFIG_ITEMS) {
|
|
536
|
-
setScrollOffset(newIndex - MAX_VISIBLE_CONFIG_ITEMS + 1);
|
|
537
|
-
}
|
|
538
|
-
return newIndex;
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
if (key.return) {
|
|
542
|
-
// Save button is at index === configKeys.length
|
|
543
|
-
if (selectedIndex === configKeys.length) {
|
|
544
|
-
handleSave();
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
const key = configKeys[selectedIndex];
|
|
548
|
-
const option = configSchema[key];
|
|
549
|
-
if (option.type === ConfigOptionType.Boolean) {
|
|
550
|
-
// Toggle boolean
|
|
551
|
-
setValues((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
552
|
-
}
|
|
553
|
-
else if (option.type === ConfigOptionType.Select && option.options) {
|
|
554
|
-
// Enter select edit mode
|
|
555
|
-
const currentValue = String(values[key] || option.options[0]);
|
|
556
|
-
const currentIndex = option.options.indexOf(currentValue);
|
|
557
|
-
setSelectOptionIndex(currentIndex >= 0 ? currentIndex : 0);
|
|
558
|
-
setEditingSelect(key);
|
|
559
|
-
}
|
|
560
|
-
else if (option.type === ConfigOptionType.String) {
|
|
561
|
-
// Enter edit mode for string
|
|
562
|
-
setEditingField(key);
|
|
563
|
-
setEditValue(String(values[key] || ''));
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}, { isActive: editingField === null && editingSelect === null });
|
|
567
|
-
if (configKeys.length === 0) {
|
|
568
|
-
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: "] " }), _jsxs(Text, { color: colors.text, bold: true, children: ["configure ", skill.name] })] }) }), _jsx(Text, { color: colors.textMuted, children: "This skill has no configuration options." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "esc to go back" }) })] }));
|
|
569
|
-
}
|
|
570
|
-
// Calculate visible range
|
|
571
|
-
const visibleEndIndex = Math.min(scrollOffset + MAX_VISIBLE_CONFIG_ITEMS, totalItems);
|
|
572
|
-
const visibleConfigKeys = configKeys.slice(scrollOffset, Math.min(scrollOffset + MAX_VISIBLE_CONFIG_ITEMS, configKeys.length));
|
|
573
|
-
const showSaveButton = visibleEndIndex > configKeys.length || scrollOffset + MAX_VISIBLE_CONFIG_ITEMS > configKeys.length;
|
|
574
|
-
const showTopIndicator = scrollOffset > 0;
|
|
575
|
-
const showBottomIndicator = scrollOffset + MAX_VISIBLE_CONFIG_ITEMS < totalItems;
|
|
576
|
-
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: "] " }), _jsxs(Text, { color: colors.text, bold: true, children: ["configure ", skill.name] })] }) }), _jsxs(Box, { flexDirection: "column", children: [showTopIndicator && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: colors.textDim, children: [" \u2191 ", scrollOffset, " more"] }) })), visibleConfigKeys.map((key) => {
|
|
577
|
-
const actualIndex = configKeys.indexOf(key);
|
|
578
|
-
const option = configSchema[key];
|
|
579
|
-
const isSelected = selectedIndex === actualIndex;
|
|
580
|
-
const isEditing = editingField === key;
|
|
581
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsxs(Text, { color: colors.textDim, children: [isSelected ? '>' : ' ', " "] }), _jsx(Text, { color: isSelected ? colors.text : colors.textMuted, children: key })] }), _jsxs(Text, { color: colors.textDim, children: [" ", option.description] }), _jsxs(Box, { children: [_jsx(Text, { color: colors.textDim, children: " " }), option.type === ConfigOptionType.Boolean ? (_jsxs(Text, { color: colors.text, children: ["[", values[key] ? 'x' : ' ', "] ", values[key] ? 'enabled' : 'disabled'] })) : option.type === ConfigOptionType.Select && option.options ? (_jsx(Text, { color: colors.text, children: option.options.map((opt, i) => {
|
|
582
|
-
const isCurrentValue = String(values[key]) === opt;
|
|
583
|
-
const isEditingThis = editingSelect === key;
|
|
584
|
-
const isHighlighted = isEditingThis && selectOptionIndex === i;
|
|
585
|
-
return (_jsxs(Text, { children: [i > 0 && _jsx(Text, { color: colors.textDim, children: " \u00B7 " }), _jsx(Text, { color: isHighlighted ? '#ffffff' : isCurrentValue ? colors.primary : colors.textMuted, backgroundColor: isHighlighted ? colors.primary : undefined, children: isCurrentValue && !isEditingThis ? `[${opt}]` : isHighlighted ? ` ${opt} ` : opt })] }, opt));
|
|
586
|
-
}) })) : isEditing ? (_jsxs(Box, { children: [_jsx(Text, { color: colors.textDim, children: '> ' }), _jsx(TextInput, { value: editValue, onChange: setEditValue, onSubmit: handleSubmitEdit })] })) : (_jsx(Text, { color: colors.text, children: String(values[key]) || '(not set)' }))] })] }, key));
|
|
587
|
-
}), showSaveButton && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsxs(Text, { color: colors.textDim, children: [selectedIndex === configKeys.length ? '>' : ' ', " "] }), _jsxs(Text, { backgroundColor: selectedIndex === configKeys.length ? colors.primary : undefined, color: selectedIndex === configKeys.length ? '#ffffff' : colors.textMuted, bold: selectedIndex === configKeys.length, children: [' ', "Save", ' '] })] }) })), showBottomIndicator && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.textDim, children: [" \u2193 ", totalItems - scrollOffset - MAX_VISIBLE_CONFIG_ITEMS, " more"] }) }))] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: editingField
|
|
588
|
-
? 'enter save · esc cancel'
|
|
589
|
-
: editingSelect
|
|
590
|
-
? '←→ choose · enter select · esc cancel'
|
|
591
|
-
: '↑↓ select · enter toggle/edit · esc back' }) })] }));
|
|
592
|
-
}
|
|
593
24
|
function App() {
|
|
594
25
|
const { exit } = useApp();
|
|
595
26
|
const tabs = [
|
|
@@ -605,88 +36,22 @@ function App() {
|
|
|
605
36
|
const [message, setMessage] = useState(null);
|
|
606
37
|
const [isEditingSettings, setIsEditingSettings] = useState(false);
|
|
607
38
|
const [readmeContent, setReadmeContent] = useState(null);
|
|
608
|
-
const [selectedSetting, setSelectedSetting] = useState(0);
|
|
609
|
-
const [isUpdating, setIsUpdating] = useState(false);
|
|
610
|
-
const [isUpdatingTools, setIsUpdatingTools] = useState(false);
|
|
611
39
|
const [previousView, setPreviousView] = useState('detail'); // Track where to return from readme
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
setTimeout(() => {
|
|
621
|
-
const result = runUpdate();
|
|
622
|
-
if (result.success) {
|
|
623
|
-
// Store message to print after leaving alternate screen (with ANSI colors)
|
|
624
|
-
const blue = '\x1b[38;2;99;102;241m'; // #6366f1
|
|
625
|
-
const dim = '\x1b[38;2;106;106;106m';
|
|
626
|
-
const reset = '\x1b[0m';
|
|
627
|
-
exitMessage = `
|
|
628
|
-
${dim}────────────────────────────────────────────────────────${reset}
|
|
629
|
-
|
|
630
|
-
${dim}╔═════╗${reset}
|
|
631
|
-
${dim}║${reset} ${blue}●${reset} ${blue}●${reset} ${dim}║${reset} ${blue}"It's quite possible this system${reset}
|
|
632
|
-
${dim}╚═╦═╦═╝${reset} ${blue}is now fully operational."${reset}
|
|
633
|
-
|
|
634
|
-
Run ${blue}droid${reset} to start the new version.
|
|
635
|
-
|
|
636
|
-
${dim}────────────────────────────────────────────────────────${reset}
|
|
637
|
-
`;
|
|
638
|
-
exit();
|
|
40
|
+
const { updateInfo, isUpdating, handleUpdate, handleAlwaysUpdate } = useAppUpdate({
|
|
41
|
+
onUpdateSuccess: (msg) => {
|
|
42
|
+
exitMessage = msg;
|
|
43
|
+
},
|
|
44
|
+
onUpdateFailure: (error) => {
|
|
45
|
+
setMessage({ text: error, type: 'error' });
|
|
46
|
+
if (!configExists()) {
|
|
47
|
+
setView('setup');
|
|
639
48
|
}
|
|
640
49
|
else {
|
|
641
|
-
|
|
642
|
-
setMessage({ text: result.message, type: 'error' });
|
|
643
|
-
// Continue to menu on failure
|
|
644
|
-
if (!configExists()) {
|
|
645
|
-
setView('setup');
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
setView('menu');
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}, 100);
|
|
652
|
-
};
|
|
653
|
-
const handleAlwaysUpdate = () => {
|
|
654
|
-
// Enable auto-update for app in config
|
|
655
|
-
setAutoUpdateConfig({ app: true });
|
|
656
|
-
// Then run the update
|
|
657
|
-
handleUpdate();
|
|
658
|
-
};
|
|
659
|
-
// Ensure system tools (marked with system: true in TOOL.yaml) are always installed and current
|
|
660
|
-
const ensureSystemTools = () => {
|
|
661
|
-
// Find all tools marked as system tools
|
|
662
|
-
const systemTools = tools.filter(t => t.system === true);
|
|
663
|
-
for (const systemTool of systemTools) {
|
|
664
|
-
const installed = isToolInstalled(systemTool.name);
|
|
665
|
-
const updateStatus = getToolUpdateStatus(systemTool.name);
|
|
666
|
-
// Install if not installed, or update if outdated (regardless of auto-update settings)
|
|
667
|
-
if (!installed || updateStatus.hasUpdate) {
|
|
668
|
-
const primarySkill = systemTool.includes.skills.find(s => s.required)?.name || systemTool.name;
|
|
669
|
-
installSkill(primarySkill);
|
|
50
|
+
setView('menu');
|
|
670
51
|
}
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
|
|
674
|
-
const checkToolUpdatesAndProceed = () => {
|
|
675
|
-
// Always ensure system tools are current (bypasses auto-update settings)
|
|
676
|
-
ensureSystemTools();
|
|
677
|
-
const updates = getToolsWithUpdates();
|
|
678
|
-
setToolUpdates(updates);
|
|
679
|
-
// If auto_update.tools is true, auto-update silently
|
|
680
|
-
if (autoUpdateConfig.tools && updates.length > 0) {
|
|
681
|
-
handleUpdateAllTools(updates, true);
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
// If there are updates and auto-update is off, show prompt
|
|
685
|
-
if (updates.length > 0) {
|
|
686
|
-
setView('tool-updates');
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
// No updates, proceed to setup or menu
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const proceedToNextView = () => {
|
|
690
55
|
if (!configExists()) {
|
|
691
56
|
setView('setup');
|
|
692
57
|
}
|
|
@@ -694,34 +59,8 @@ ${dim}────────────────────────
|
|
|
694
59
|
setView('menu');
|
|
695
60
|
}
|
|
696
61
|
};
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
setIsUpdatingTools(true);
|
|
700
|
-
}
|
|
701
|
-
setTimeout(() => {
|
|
702
|
-
let successCount = 0;
|
|
703
|
-
let failCount = 0;
|
|
704
|
-
const updatedNames = [];
|
|
705
|
-
for (const tool of updates) {
|
|
706
|
-
// Find the tool to get its primary skill
|
|
707
|
-
const toolManifest = tools.find(t => t.name === tool.name);
|
|
708
|
-
if (toolManifest) {
|
|
709
|
-
const primarySkill = toolManifest.includes.skills.find(s => s.required)?.name || toolManifest.name;
|
|
710
|
-
const result = updateSkill(primarySkill);
|
|
711
|
-
if (result.success) {
|
|
712
|
-
successCount++;
|
|
713
|
-
updatedNames.push(tool.name);
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
failCount++;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
setIsUpdatingTools(false);
|
|
721
|
-
// Track which tools were auto-updated for visual indicator
|
|
722
|
-
if (silent && updatedNames.length > 0) {
|
|
723
|
-
setAutoUpdatedTools(updatedNames);
|
|
724
|
-
}
|
|
62
|
+
const { toolUpdates, isUpdatingTools, autoUpdatedTools, checkForUpdates, updateAllTools, enableAutoUpdateAndUpdate, } = useToolUpdates({
|
|
63
|
+
onUpdateComplete: ({ successCount, failCount, silent }) => {
|
|
725
64
|
if (successCount > 0) {
|
|
726
65
|
setMessage({
|
|
727
66
|
text: silent
|
|
@@ -730,47 +69,25 @@ ${dim}────────────────────────
|
|
|
730
69
|
type: failCount > 0 ? 'error' : 'success',
|
|
731
70
|
});
|
|
732
71
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const handleAlwaysUpdateTools = () => {
|
|
743
|
-
// Enable auto-update in config
|
|
744
|
-
setAutoUpdateConfig({ tools: true });
|
|
745
|
-
// Then update all
|
|
746
|
-
handleUpdateAllTools();
|
|
747
|
-
};
|
|
748
|
-
const handleSkipToolUpdates = () => {
|
|
749
|
-
if (!configExists()) {
|
|
750
|
-
setView('setup');
|
|
72
|
+
proceedToNextView();
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
// Check for tool updates and proceed to next view
|
|
76
|
+
const checkToolUpdatesAndProceed = () => {
|
|
77
|
+
const { updates, shouldAutoUpdate } = checkForUpdates();
|
|
78
|
+
if (shouldAutoUpdate) {
|
|
79
|
+
updateAllTools(updates, true);
|
|
80
|
+
return;
|
|
751
81
|
}
|
|
752
|
-
|
|
753
|
-
setView('
|
|
82
|
+
if (updates.length > 0) {
|
|
83
|
+
setView('tool-updates');
|
|
84
|
+
return;
|
|
754
85
|
}
|
|
86
|
+
proceedToNextView();
|
|
755
87
|
};
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
setAutoUpdateConfig({ tools: !current.tools });
|
|
759
|
-
// Force re-render by setting message (auto-clears on next input)
|
|
760
|
-
setMessage({
|
|
761
|
-
text: `✓ Auto-update tools ${!current.tools ? 'enabled' : 'disabled'}`,
|
|
762
|
-
type: 'success',
|
|
763
|
-
});
|
|
764
|
-
};
|
|
765
|
-
const handleToggleAutoUpdateApp = () => {
|
|
766
|
-
const current = getAutoUpdateConfig();
|
|
767
|
-
setAutoUpdateConfig({ app: !current.app });
|
|
768
|
-
setMessage({
|
|
769
|
-
text: `✓ Auto-update app ${!current.app ? 'enabled' : 'disabled'}`,
|
|
770
|
-
type: 'success',
|
|
771
|
-
});
|
|
88
|
+
const handleSkipToolUpdates = () => {
|
|
89
|
+
proceedToNextView();
|
|
772
90
|
};
|
|
773
|
-
const MAX_VISIBLE_ITEMS = 6;
|
|
774
91
|
const tools = getBundledTools();
|
|
775
92
|
// Keep skills for configure view (tools configure via their primary skill)
|
|
776
93
|
const skills = getBundledSkills();
|
|
@@ -825,7 +142,6 @@ ${dim}────────────────────────
|
|
|
825
142
|
}
|
|
826
143
|
else if (activeTab === 'settings') {
|
|
827
144
|
setView('detail');
|
|
828
|
-
setSelectedSetting(0);
|
|
829
145
|
}
|
|
830
146
|
}
|
|
831
147
|
}
|
|
@@ -833,27 +149,12 @@ ${dim}────────────────────────
|
|
|
833
149
|
if (key.escape || key.backspace) {
|
|
834
150
|
setView('menu');
|
|
835
151
|
setSelectedAction(0);
|
|
836
|
-
setSelectedSetting(0);
|
|
837
152
|
}
|
|
838
153
|
if (activeTab === 'settings') {
|
|
839
|
-
// Settings detail view
|
|
840
|
-
if (key.upArrow) {
|
|
841
|
-
setSelectedSetting((prev) => Math.max(0, prev - 1));
|
|
842
|
-
}
|
|
843
|
-
if (key.downArrow) {
|
|
844
|
-
setSelectedSetting((prev) => Math.min(2, prev + 1)); // 0: auto-update tools, 1: auto-update app, 2: edit profile
|
|
845
|
-
}
|
|
154
|
+
// Settings detail view - just enter to edit
|
|
846
155
|
if (key.return) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
}
|
|
850
|
-
else if (selectedSetting === 1) {
|
|
851
|
-
handleToggleAutoUpdateApp();
|
|
852
|
-
}
|
|
853
|
-
else if (selectedSetting === 2) {
|
|
854
|
-
setIsEditingSettings(true);
|
|
855
|
-
setView('setup');
|
|
856
|
-
}
|
|
156
|
+
setIsEditingSettings(true);
|
|
157
|
+
setView('setup');
|
|
857
158
|
}
|
|
858
159
|
}
|
|
859
160
|
if (key.leftArrow && activeTab === 'tools') {
|
|
@@ -953,7 +254,7 @@ ${dim}────────────────────────
|
|
|
953
254
|
return (_jsx(WelcomeScreen, { updateInfo: updateInfo, isUpdating: isUpdating, onUpdate: handleUpdate, onAlways: handleAlwaysUpdate, onExit: exit, onContinue: checkToolUpdatesAndProceed }));
|
|
954
255
|
}
|
|
955
256
|
if (view === 'tool-updates' && toolUpdates.length > 0) {
|
|
956
|
-
return (_jsx(ToolUpdatePrompt, { toolUpdates: toolUpdates, onUpdateAll: () =>
|
|
257
|
+
return (_jsx(ToolUpdatePrompt, { toolUpdates: toolUpdates, onUpdateAll: () => updateAllTools(), onAlways: enableAutoUpdateAndUpdate, onSkip: handleSkipToolUpdates, isUpdating: isUpdatingTools }));
|
|
957
258
|
}
|
|
958
259
|
if (view === 'setup') {
|
|
959
260
|
return (_jsx(SetupScreen, { onComplete: () => {
|
|
@@ -987,16 +288,13 @@ ${dim}────────────────────────
|
|
|
987
288
|
setView('detail');
|
|
988
289
|
} }));
|
|
989
290
|
}
|
|
990
|
-
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', wasAutoUpdated: autoUpdatedTools.includes(tool.name) }, 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, {
|
|
991
|
-
setIsEditingSettings(true);
|
|
992
|
-
setView('setup');
|
|
993
|
-
}, isFocused: view === 'detail', onToggleAutoUpdate: handleToggleAutoUpdateTools, selectedSetting: selectedSetting }))] }));
|
|
291
|
+
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', wasAutoUpdated: autoUpdatedTools.includes(tool.name) }, 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, { isFocused: view === 'detail' }))] }));
|
|
994
292
|
}
|
|
995
293
|
export async function tuiCommand() {
|
|
996
294
|
// Enter alternate screen (fullscreen mode)
|
|
997
295
|
process.stdout.write('\x1b[?1049h');
|
|
998
296
|
process.stdout.write('\x1b[H'); // Move cursor to top-left
|
|
999
|
-
const {
|
|
297
|
+
const { waitUntilExit } = render(_jsx(App, {}));
|
|
1000
298
|
await waitUntilExit();
|
|
1001
299
|
// Leave alternate screen
|
|
1002
300
|
process.stdout.write('\x1b[?1049l');
|