@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.
Files changed (204) hide show
  1. package/.claude/CLAUDE.md +31 -9
  2. package/CHANGELOG.md +35 -0
  3. package/dist/bin/droid.js +8 -8
  4. package/dist/bin/droid.js.map +1 -1
  5. package/dist/commands/config.js +1 -1
  6. package/dist/commands/config.js.map +1 -1
  7. package/dist/commands/install.js +3 -3
  8. package/dist/commands/install.js.map +1 -1
  9. package/dist/commands/setup.d.ts +1 -1
  10. package/dist/commands/setup.d.ts.map +1 -1
  11. package/dist/commands/setup.js +3 -3
  12. package/dist/commands/setup.js.map +1 -1
  13. package/dist/commands/skills.js +4 -4
  14. package/dist/commands/skills.js.map +1 -1
  15. package/dist/commands/tui/components/Badge.d.ts +13 -0
  16. package/dist/commands/tui/components/Badge.d.ts.map +1 -0
  17. package/dist/commands/tui/components/Badge.js +29 -0
  18. package/dist/commands/tui/components/Badge.js.map +1 -0
  19. package/dist/commands/tui/components/Markdown.d.ts +5 -0
  20. package/dist/commands/tui/components/Markdown.d.ts.map +1 -0
  21. package/dist/commands/tui/components/Markdown.js +42 -0
  22. package/dist/commands/tui/components/Markdown.js.map +1 -0
  23. package/dist/commands/tui/components/SettingsDetails.d.ts +5 -0
  24. package/dist/commands/tui/components/SettingsDetails.d.ts.map +1 -0
  25. package/dist/commands/tui/components/SettingsDetails.js +11 -0
  26. package/dist/commands/tui/components/SettingsDetails.js.map +1 -0
  27. package/dist/commands/tui/components/TabBar.d.ts +10 -0
  28. package/dist/commands/tui/components/TabBar.d.ts.map +1 -0
  29. package/dist/commands/tui/components/TabBar.js +7 -0
  30. package/dist/commands/tui/components/TabBar.js.map +1 -0
  31. package/dist/commands/tui/components/ToolDetails.d.ts +8 -0
  32. package/dist/commands/tui/components/ToolDetails.d.ts.map +1 -0
  33. package/dist/commands/tui/components/ToolDetails.js +35 -0
  34. package/dist/commands/tui/components/ToolDetails.js.map +1 -0
  35. package/dist/commands/tui/components/ToolItem.d.ts +9 -0
  36. package/dist/commands/tui/components/ToolItem.d.ts.map +1 -0
  37. package/dist/commands/tui/components/ToolItem.js +11 -0
  38. package/dist/commands/tui/components/ToolItem.js.map +1 -0
  39. package/dist/commands/tui/constants.d.ts +16 -0
  40. package/dist/commands/tui/constants.d.ts.map +1 -0
  41. package/dist/commands/tui/constants.js +17 -0
  42. package/dist/commands/tui/constants.js.map +1 -0
  43. package/dist/commands/tui/hooks/useAppUpdate.d.ts +13 -0
  44. package/dist/commands/tui/hooks/useAppUpdate.d.ts.map +1 -0
  45. package/dist/commands/tui/hooks/useAppUpdate.js +52 -0
  46. package/dist/commands/tui/hooks/useAppUpdate.js.map +1 -0
  47. package/dist/commands/tui/hooks/useToolUpdates.d.ts +22 -0
  48. package/dist/commands/tui/hooks/useToolUpdates.d.ts.map +1 -0
  49. package/dist/commands/tui/hooks/useToolUpdates.js +77 -0
  50. package/dist/commands/tui/hooks/useToolUpdates.js.map +1 -0
  51. package/dist/commands/tui/types.d.ts +5 -0
  52. package/dist/commands/tui/types.d.ts.map +1 -0
  53. package/dist/commands/tui/types.js +2 -0
  54. package/dist/commands/tui/types.js.map +1 -0
  55. package/dist/commands/tui/views/ReadmeViewer.d.ts +7 -0
  56. package/dist/commands/tui/views/ReadmeViewer.d.ts.map +1 -0
  57. package/dist/commands/tui/views/ReadmeViewer.js +56 -0
  58. package/dist/commands/tui/views/ReadmeViewer.js.map +1 -0
  59. package/dist/commands/tui/views/SetupScreen.d.ts +8 -0
  60. package/dist/commands/tui/views/SetupScreen.d.ts.map +1 -0
  61. package/dist/commands/tui/views/SetupScreen.js +114 -0
  62. package/dist/commands/tui/views/SetupScreen.js.map +1 -0
  63. package/dist/commands/tui/views/SkillConfigScreen.d.ts +8 -0
  64. package/dist/commands/tui/views/SkillConfigScreen.d.ts.map +1 -0
  65. package/dist/commands/tui/views/SkillConfigScreen.js +148 -0
  66. package/dist/commands/tui/views/SkillConfigScreen.js.map +1 -0
  67. package/dist/commands/tui/views/ToolExplorer.d.ts +8 -0
  68. package/dist/commands/tui/views/ToolExplorer.d.ts.map +1 -0
  69. package/dist/commands/tui/views/ToolExplorer.js +86 -0
  70. package/dist/commands/tui/views/ToolExplorer.js.map +1 -0
  71. package/dist/commands/tui/views/ToolUpdatePrompt.d.ts +10 -0
  72. package/dist/commands/tui/views/ToolUpdatePrompt.d.ts.map +1 -0
  73. package/dist/commands/tui/views/ToolUpdatePrompt.js +38 -0
  74. package/dist/commands/tui/views/ToolUpdatePrompt.js.map +1 -0
  75. package/dist/commands/tui/views/WelcomeScreen.d.ts +11 -0
  76. package/dist/commands/tui/views/WelcomeScreen.d.ts.map +1 -0
  77. package/dist/commands/tui/views/WelcomeScreen.js +46 -0
  78. package/dist/commands/tui/views/WelcomeScreen.js.map +1 -0
  79. package/dist/commands/tui.d.ts.map +1 -1
  80. package/dist/commands/tui.js +54 -756
  81. package/dist/commands/tui.js.map +1 -1
  82. package/dist/commands/uninstall.js +2 -2
  83. package/dist/commands/uninstall.js.map +1 -1
  84. package/dist/commands/update.js +1 -1
  85. package/dist/commands/update.js.map +1 -1
  86. package/dist/index.d.ts +4 -4
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +4 -4
  89. package/dist/index.js.map +1 -1
  90. package/dist/lib/agents.d.ts +7 -7
  91. package/dist/lib/agents.d.ts.map +1 -1
  92. package/dist/lib/agents.js +63 -41
  93. package/dist/lib/agents.js.map +1 -1
  94. package/dist/lib/config.d.ts +1 -1
  95. package/dist/lib/config.d.ts.map +1 -1
  96. package/dist/lib/config.js +1 -1
  97. package/dist/lib/config.js.map +1 -1
  98. package/dist/lib/platforms.d.ts +1 -1
  99. package/dist/lib/platforms.d.ts.map +1 -1
  100. package/dist/lib/platforms.js +1 -1
  101. package/dist/lib/platforms.js.map +1 -1
  102. package/dist/lib/skill-config.d.ts +1 -1
  103. package/dist/lib/skill-config.d.ts.map +1 -1
  104. package/dist/lib/skill-config.js +2 -2
  105. package/dist/lib/skill-config.js.map +1 -1
  106. package/dist/lib/skills.d.ts +2 -1
  107. package/dist/lib/skills.d.ts.map +1 -1
  108. package/dist/lib/skills.js +45 -12
  109. package/dist/lib/skills.js.map +1 -1
  110. package/dist/lib/tools.d.ts +1 -1
  111. package/dist/lib/tools.d.ts.map +1 -1
  112. package/dist/lib/tools.js +3 -3
  113. package/dist/lib/tools.js.map +1 -1
  114. package/dist/lib/types.d.ts +4 -0
  115. package/dist/lib/types.d.ts.map +1 -1
  116. package/dist/tools/README.md +79 -50
  117. package/dist/tools/brain/TOOL.yaml +1 -1
  118. package/dist/tools/brain/skills/brain/SKILL.md +1 -0
  119. package/dist/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
  120. package/dist/tools/coach/TOOL.yaml +1 -1
  121. package/dist/tools/coach/skills/coach/SKILL.md +1 -0
  122. package/{src/tools/code-review/agents/edi-standards-reviewer/AGENT.md → dist/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
  123. package/dist/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
  124. package/{src/tools/code-review/agents/test-coverage-analyzer/AGENT.md → dist/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
  125. package/dist/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
  126. package/dist/tools/code-review/skills/code-review/SKILL.md +1 -0
  127. package/dist/tools/comments/TOOL.yaml +2 -2
  128. package/dist/tools/comments/skills/comments/SKILL.md +1 -0
  129. package/dist/tools/droid/skills/droid/SKILL.md +1 -0
  130. package/dist/tools/project/skills/project/SKILL.md +1 -0
  131. package/package.json +3 -3
  132. package/src/bin/droid.ts +8 -8
  133. package/src/commands/config.ts +1 -1
  134. package/src/commands/install.ts +3 -3
  135. package/src/commands/setup.test.ts +1 -1
  136. package/src/commands/setup.ts +3 -3
  137. package/src/commands/skills.ts +4 -4
  138. package/src/commands/tui/components/Badge.tsx +86 -0
  139. package/src/commands/tui/components/Markdown.tsx +48 -0
  140. package/src/commands/tui/components/SettingsDetails.tsx +70 -0
  141. package/src/commands/tui/components/TabBar.tsx +26 -0
  142. package/src/commands/tui/components/ToolDetails.tsx +117 -0
  143. package/src/commands/tui/components/ToolItem.tsx +39 -0
  144. package/src/commands/tui/constants.ts +17 -0
  145. package/src/commands/tui/hooks/useAppUpdate.ts +67 -0
  146. package/src/commands/tui/hooks/useToolUpdates.ts +110 -0
  147. package/src/commands/tui/types.ts +4 -0
  148. package/src/commands/tui/views/ReadmeViewer.tsx +93 -0
  149. package/src/commands/tui/views/SetupScreen.tsx +242 -0
  150. package/src/commands/tui/views/SkillConfigScreen.tsx +278 -0
  151. package/src/commands/tui/views/ToolExplorer.tsx +190 -0
  152. package/src/commands/tui/views/ToolUpdatePrompt.tsx +109 -0
  153. package/src/commands/tui/views/WelcomeScreen.tsx +149 -0
  154. package/src/commands/tui.tsx +65 -1588
  155. package/src/commands/uninstall.ts +2 -2
  156. package/src/commands/update.ts +1 -1
  157. package/src/index.ts +4 -4
  158. package/src/lib/agents.ts +68 -45
  159. package/src/lib/config.ts +1 -1
  160. package/src/lib/platforms.ts +1 -1
  161. package/src/lib/skill-config.ts +2 -2
  162. package/src/lib/skills.test.ts +28 -33
  163. package/src/lib/skills.ts +49 -12
  164. package/src/lib/tools.ts +3 -3
  165. package/src/lib/types.test.ts +1 -1
  166. package/src/lib/types.ts +5 -0
  167. package/src/lib/version.test.ts +1 -1
  168. package/src/tools/README.md +79 -50
  169. package/src/tools/brain/TOOL.yaml +1 -1
  170. package/src/tools/brain/skills/brain/SKILL.md +1 -0
  171. package/src/tools/brain/skills/brain-obsidian/SKILL.md +1 -0
  172. package/src/tools/coach/TOOL.yaml +1 -1
  173. package/src/tools/coach/skills/coach/SKILL.md +1 -0
  174. package/{dist/tools/code-review/agents/edi-standards-reviewer/AGENT.md → src/tools/code-review/agents/edi-standards-reviewer.md} +10 -0
  175. package/src/tools/code-review/agents/{error-handling-reviewer/AGENT.md → error-handling-reviewer.md} +10 -0
  176. package/{dist/tools/code-review/agents/test-coverage-analyzer/AGENT.md → src/tools/code-review/agents/test-coverage-analyzer.md} +11 -0
  177. package/src/tools/code-review/agents/{type-reviewer/AGENT.md → type-reviewer.md} +10 -0
  178. package/src/tools/code-review/skills/code-review/SKILL.md +1 -0
  179. package/src/tools/comments/TOOL.yaml +2 -2
  180. package/src/tools/comments/skills/comments/SKILL.md +1 -0
  181. package/src/tools/droid/skills/droid/SKILL.md +1 -0
  182. package/src/tools/project/skills/project/SKILL.md +1 -0
  183. package/dist/tools/brain/skills/brain/SKILL.yaml +0 -29
  184. package/dist/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
  185. package/dist/tools/coach/skills/coach/SKILL.yaml +0 -25
  186. package/dist/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
  187. package/dist/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
  188. package/dist/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
  189. package/dist/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
  190. package/dist/tools/code-review/skills/code-review/SKILL.yaml +0 -19
  191. package/dist/tools/comments/skills/comments/SKILL.yaml +0 -50
  192. package/dist/tools/droid/skills/droid/SKILL.yaml +0 -7
  193. package/dist/tools/project/skills/project/SKILL.yaml +0 -30
  194. package/src/tools/brain/skills/brain/SKILL.yaml +0 -29
  195. package/src/tools/brain/skills/brain-obsidian/SKILL.yaml +0 -42
  196. package/src/tools/coach/skills/coach/SKILL.yaml +0 -25
  197. package/src/tools/code-review/agents/edi-standards-reviewer/AGENT.yaml +0 -14
  198. package/src/tools/code-review/agents/error-handling-reviewer/AGENT.yaml +0 -14
  199. package/src/tools/code-review/agents/test-coverage-analyzer/AGENT.yaml +0 -14
  200. package/src/tools/code-review/agents/type-reviewer/AGENT.yaml +0 -13
  201. package/src/tools/code-review/skills/code-review/SKILL.yaml +0 -19
  202. package/src/tools/comments/skills/comments/SKILL.yaml +0 -50
  203. package/src/tools/droid/skills/droid/SKILL.yaml +0 -7
  204. package/src/tools/project/skills/project/SKILL.yaml +0 -30
@@ -0,0 +1,110 @@
1
+ import { useState, useCallback } from 'react';
2
+ import {
3
+ getBundledTools,
4
+ isToolInstalled,
5
+ getToolUpdateStatus,
6
+ getToolsWithUpdates,
7
+ type ToolUpdateInfo,
8
+ } from '../../../lib/tools';
9
+ import { installSkill, updateSkill } from '../../../lib/skills';
10
+ import { getAutoUpdateConfig, setAutoUpdateConfig } from '../../../lib/config';
11
+ import type { ToolManifest } from '../../../lib/types';
12
+
13
+ export interface UseToolUpdatesOptions {
14
+ onUpdateComplete: (result: { successCount: number; failCount: number; updatedNames: string[]; silent: boolean }) => void;
15
+ }
16
+
17
+ export interface UseToolUpdatesResult {
18
+ toolUpdates: ToolUpdateInfo[];
19
+ isUpdatingTools: boolean;
20
+ autoUpdatedTools: string[];
21
+ checkForUpdates: () => { updates: ToolUpdateInfo[]; shouldAutoUpdate: boolean };
22
+ updateAllTools: (updates?: ToolUpdateInfo[], silent?: boolean) => void;
23
+ enableAutoUpdateAndUpdate: () => void;
24
+ }
25
+
26
+ export function useToolUpdates({ onUpdateComplete }: UseToolUpdatesOptions): UseToolUpdatesResult {
27
+ const [toolUpdates, setToolUpdates] = useState<ToolUpdateInfo[]>([]);
28
+ const [isUpdatingTools, setIsUpdatingTools] = useState(false);
29
+ const [autoUpdatedTools, setAutoUpdatedTools] = useState<string[]>([]);
30
+
31
+ const tools = getBundledTools();
32
+
33
+ // Ensure system tools are installed/updated (bypasses auto-update settings)
34
+ const ensureSystemTools = useCallback(() => {
35
+ const systemTools = tools.filter(t => (t as ToolManifest & { system?: boolean }).system === true);
36
+
37
+ for (const systemTool of systemTools) {
38
+ const installed = isToolInstalled(systemTool.name);
39
+ const updateStatus = getToolUpdateStatus(systemTool.name);
40
+
41
+ if (!installed || updateStatus.hasUpdate) {
42
+ const primarySkill = systemTool.includes.skills.find(s => s.required)?.name || systemTool.name;
43
+ installSkill(primarySkill);
44
+ }
45
+ }
46
+ }, [tools]);
47
+
48
+ // Check for updates, returns info for caller to decide what to do
49
+ const checkForUpdates = useCallback(() => {
50
+ ensureSystemTools();
51
+ const updates = getToolsWithUpdates();
52
+ setToolUpdates(updates);
53
+
54
+ const autoUpdateConfig = getAutoUpdateConfig();
55
+ return {
56
+ updates,
57
+ shouldAutoUpdate: autoUpdateConfig.tools && updates.length > 0,
58
+ };
59
+ }, [ensureSystemTools]);
60
+
61
+ // Update all tools
62
+ const updateAllTools = useCallback((updates: ToolUpdateInfo[] = toolUpdates, silent = false) => {
63
+ if (!silent) {
64
+ setIsUpdatingTools(true);
65
+ }
66
+
67
+ setTimeout(() => {
68
+ let successCount = 0;
69
+ let failCount = 0;
70
+ const updatedNames: string[] = [];
71
+
72
+ for (const tool of updates) {
73
+ const toolManifest = tools.find(t => t.name === tool.name);
74
+ if (toolManifest) {
75
+ const primarySkill = toolManifest.includes.skills.find(s => s.required)?.name || toolManifest.name;
76
+ const result = updateSkill(primarySkill);
77
+ if (result.success) {
78
+ successCount++;
79
+ updatedNames.push(tool.name);
80
+ } else {
81
+ failCount++;
82
+ }
83
+ }
84
+ }
85
+
86
+ setIsUpdatingTools(false);
87
+
88
+ if (silent && updatedNames.length > 0) {
89
+ setAutoUpdatedTools(updatedNames);
90
+ }
91
+
92
+ onUpdateComplete({ successCount, failCount, updatedNames, silent });
93
+ }, 100);
94
+ }, [toolUpdates, tools, onUpdateComplete]);
95
+
96
+ // Enable auto-update and run updates
97
+ const enableAutoUpdateAndUpdate = useCallback(() => {
98
+ setAutoUpdateConfig({ tools: true });
99
+ updateAllTools();
100
+ }, [updateAllTools]);
101
+
102
+ return {
103
+ toolUpdates,
104
+ isUpdatingTools,
105
+ autoUpdatedTools,
106
+ checkForUpdates,
107
+ updateAllTools,
108
+ enableAutoUpdateAndUpdate,
109
+ };
110
+ }
@@ -0,0 +1,4 @@
1
+ export type Tab = 'tools' | 'settings';
2
+ export type View = 'welcome' | 'tool-updates' | 'setup' | 'menu' | 'detail' | 'configure' | 'readme' | 'explorer';
3
+ export type SetupStep = 'platform' | 'user_mention' | 'auto_update' | 'confirm';
4
+ export type ComponentType = 'skill' | 'command' | 'agent';
@@ -0,0 +1,93 @@
1
+ import { Box, Text, useInput } from 'ink';
2
+ import { useState, useMemo } from 'react';
3
+ import { colors } from '../constants';
4
+ import { MarkdownLine } from '../components/Markdown';
5
+
6
+ export interface ReadmeViewerProps {
7
+ title: string;
8
+ content: string;
9
+ onClose: () => void;
10
+ }
11
+
12
+ export function ReadmeViewer({ title, content, onClose }: ReadmeViewerProps) {
13
+ const [scrollOffset, setScrollOffset] = useState(0);
14
+ const lines = useMemo(() => content.split('\n'), [content]);
15
+ const maxVisible = 20;
16
+
17
+ // Pre-compute code block state for each line
18
+ const lineStates = useMemo(() => {
19
+ const states: boolean[] = [];
20
+ let inCode = false;
21
+ for (const line of lines) {
22
+ if (line.startsWith('```')) {
23
+ states.push(false); // Delimiter itself is not "in" code block for styling
24
+ inCode = !inCode;
25
+ } else {
26
+ states.push(inCode);
27
+ }
28
+ }
29
+ return states;
30
+ }, [lines]);
31
+
32
+ // Max offset: when at end, we have top indicator + (maxVisible-1) content lines
33
+ // So max offset is lines.length - (maxVisible - 1) = lines.length - maxVisible + 1
34
+ const maxOffset = Math.max(0, lines.length - maxVisible + 1);
35
+
36
+ useInput((input, key) => {
37
+ if (key.escape) {
38
+ onClose();
39
+ return;
40
+ }
41
+ if (key.upArrow) {
42
+ setScrollOffset((prev) => Math.max(0, prev - 1));
43
+ }
44
+ if (key.downArrow) {
45
+ setScrollOffset((prev) => Math.min(maxOffset, prev + 1));
46
+ }
47
+ if (key.pageDown || input === ' ') {
48
+ setScrollOffset((prev) => Math.min(maxOffset, prev + maxVisible));
49
+ }
50
+ if (key.pageUp) {
51
+ setScrollOffset((prev) => Math.max(0, prev - maxVisible));
52
+ }
53
+ });
54
+
55
+ // Adjust visible lines based on whether indicators are shown
56
+ const showTopIndicator = scrollOffset > 0;
57
+ // Reserve space for bottom indicator if not at end
58
+ const contentLines = maxVisible - (showTopIndicator ? 1 : 0);
59
+ const endIndex = Math.min(scrollOffset + contentLines, lines.length);
60
+ const showBottomIndicator = endIndex < lines.length;
61
+ const actualContentLines = contentLines - (showBottomIndicator ? 1 : 0);
62
+ const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
63
+
64
+ return (
65
+ <Box flexDirection="column" padding={1}>
66
+ <Box marginBottom={1}>
67
+ <Text color={colors.text} bold>{title}</Text>
68
+ <Text color={colors.textDim}> · {lines.length} lines</Text>
69
+ </Box>
70
+
71
+ <Box
72
+ flexDirection="column"
73
+ borderStyle="single"
74
+ borderColor={colors.border}
75
+ paddingX={1}
76
+ >
77
+ {showTopIndicator && (
78
+ <Text color={colors.textDim}>↑ {scrollOffset} more lines</Text>
79
+ )}
80
+ {visibleLines.map((line, i) => (
81
+ <MarkdownLine key={scrollOffset + i} line={line} inCodeBlock={lineStates[scrollOffset + i]} />
82
+ ))}
83
+ {showBottomIndicator && (
84
+ <Text color={colors.textDim}>↓ {lines.length - scrollOffset - actualContentLines} more lines</Text>
85
+ )}
86
+ </Box>
87
+
88
+ <Box marginTop={1}>
89
+ <Text color={colors.textDim}>↑↓ scroll · space/pgdn page · esc back</Text>
90
+ </Box>
91
+ </Box>
92
+ );
93
+ }
@@ -0,0 +1,242 @@
1
+ import { Box, Text, useInput } from 'ink';
2
+ import TextInput from 'ink-text-input';
3
+ import { useState } from 'react';
4
+ import { colors } from '../constants';
5
+ import type { SetupStep } from '../types';
6
+ import { Platform, BuiltInOutput, type DroidConfig } from '../../../lib/types';
7
+ import { loadConfig, saveConfig, getAutoUpdateConfig, setAutoUpdateConfig } from '../../../lib/config';
8
+ import { configurePlatformPermissions } from '../../setup';
9
+
10
+ export interface SetupScreenProps {
11
+ onComplete: () => void;
12
+ onSkip: () => void;
13
+ initialConfig?: DroidConfig;
14
+ }
15
+
16
+ const platformOptions = [
17
+ { label: 'Claude Code', value: Platform.ClaudeCode },
18
+ { label: 'OpenCode', value: Platform.OpenCode },
19
+ ];
20
+
21
+ export function SetupScreen({ onComplete, onSkip, initialConfig }: SetupScreenProps) {
22
+ const [step, setStep] = useState<SetupStep>('platform');
23
+ const [platform, setPlatform] = useState<Platform>(
24
+ initialConfig?.platform || Platform.ClaudeCode
25
+ );
26
+ const [userMention, setUserMention] = useState(initialConfig?.user_mention || '@user');
27
+ const [selectedIndex, setSelectedIndex] = useState(0);
28
+ const [error, setError] = useState<string | null>(null);
29
+
30
+ // Auto-update state
31
+ const existingAutoUpdate = getAutoUpdateConfig();
32
+ const [autoUpdateTools, setAutoUpdateTools] = useState(existingAutoUpdate.tools);
33
+ const [autoUpdateApp, setAutoUpdateApp] = useState(existingAutoUpdate.app);
34
+ const [autoUpdateSelectedIndex, setAutoUpdateSelectedIndex] = useState(0);
35
+
36
+ const steps: SetupStep[] = ['platform', 'user_mention', 'auto_update', 'confirm'];
37
+ const stepIndex = steps.indexOf(step);
38
+ const totalSteps = steps.length - 1; // Don't count confirm as a step
39
+
40
+ const handleUserMentionSubmit = () => {
41
+ // Auto-add @ prefix if missing
42
+ const mention = userMention.startsWith('@') ? userMention : `@${userMention}`;
43
+ setUserMention(mention);
44
+ setError(null);
45
+ setStep('auto_update');
46
+ setAutoUpdateSelectedIndex(0);
47
+ };
48
+
49
+ // Handle escape during text input (only intercept escape, nothing else)
50
+ useInput((input, key) => {
51
+ if (key.escape) {
52
+ setStep('platform');
53
+ setSelectedIndex(0);
54
+ }
55
+ }, { isActive: step === 'user_mention' });
56
+
57
+ // Handle all input for non-text-input steps
58
+ useInput((input, key) => {
59
+ if (key.escape) {
60
+ if (step === 'platform') {
61
+ onSkip();
62
+ } else if (step === 'auto_update') {
63
+ setStep('user_mention');
64
+ } else if (step === 'confirm') {
65
+ setStep('auto_update');
66
+ setAutoUpdateSelectedIndex(0);
67
+ }
68
+ return;
69
+ }
70
+
71
+ if (step === 'platform') {
72
+ if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
73
+ if (key.downArrow) setSelectedIndex((prev) => Math.min(platformOptions.length - 1, prev + 1));
74
+ if (key.return) {
75
+ setPlatform(platformOptions[selectedIndex].value);
76
+ setStep('user_mention');
77
+ }
78
+ } else if (step === 'auto_update') {
79
+ // 0 = auto-update tools, 1 = auto-update app, 2 = next button
80
+ if (key.upArrow) setAutoUpdateSelectedIndex((prev) => Math.max(0, prev - 1));
81
+ if (key.downArrow) setAutoUpdateSelectedIndex((prev) => Math.min(2, prev + 1));
82
+ if (key.return) {
83
+ if (autoUpdateSelectedIndex === 0) {
84
+ setAutoUpdateTools(!autoUpdateTools);
85
+ } else if (autoUpdateSelectedIndex === 1) {
86
+ setAutoUpdateApp(!autoUpdateApp);
87
+ } else {
88
+ setStep('confirm');
89
+ }
90
+ }
91
+ } else if (step === 'confirm') {
92
+ if (key.return) {
93
+ const existingConfig = loadConfig();
94
+ const config: DroidConfig = {
95
+ ...existingConfig,
96
+ platform: platform,
97
+ user_mention: userMention,
98
+ output_preference: BuiltInOutput.Terminal, // Default to terminal
99
+ };
100
+ saveConfig(config);
101
+ setAutoUpdateConfig({ tools: autoUpdateTools, app: autoUpdateApp });
102
+ configurePlatformPermissions(platform);
103
+ onComplete();
104
+ }
105
+ }
106
+ }, { isActive: step !== 'user_mention' });
107
+
108
+ const renderHeader = () => (
109
+ <Box flexDirection="column" marginBottom={1}>
110
+ <Text>
111
+ <Text color={colors.textDim}>[</Text>
112
+ <Text color={colors.primary}>●</Text>
113
+ <Text color={colors.textDim}> </Text>
114
+ <Text color={colors.primary}>●</Text>
115
+ <Text color={colors.textDim}>] </Text>
116
+ <Text color={colors.text} bold>droid setup</Text>
117
+ <Text color={colors.textDim}> · Step {Math.min(stepIndex + 1, totalSteps)} of {totalSteps}</Text>
118
+ </Text>
119
+ </Box>
120
+ );
121
+
122
+ if (step === 'platform') {
123
+ return (
124
+ <Box flexDirection="column" padding={1}>
125
+ {renderHeader()}
126
+ <Text color={colors.text}>Which platform are you using?</Text>
127
+ <Box flexDirection="column" marginTop={1}>
128
+ {platformOptions.map((option, index) => (
129
+ <Text key={option.value}>
130
+ <Text color={colors.textDim}>{index === selectedIndex ? '>' : ' '} </Text>
131
+ <Text color={index === selectedIndex ? colors.text : colors.textMuted}>{option.label}</Text>
132
+ </Text>
133
+ ))}
134
+ </Box>
135
+ <Box marginTop={1}>
136
+ <Text color={colors.textDim}>↑↓ select · enter next · esc skip</Text>
137
+ </Box>
138
+ </Box>
139
+ );
140
+ }
141
+
142
+ if (step === 'user_mention') {
143
+ return (
144
+ <Box flexDirection="column" padding={1}>
145
+ {renderHeader()}
146
+ <Text color={colors.text}>What @mention should be used for you?</Text>
147
+ <Box marginTop={1}>
148
+ <Text color={colors.textDim}>{'> '}</Text>
149
+ <TextInput
150
+ value={userMention}
151
+ onChange={setUserMention}
152
+ onSubmit={handleUserMentionSubmit}
153
+ placeholder="@user"
154
+ />
155
+ </Box>
156
+ {error && (
157
+ <Box marginTop={1}>
158
+ <Text color={colors.error}>{error}</Text>
159
+ </Box>
160
+ )}
161
+ <Box marginTop={1}>
162
+ <Text color={colors.textDim}>enter next · esc back</Text>
163
+ </Box>
164
+ </Box>
165
+ );
166
+ }
167
+
168
+ if (step === 'auto_update') {
169
+ return (
170
+ <Box flexDirection="column" padding={1}>
171
+ {renderHeader()}
172
+ <Text color={colors.text}>Configure auto-update behaviour</Text>
173
+ <Box flexDirection="column" marginTop={1}>
174
+ <Box>
175
+ <Text>
176
+ <Text color={colors.textDim}>{autoUpdateSelectedIndex === 0 ? '>' : ' '} </Text>
177
+ <Text color={autoUpdateSelectedIndex === 0 ? colors.text : colors.textMuted}>
178
+ [{autoUpdateTools ? 'x' : ' '}] Auto-update tools
179
+ </Text>
180
+ </Text>
181
+ </Box>
182
+ <Text color={colors.textDim}> Update tools automatically when droid starts</Text>
183
+ <Box marginTop={1}>
184
+ <Text>
185
+ <Text color={colors.textDim}>{autoUpdateSelectedIndex === 1 ? '>' : ' '} </Text>
186
+ <Text color={autoUpdateSelectedIndex === 1 ? colors.text : colors.textMuted}>
187
+ [{autoUpdateApp ? 'x' : ' '}] Auto-update app
188
+ </Text>
189
+ </Text>
190
+ </Box>
191
+ <Text color={colors.textDim}> Update droid automatically when a new version is available</Text>
192
+ </Box>
193
+ <Box marginTop={2}>
194
+ <Text
195
+ backgroundColor={autoUpdateSelectedIndex === 2 ? colors.primary : colors.bgSelected}
196
+ color={autoUpdateSelectedIndex === 2 ? '#ffffff' : colors.textMuted}
197
+ bold={autoUpdateSelectedIndex === 2}
198
+ >
199
+ {' '}Next{' '}
200
+ </Text>
201
+ </Box>
202
+ <Box marginTop={1}>
203
+ <Text color={colors.textDim}>↑↓ select · enter toggle/next · esc back</Text>
204
+ </Box>
205
+ </Box>
206
+ );
207
+ }
208
+
209
+ // Confirm step
210
+ return (
211
+ <Box flexDirection="column" padding={1}>
212
+ {renderHeader()}
213
+ <Text color={colors.text} bold>Review your settings</Text>
214
+ <Box flexDirection="column" marginTop={1}>
215
+ <Text>
216
+ <Text color={colors.textDim}>Platform: </Text>
217
+ <Text color={colors.text}>{platform === Platform.ClaudeCode ? 'Claude Code' : 'OpenCode'}</Text>
218
+ </Text>
219
+ <Text>
220
+ <Text color={colors.textDim}>Your @mention: </Text>
221
+ <Text color={colors.text}>{userMention}</Text>
222
+ </Text>
223
+ <Text>
224
+ <Text color={colors.textDim}>Auto-update tools: </Text>
225
+ <Text color={colors.text}>{autoUpdateTools ? 'enabled' : 'disabled'}</Text>
226
+ </Text>
227
+ <Text>
228
+ <Text color={colors.textDim}>Auto-update app: </Text>
229
+ <Text color={colors.text}>{autoUpdateApp ? 'enabled' : 'disabled'}</Text>
230
+ </Text>
231
+ </Box>
232
+ <Box marginTop={2}>
233
+ <Text backgroundColor={colors.primary} color="#ffffff" bold>
234
+ {' '}Save{' '}
235
+ </Text>
236
+ </Box>
237
+ <Box marginTop={1}>
238
+ <Text color={colors.textDim}>enter save · esc back</Text>
239
+ </Box>
240
+ </Box>
241
+ );
242
+ }