@orderful/droid 0.11.1 → 0.13.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/CHANGELOG.md +22 -0
- package/dist/commands/tui.d.ts.map +1 -1
- package/dist/commands/tui.js +245 -37
- package/dist/commands/tui.js.map +1 -1
- package/dist/lib/config.d.ts +9 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +26 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/tools.d.ts +11 -5
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/lib/tools.js +30 -1
- package/dist/lib/tools.js.map +1 -1
- package/dist/lib/types.d.ts +5 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/tools/droid/TOOL.yaml +18 -0
- package/dist/tools/droid/skills/droid/SKILL.md +117 -0
- package/dist/tools/droid/skills/droid/SKILL.yaml +7 -0
- package/package.json +1 -1
- package/src/commands/tui.tsx +401 -41
- package/src/lib/config.ts +30 -1
- package/src/lib/tools.ts +45 -6
- package/src/lib/types.ts +6 -0
- package/src/tools/droid/TOOL.yaml +18 -0
- package/src/tools/droid/skills/droid/SKILL.md +117 -0
- package/src/tools/droid/skills/droid/SKILL.yaml +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @orderful/droid
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#74](https://github.com/Orderful/droid/pull/74) [`4e820c2`](https://github.com/Orderful/droid/commit/4e820c21b11cfbacc0fe35ee65fc98d169f065ec) Thanks [@frytyler](https://github.com/frytyler)! - Add droid meta-skill for update awareness
|
|
8
|
+
- New "droid" tool with `system: true` flag (auto-installs and stays current)
|
|
9
|
+
- SKILL.md teaches Claude to check npm for droid updates and nudge users
|
|
10
|
+
- System tools can't be uninstalled (Uninstall button hidden)
|
|
11
|
+
|
|
12
|
+
## 0.12.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#72](https://github.com/Orderful/droid/pull/72) [`1611eac`](https://github.com/Orderful/droid/commit/1611eacb9076279bcc4ed552857d3c546441f80a) Thanks [@frytyler](https://github.com/frytyler)! - Add auto-update feature for app and tools
|
|
17
|
+
- Add `auto_update.app` and `auto_update.tools` config options
|
|
18
|
+
- Show tool updates prompt after welcome screen with [Update All] [Always] [Skip]
|
|
19
|
+
- Add [Always] button to app update prompt for consistency
|
|
20
|
+
- "Always" enables auto-update in config for future runs
|
|
21
|
+
- When auto-update is enabled, tools update silently on startup with indicator
|
|
22
|
+
- Add auto-update toggle to Settings screen
|
|
23
|
+
- Tools default to auto-update enabled, app defaults to disabled
|
|
24
|
+
|
|
3
25
|
## 0.11.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../../src/commands/tui.tsx"],"names":[],"mappings":"AA87DA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBhD"}
|
package/dist/commands/tui.js
CHANGED
|
@@ -6,12 +6,14 @@ import { execSync } from 'child_process';
|
|
|
6
6
|
import { existsSync, readFileSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { getBundledSkills, installSkill, uninstallSkill, updateSkill, } from '../lib/skills.js';
|
|
9
|
-
import { getBundledTools, getBundledToolsDir, isToolInstalled, getToolUpdateStatus, getInstalledToolVersion } from '../lib/tools.js';
|
|
10
|
-
import { configExists, loadConfig, saveConfig, loadSkillOverrides, saveSkillOverrides } from '../lib/config.js';
|
|
9
|
+
import { getBundledTools, getBundledToolsDir, isToolInstalled, getToolUpdateStatus, getInstalledToolVersion, getToolsWithUpdates } from '../lib/tools.js';
|
|
10
|
+
import { configExists, loadConfig, saveConfig, loadSkillOverrides, saveSkillOverrides, getAutoUpdateConfig, setAutoUpdateConfig } from '../lib/config.js';
|
|
11
11
|
import { configurePlatformPermissions } from './setup.js';
|
|
12
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
|
+
// Module-level variable to store exit message (printed after leaving alternate screen)
|
|
16
|
+
let exitMessage = null;
|
|
15
17
|
const colors = {
|
|
16
18
|
primary: '#6366f1',
|
|
17
19
|
bgSelected: '#2d2d2d',
|
|
@@ -90,20 +92,26 @@ function getOutputOptions() {
|
|
|
90
92
|
}
|
|
91
93
|
return options;
|
|
92
94
|
}
|
|
93
|
-
function WelcomeScreen({ onContinue, onUpdate, onExit, updateInfo, isUpdating }) {
|
|
95
|
+
function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isUpdating }) {
|
|
94
96
|
const [selectedButton, setSelectedButton] = useState(0);
|
|
95
97
|
const welcomeQuote = useMemo(() => getRandomQuote(), []);
|
|
96
98
|
useInput((input, key) => {
|
|
97
99
|
if (isUpdating)
|
|
98
100
|
return;
|
|
99
101
|
if (updateInfo.hasUpdate) {
|
|
100
|
-
if (key.leftArrow
|
|
101
|
-
setSelectedButton((prev) => (prev
|
|
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));
|
|
102
107
|
}
|
|
103
108
|
if (key.return) {
|
|
104
109
|
if (selectedButton === 0) {
|
|
105
110
|
onUpdate();
|
|
106
111
|
}
|
|
112
|
+
else if (selectedButton === 1) {
|
|
113
|
+
onAlways();
|
|
114
|
+
}
|
|
107
115
|
else {
|
|
108
116
|
onContinue();
|
|
109
117
|
}
|
|
@@ -122,7 +130,40 @@ function WelcomeScreen({ onContinue, onUpdate, onExit, updateInfo, isUpdating })
|
|
|
122
130
|
}
|
|
123
131
|
});
|
|
124
132
|
const hasUpdate = updateInfo.hasUpdate && updateInfo.latestVersion;
|
|
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 ===
|
|
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" }) })] }) }));
|
|
126
167
|
}
|
|
127
168
|
function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
128
169
|
const [step, setStep] = useState('platform');
|
|
@@ -202,11 +243,11 @@ function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
|
202
243
|
function TabBar({ tabs, activeTab }) {
|
|
203
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))) }));
|
|
204
245
|
}
|
|
205
|
-
function ToolItem({ tool, isSelected, isActive, }) {
|
|
246
|
+
function ToolItem({ tool, isSelected, isActive, wasAutoUpdated, }) {
|
|
206
247
|
const installed = isToolInstalled(tool.name);
|
|
207
248
|
const installedVersion = getInstalledToolVersion(tool.name);
|
|
208
249
|
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" })] }) }));
|
|
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" })] }) }));
|
|
210
251
|
}
|
|
211
252
|
function ToolDetails({ tool, isFocused, selectedAction, }) {
|
|
212
253
|
if (!tool) {
|
|
@@ -215,6 +256,7 @@ function ToolDetails({ tool, isFocused, selectedAction, }) {
|
|
|
215
256
|
const installed = isToolInstalled(tool.name);
|
|
216
257
|
const installedVersion = getInstalledToolVersion(tool.name);
|
|
217
258
|
const updateStatus = getToolUpdateStatus(tool.name);
|
|
259
|
+
const isSystemTool = tool.system === true;
|
|
218
260
|
const actions = installed
|
|
219
261
|
? [
|
|
220
262
|
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
@@ -222,10 +264,12 @@ function ToolDetails({ tool, isFocused, selectedAction, }) {
|
|
|
222
264
|
? [{ id: 'update', label: `Update (${updateStatus.bundledVersion})`, variant: 'primary' }]
|
|
223
265
|
: []),
|
|
224
266
|
{ id: 'configure', label: 'Configure', variant: 'default' },
|
|
225
|
-
|
|
267
|
+
// System tools can't be uninstalled
|
|
268
|
+
...(!isSystemTool ? [{ id: 'uninstall', label: 'Uninstall', variant: 'danger' }] : []),
|
|
226
269
|
]
|
|
227
270
|
: [
|
|
228
271
|
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
272
|
+
// System tools auto-install, but show Install for manual trigger if needed
|
|
229
273
|
{ id: 'install', label: 'Install', variant: 'primary' },
|
|
230
274
|
];
|
|
231
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
|
|
@@ -400,10 +444,10 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
|
400
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));
|
|
401
445
|
}) })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.textDim, children: "\u2190\u2192 navigate \u00B7 enter view source \u00B7 esc back" }) })] }));
|
|
402
446
|
}
|
|
403
|
-
function SettingsDetails({ onEditSettings, isFocused, }) {
|
|
447
|
+
function SettingsDetails({ onEditSettings, isFocused, onToggleAutoUpdate, selectedSetting, }) {
|
|
404
448
|
const config = loadConfig();
|
|
405
|
-
const
|
|
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:
|
|
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" }) }))] }));
|
|
407
451
|
}
|
|
408
452
|
const MAX_VISIBLE_CONFIG_ITEMS = 4; // Each config item takes ~3 lines
|
|
409
453
|
function SkillConfigScreen({ skill, onComplete, onCancel }) {
|
|
@@ -561,18 +605,36 @@ function App() {
|
|
|
561
605
|
const [message, setMessage] = useState(null);
|
|
562
606
|
const [isEditingSettings, setIsEditingSettings] = useState(false);
|
|
563
607
|
const [readmeContent, setReadmeContent] = useState(null);
|
|
608
|
+
const [selectedSetting, setSelectedSetting] = useState(0);
|
|
564
609
|
const [isUpdating, setIsUpdating] = useState(false);
|
|
610
|
+
const [isUpdatingTools, setIsUpdatingTools] = useState(false);
|
|
565
611
|
const [previousView, setPreviousView] = useState('detail'); // Track where to return from readme
|
|
612
|
+
const [toolUpdates, setToolUpdates] = useState([]);
|
|
613
|
+
const [autoUpdatedTools, setAutoUpdatedTools] = useState([]);
|
|
566
614
|
// Check for updates once on mount
|
|
567
615
|
const updateInfo = useMemo(() => getUpdateInfo(), []);
|
|
616
|
+
const autoUpdateConfig = useMemo(() => getAutoUpdateConfig(), []);
|
|
568
617
|
const handleUpdate = () => {
|
|
569
618
|
setIsUpdating(true);
|
|
570
619
|
// Run update in next tick to allow UI to show "Updating..."
|
|
571
620
|
setTimeout(() => {
|
|
572
621
|
const result = runUpdate();
|
|
573
622
|
if (result.success) {
|
|
574
|
-
//
|
|
575
|
-
|
|
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
|
+
`;
|
|
576
638
|
exit();
|
|
577
639
|
}
|
|
578
640
|
else {
|
|
@@ -588,6 +650,126 @@ function App() {
|
|
|
588
650
|
}
|
|
589
651
|
}, 100);
|
|
590
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);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
// Check for tool updates and proceed to next view
|
|
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
|
|
690
|
+
if (!configExists()) {
|
|
691
|
+
setView('setup');
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
setView('menu');
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
const handleUpdateAllTools = (updates = toolUpdates, silent = false) => {
|
|
698
|
+
if (!silent) {
|
|
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
|
+
}
|
|
725
|
+
if (successCount > 0) {
|
|
726
|
+
setMessage({
|
|
727
|
+
text: silent
|
|
728
|
+
? `↑ Auto-updated ${successCount} tool${successCount > 1 ? 's' : ''}`
|
|
729
|
+
: `✓ Updated ${successCount} tool${successCount > 1 ? 's' : ''}${failCount > 0 ? `, ${failCount} failed` : ''}`,
|
|
730
|
+
type: failCount > 0 ? 'error' : 'success',
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
// Proceed to next view
|
|
734
|
+
if (!configExists()) {
|
|
735
|
+
setView('setup');
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
setView('menu');
|
|
739
|
+
}
|
|
740
|
+
}, 100);
|
|
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');
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
setView('menu');
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
const handleToggleAutoUpdateTools = () => {
|
|
757
|
+
const current = getAutoUpdateConfig();
|
|
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
|
+
});
|
|
772
|
+
};
|
|
591
773
|
const MAX_VISIBLE_ITEMS = 6;
|
|
592
774
|
const tools = getBundledTools();
|
|
593
775
|
// Keep skills for configure view (tools configure via their primary skill)
|
|
@@ -642,8 +824,8 @@ function App() {
|
|
|
642
824
|
setView('detail');
|
|
643
825
|
}
|
|
644
826
|
else if (activeTab === 'settings') {
|
|
645
|
-
|
|
646
|
-
|
|
827
|
+
setView('detail');
|
|
828
|
+
setSelectedSetting(0);
|
|
647
829
|
}
|
|
648
830
|
}
|
|
649
831
|
}
|
|
@@ -651,19 +833,41 @@ function App() {
|
|
|
651
833
|
if (key.escape || key.backspace) {
|
|
652
834
|
setView('menu');
|
|
653
835
|
setSelectedAction(0);
|
|
836
|
+
setSelectedSetting(0);
|
|
654
837
|
}
|
|
655
|
-
if (
|
|
838
|
+
if (activeTab === 'settings') {
|
|
839
|
+
// Settings detail view navigation
|
|
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
|
+
}
|
|
846
|
+
if (key.return) {
|
|
847
|
+
if (selectedSetting === 0) {
|
|
848
|
+
handleToggleAutoUpdateTools();
|
|
849
|
+
}
|
|
850
|
+
else if (selectedSetting === 1) {
|
|
851
|
+
handleToggleAutoUpdateApp();
|
|
852
|
+
}
|
|
853
|
+
else if (selectedSetting === 2) {
|
|
854
|
+
setIsEditingSettings(true);
|
|
855
|
+
setView('setup');
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (key.leftArrow && activeTab === 'tools') {
|
|
656
860
|
setSelectedAction((prev) => Math.max(0, prev - 1));
|
|
657
861
|
}
|
|
658
|
-
if (key.rightArrow) {
|
|
862
|
+
if (key.rightArrow && activeTab === 'tools') {
|
|
659
863
|
let maxActions = 0;
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
864
|
+
const tool = tools[selectedIndex];
|
|
865
|
+
const installed = tool ? isToolInstalled(tool.name) : false;
|
|
866
|
+
const hasUpdate = tool ? getToolUpdateStatus(tool.name).hasUpdate : false;
|
|
867
|
+
const isSystem = tool ? tool.system === true : false;
|
|
868
|
+
// Explore, [Update], Configure, [Uninstall] or Explore, Install
|
|
869
|
+
// System tools don't have Uninstall, so one less action
|
|
870
|
+
maxActions = installed ? (hasUpdate ? (isSystem ? 2 : 3) : (isSystem ? 1 : 2)) : 1;
|
|
667
871
|
setSelectedAction((prev) => Math.min(maxActions, prev + 1));
|
|
668
872
|
}
|
|
669
873
|
if (key.return && activeTab === 'tools') {
|
|
@@ -671,13 +875,14 @@ function App() {
|
|
|
671
875
|
if (tool) {
|
|
672
876
|
const installed = isToolInstalled(tool.name);
|
|
673
877
|
const toolUpdateStatus = getToolUpdateStatus(tool.name);
|
|
878
|
+
const isSystemTool = tool.system === true;
|
|
674
879
|
// Build actions array to match ToolDetails
|
|
675
880
|
const toolActions = installed
|
|
676
881
|
? [
|
|
677
882
|
{ id: 'explore' },
|
|
678
883
|
...(toolUpdateStatus.hasUpdate ? [{ id: 'update' }] : []),
|
|
679
884
|
{ id: 'configure' },
|
|
680
|
-
{ id: 'uninstall' },
|
|
885
|
+
...(!isSystemTool ? [{ id: 'uninstall' }] : []),
|
|
681
886
|
]
|
|
682
887
|
: [{ id: 'explore' }, { id: 'install' }];
|
|
683
888
|
const actionId = toolActions[selectedAction]?.id;
|
|
@@ -738,22 +943,17 @@ function App() {
|
|
|
738
943
|
}
|
|
739
944
|
}
|
|
740
945
|
}
|
|
741
|
-
}, { isActive: view !== 'welcome' && view !== 'setup' && view !== 'configure' && view !== 'explorer' });
|
|
946
|
+
}, { isActive: view !== 'welcome' && view !== 'tool-updates' && view !== 'setup' && view !== 'configure' && view !== 'explorer' });
|
|
742
947
|
const selectedTool = activeTab === 'tools' ? tools[selectedIndex] ?? null : null;
|
|
743
948
|
// For configure view, we need the matching skill manifest
|
|
744
949
|
const selectedSkillForConfig = selectedTool
|
|
745
950
|
? skills.find(s => s.name === (selectedTool.includes.skills.find(sk => sk.required)?.name || selectedTool.name))
|
|
746
951
|
: null;
|
|
747
952
|
if (view === 'welcome') {
|
|
748
|
-
return (_jsx(WelcomeScreen, { updateInfo: updateInfo, isUpdating: isUpdating, onUpdate: handleUpdate, onExit: exit, onContinue:
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
setView('menu');
|
|
755
|
-
}
|
|
756
|
-
} }));
|
|
953
|
+
return (_jsx(WelcomeScreen, { updateInfo: updateInfo, isUpdating: isUpdating, onUpdate: handleUpdate, onAlways: handleAlwaysUpdate, onExit: exit, onContinue: checkToolUpdatesAndProceed }));
|
|
954
|
+
}
|
|
955
|
+
if (view === 'tool-updates' && toolUpdates.length > 0) {
|
|
956
|
+
return (_jsx(ToolUpdatePrompt, { toolUpdates: toolUpdates, onUpdateAll: () => handleUpdateAllTools(), onAlways: handleAlwaysUpdateTools, onSkip: handleSkipToolUpdates, isUpdating: isUpdatingTools }));
|
|
757
957
|
}
|
|
758
958
|
if (view === 'setup') {
|
|
759
959
|
return (_jsx(SetupScreen, { onComplete: () => {
|
|
@@ -787,7 +987,10 @@ function App() {
|
|
|
787
987
|
setView('detail');
|
|
788
988
|
} }));
|
|
789
989
|
}
|
|
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: () =>
|
|
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, { onEditSettings: () => {
|
|
991
|
+
setIsEditingSettings(true);
|
|
992
|
+
setView('setup');
|
|
993
|
+
}, isFocused: view === 'detail', onToggleAutoUpdate: handleToggleAutoUpdateTools, selectedSetting: selectedSetting }))] }));
|
|
791
994
|
}
|
|
792
995
|
export async function tuiCommand() {
|
|
793
996
|
// Enter alternate screen (fullscreen mode)
|
|
@@ -797,5 +1000,10 @@ export async function tuiCommand() {
|
|
|
797
1000
|
await waitUntilExit();
|
|
798
1001
|
// Leave alternate screen
|
|
799
1002
|
process.stdout.write('\x1b[?1049l');
|
|
1003
|
+
// Print exit message if set (e.g., after successful update)
|
|
1004
|
+
if (exitMessage) {
|
|
1005
|
+
console.log(exitMessage);
|
|
1006
|
+
exitMessage = null;
|
|
1007
|
+
}
|
|
800
1008
|
}
|
|
801
1009
|
//# sourceMappingURL=tui.js.map
|