@orderful/droid 0.16.0 → 0.17.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 +3 -43
- package/AGENTS.md +75 -0
- package/CHANGELOG.md +18 -0
- package/bun.lock +12 -0
- package/dist/bin/droid.js +3064 -54
- package/dist/commands/tui/components/Badge.test.d.ts +2 -0
- package/dist/commands/tui/components/Badge.test.d.ts.map +1 -0
- package/dist/commands/tui/components/TabBar.test.d.ts +2 -0
- package/dist/commands/tui/components/TabBar.test.d.ts.map +1 -0
- package/dist/index.js +952 -6
- package/package.json +7 -2
- package/playwright.config.ts +20 -0
- package/scripts/build.ts +78 -0
- package/scripts/screenshot-tui.ts +126 -0
- package/src/commands/tui/components/Badge.test.tsx +94 -0
- package/src/commands/tui/components/TabBar.test.tsx +39 -0
- package/tests/e2e/tui.spec.ts +128 -0
- package/dist/bin/droid.js.map +0 -1
- package/dist/commands/config.js +0 -67
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/install.js +0 -45
- package/dist/commands/install.js.map +0 -1
- package/dist/commands/setup.js +0 -269
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/skills.js +0 -144
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/tui/components/Badge.js +0 -29
- package/dist/commands/tui/components/Badge.js.map +0 -1
- package/dist/commands/tui/components/Markdown.js +0 -42
- package/dist/commands/tui/components/Markdown.js.map +0 -1
- package/dist/commands/tui/components/SettingsDetails.js +0 -11
- package/dist/commands/tui/components/SettingsDetails.js.map +0 -1
- package/dist/commands/tui/components/TabBar.js +0 -7
- package/dist/commands/tui/components/TabBar.js.map +0 -1
- package/dist/commands/tui/components/ToolDetails.js +0 -35
- package/dist/commands/tui/components/ToolDetails.js.map +0 -1
- package/dist/commands/tui/components/ToolItem.js +0 -11
- package/dist/commands/tui/components/ToolItem.js.map +0 -1
- package/dist/commands/tui/constants.js +0 -17
- package/dist/commands/tui/constants.js.map +0 -1
- package/dist/commands/tui/hooks/useAppUpdate.js +0 -52
- package/dist/commands/tui/hooks/useAppUpdate.js.map +0 -1
- package/dist/commands/tui/hooks/useToolUpdates.js +0 -77
- package/dist/commands/tui/hooks/useToolUpdates.js.map +0 -1
- package/dist/commands/tui/types.js +0 -2
- package/dist/commands/tui/types.js.map +0 -1
- package/dist/commands/tui/views/ReadmeViewer.js +0 -56
- package/dist/commands/tui/views/ReadmeViewer.js.map +0 -1
- package/dist/commands/tui/views/SetupScreen.js +0 -114
- package/dist/commands/tui/views/SetupScreen.js.map +0 -1
- package/dist/commands/tui/views/SkillConfigScreen.js +0 -148
- package/dist/commands/tui/views/SkillConfigScreen.js.map +0 -1
- package/dist/commands/tui/views/ToolExplorer.js +0 -86
- package/dist/commands/tui/views/ToolExplorer.js.map +0 -1
- package/dist/commands/tui/views/ToolUpdatePrompt.js +0 -38
- package/dist/commands/tui/views/ToolUpdatePrompt.js.map +0 -1
- package/dist/commands/tui/views/WelcomeScreen.js +0 -46
- package/dist/commands/tui/views/WelcomeScreen.js.map +0 -1
- package/dist/commands/tui.js +0 -307
- package/dist/commands/tui.js.map +0 -1
- package/dist/commands/uninstall.js +0 -26
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.js +0 -45
- package/dist/commands/update.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/agents.js +0 -248
- package/dist/lib/agents.js.map +0 -1
- package/dist/lib/config.js +0 -196
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/platforms.js +0 -52
- package/dist/lib/platforms.js.map +0 -1
- package/dist/lib/quotes.js +0 -24
- package/dist/lib/quotes.js.map +0 -1
- package/dist/lib/skill-config.js +0 -80
- package/dist/lib/skill-config.js.map +0 -1
- package/dist/lib/skills.js +0 -582
- package/dist/lib/skills.js.map +0 -1
- package/dist/lib/tools.js +0 -145
- package/dist/lib/tools.js.map +0 -1
- package/dist/lib/types.js +0 -50
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/version.js +0 -100
- package/dist/lib/version.js.map +0 -1
package/dist/bin/droid.js
CHANGED
|
@@ -1,57 +1,3067 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
|
|
3
|
+
// src/bin/droid.ts
|
|
4
|
+
import { program } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/setup.ts
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
import chalk2 from "chalk";
|
|
9
|
+
import { execSync as execSync2 } from "child_process";
|
|
10
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
11
|
+
import { join as join7 } from "path";
|
|
12
|
+
import { homedir as homedir3 } from "os";
|
|
13
|
+
|
|
14
|
+
// src/lib/config.ts
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
import YAML from "yaml";
|
|
19
|
+
|
|
20
|
+
// src/lib/types.ts
|
|
21
|
+
function getPlatformTools(config) {
|
|
22
|
+
return config.platforms[config.platform]?.tools ?? {};
|
|
23
|
+
}
|
|
24
|
+
function setPlatformTools(config, tools) {
|
|
25
|
+
if (!config.platforms[config.platform]) {
|
|
26
|
+
config.platforms[config.platform] = { tools: {} };
|
|
27
|
+
}
|
|
28
|
+
config.platforms[config.platform].tools = tools;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/lib/config.ts
|
|
32
|
+
var CONFIG_DIR = join(homedir(), ".droid");
|
|
33
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.yaml");
|
|
34
|
+
var DEFAULT_AUTO_UPDATE = {
|
|
35
|
+
app: false,
|
|
36
|
+
// Opt-in: user must enable
|
|
37
|
+
tools: true
|
|
38
|
+
// Opt-out: enabled by default (tools only update when app updates)
|
|
39
|
+
};
|
|
40
|
+
var DEFAULT_CONFIG = {
|
|
41
|
+
platform: "claude-code" /* ClaudeCode */,
|
|
42
|
+
user_mention: "@user",
|
|
43
|
+
output_preference: "terminal" /* Terminal */,
|
|
44
|
+
git_username: "",
|
|
45
|
+
platforms: {}
|
|
46
|
+
};
|
|
47
|
+
function migrateConfig(config) {
|
|
48
|
+
if ("ai_tool" in config && !("platform" in config)) {
|
|
49
|
+
const legacyConfig = config;
|
|
50
|
+
return {
|
|
51
|
+
platform: legacyConfig.ai_tool,
|
|
52
|
+
user_mention: legacyConfig.user_mention ?? DEFAULT_CONFIG.user_mention,
|
|
53
|
+
output_preference: legacyConfig.output_preference ?? DEFAULT_CONFIG.output_preference,
|
|
54
|
+
git_username: legacyConfig.git_username ?? DEFAULT_CONFIG.git_username,
|
|
55
|
+
platforms: {
|
|
56
|
+
[legacyConfig.ai_tool]: {
|
|
57
|
+
tools: legacyConfig.skills ?? {}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...DEFAULT_CONFIG,
|
|
64
|
+
...config,
|
|
65
|
+
platforms: config.platforms ?? {}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function ensureConfigDir() {
|
|
69
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
70
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function configExists() {
|
|
74
|
+
return existsSync(CONFIG_FILE);
|
|
75
|
+
}
|
|
76
|
+
function loadConfig() {
|
|
77
|
+
ensureConfigDir();
|
|
78
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
79
|
+
return { ...DEFAULT_CONFIG };
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
83
|
+
const rawConfig = YAML.parse(content);
|
|
84
|
+
const needsMigration = "ai_tool" in rawConfig && !("platform" in rawConfig);
|
|
85
|
+
const config = migrateConfig(rawConfig);
|
|
86
|
+
if (needsMigration) {
|
|
87
|
+
saveConfig(config);
|
|
88
|
+
}
|
|
89
|
+
return config;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
92
|
+
console.error(`Error reading config: ${message}`);
|
|
93
|
+
return { ...DEFAULT_CONFIG };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function saveConfig(config) {
|
|
97
|
+
ensureConfigDir();
|
|
98
|
+
const content = YAML.stringify(config, { indent: 2 });
|
|
99
|
+
writeFileSync(CONFIG_FILE, content, "utf-8");
|
|
100
|
+
}
|
|
101
|
+
function getConfigValue(key) {
|
|
102
|
+
const config = loadConfig();
|
|
103
|
+
const keys = key.split(".");
|
|
104
|
+
let value = config;
|
|
105
|
+
for (const k of keys) {
|
|
106
|
+
if (value === void 0 || value === null || typeof value !== "object") {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
value = value[k];
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function setConfigValue(key, value) {
|
|
114
|
+
const config = loadConfig();
|
|
115
|
+
const keys = key.split(".");
|
|
116
|
+
let current = config;
|
|
117
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
118
|
+
if (current[keys[i]] === void 0) {
|
|
119
|
+
current[keys[i]] = {};
|
|
120
|
+
}
|
|
121
|
+
current = current[keys[i]];
|
|
122
|
+
}
|
|
123
|
+
current[keys[keys.length - 1]] = value;
|
|
124
|
+
saveConfig(config);
|
|
125
|
+
}
|
|
126
|
+
function getConfigPath() {
|
|
127
|
+
return CONFIG_FILE;
|
|
128
|
+
}
|
|
129
|
+
function getSkillOverridesPath(skillName) {
|
|
130
|
+
return join(CONFIG_DIR, "skills", skillName, "overrides.yaml");
|
|
131
|
+
}
|
|
132
|
+
function loadSkillOverrides(skillName) {
|
|
133
|
+
const overridesPath = getSkillOverridesPath(skillName);
|
|
134
|
+
if (!existsSync(overridesPath)) {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const content = readFileSync(overridesPath, "utf-8");
|
|
139
|
+
return YAML.parse(content) || {};
|
|
140
|
+
} catch {
|
|
141
|
+
return {};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function saveSkillOverrides(skillName, overrides) {
|
|
145
|
+
const overridesPath = getSkillOverridesPath(skillName);
|
|
146
|
+
const skillDir = join(CONFIG_DIR, "skills", skillName);
|
|
147
|
+
if (!existsSync(skillDir)) {
|
|
148
|
+
mkdirSync(skillDir, { recursive: true });
|
|
149
|
+
}
|
|
150
|
+
const content = YAML.stringify(overrides, { indent: 2 });
|
|
151
|
+
writeFileSync(overridesPath, content, "utf-8");
|
|
152
|
+
}
|
|
153
|
+
function getAutoUpdateConfig() {
|
|
154
|
+
const config = loadConfig();
|
|
155
|
+
return {
|
|
156
|
+
...DEFAULT_AUTO_UPDATE,
|
|
157
|
+
...config.auto_update
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function setAutoUpdateConfig(updates) {
|
|
161
|
+
const config = loadConfig();
|
|
162
|
+
config.auto_update = {
|
|
163
|
+
...DEFAULT_AUTO_UPDATE,
|
|
164
|
+
...config.auto_update,
|
|
165
|
+
...updates
|
|
166
|
+
};
|
|
167
|
+
saveConfig(config);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/lib/skills.ts
|
|
171
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, rmSync } from "fs";
|
|
172
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
173
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
174
|
+
import YAML4 from "yaml";
|
|
175
|
+
|
|
176
|
+
// src/lib/agents.ts
|
|
177
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2 } from "fs";
|
|
178
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
179
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
180
|
+
import YAML3 from "yaml";
|
|
181
|
+
|
|
182
|
+
// src/lib/platforms.ts
|
|
183
|
+
import { join as join2 } from "path";
|
|
184
|
+
import { homedir as homedir2 } from "os";
|
|
185
|
+
var PLATFORM_PATHS = {
|
|
186
|
+
["claude-code" /* ClaudeCode */]: {
|
|
187
|
+
skills: join2(homedir2(), ".claude", "skills"),
|
|
188
|
+
commands: join2(homedir2(), ".claude", "commands"),
|
|
189
|
+
agents: join2(homedir2(), ".claude", "agents"),
|
|
190
|
+
config: join2(homedir2(), ".claude", "CLAUDE.md")
|
|
191
|
+
},
|
|
192
|
+
["opencode" /* OpenCode */]: {
|
|
193
|
+
skills: join2(homedir2(), ".config", "opencode", "skills"),
|
|
194
|
+
commands: join2(homedir2(), ".config", "opencode", "command"),
|
|
195
|
+
agents: join2(homedir2(), ".config", "opencode", "agent"),
|
|
196
|
+
config: join2(homedir2(), ".config", "opencode", "AGENTS.md")
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function getSkillsPath(platform) {
|
|
200
|
+
return PLATFORM_PATHS[platform].skills;
|
|
201
|
+
}
|
|
202
|
+
function getCommandsPath(platform) {
|
|
203
|
+
return PLATFORM_PATHS[platform].commands;
|
|
204
|
+
}
|
|
205
|
+
function getAgentsPath(platform) {
|
|
206
|
+
return PLATFORM_PATHS[platform].agents;
|
|
207
|
+
}
|
|
208
|
+
function getConfigPath2(platform) {
|
|
209
|
+
return PLATFORM_PATHS[platform].config;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/lib/tools.ts
|
|
213
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
214
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
215
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
216
|
+
import YAML2 from "yaml";
|
|
217
|
+
|
|
218
|
+
// src/lib/version.ts
|
|
219
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
220
|
+
import { fileURLToPath } from "url";
|
|
221
|
+
import { dirname, join as join3 } from "path";
|
|
222
|
+
import { execSync } from "child_process";
|
|
223
|
+
import chalk from "chalk";
|
|
224
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
225
|
+
var packageJsonPath = join3(__dirname, "../../package.json");
|
|
226
|
+
function getVersion() {
|
|
227
|
+
try {
|
|
228
|
+
const pkg = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
229
|
+
return pkg.version;
|
|
230
|
+
} catch {
|
|
231
|
+
return "0.0.0";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function compareSemver(a, b) {
|
|
235
|
+
const partsA = a.split(".").map(Number);
|
|
236
|
+
const partsB = b.split(".").map(Number);
|
|
237
|
+
for (let i = 0; i < 3; i++) {
|
|
238
|
+
const numA = partsA[i] || 0;
|
|
239
|
+
const numB = partsB[i] || 0;
|
|
240
|
+
if (numA > numB) return 1;
|
|
241
|
+
if (numA < numB) return -1;
|
|
242
|
+
}
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
function getUpdateInfo() {
|
|
246
|
+
const currentVersion = getVersion();
|
|
247
|
+
try {
|
|
248
|
+
const latestVersion = execSync("npm view @orderful/droid version 2>/dev/null", {
|
|
249
|
+
encoding: "utf-8",
|
|
250
|
+
timeout: 3e3
|
|
251
|
+
}).trim();
|
|
252
|
+
return {
|
|
253
|
+
hasUpdate: latestVersion ? compareSemver(latestVersion, currentVersion) > 0 : false,
|
|
254
|
+
currentVersion,
|
|
255
|
+
latestVersion: latestVersion || null
|
|
256
|
+
};
|
|
257
|
+
} catch {
|
|
258
|
+
return {
|
|
259
|
+
hasUpdate: false,
|
|
260
|
+
currentVersion,
|
|
261
|
+
latestVersion: null
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function runUpdate() {
|
|
266
|
+
try {
|
|
267
|
+
execSync("npm install -g @orderful/droid@latest", {
|
|
268
|
+
encoding: "utf-8",
|
|
269
|
+
stdio: "pipe",
|
|
270
|
+
timeout: 6e4
|
|
271
|
+
});
|
|
272
|
+
return { success: true, message: "Update complete!" };
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return { success: false, message: `Update failed: ${error}` };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/lib/tools.ts
|
|
279
|
+
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
280
|
+
var BUNDLED_TOOLS_DIR = join4(__dirname2, "../tools");
|
|
281
|
+
function getBundledToolsDir() {
|
|
282
|
+
return BUNDLED_TOOLS_DIR;
|
|
283
|
+
}
|
|
284
|
+
function loadToolManifest(toolDir) {
|
|
285
|
+
const manifestPath = join4(toolDir, "TOOL.yaml");
|
|
286
|
+
if (!existsSync2(manifestPath)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
const content = readFileSync3(manifestPath, "utf-8");
|
|
291
|
+
const parsed = YAML2.parse(content);
|
|
292
|
+
const includes = {
|
|
293
|
+
skills: parsed.includes?.skills || [],
|
|
294
|
+
commands: parsed.includes?.commands || [],
|
|
295
|
+
agents: parsed.includes?.agents || []
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
...parsed,
|
|
299
|
+
includes
|
|
300
|
+
};
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function getBundledTools() {
|
|
306
|
+
if (!existsSync2(BUNDLED_TOOLS_DIR)) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
const toolDirs = readdirSync(BUNDLED_TOOLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
310
|
+
const tools = [];
|
|
311
|
+
for (const toolName of toolDirs) {
|
|
312
|
+
const manifest = loadToolManifest(join4(BUNDLED_TOOLS_DIR, toolName));
|
|
313
|
+
if (manifest) {
|
|
314
|
+
tools.push(manifest);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return tools;
|
|
318
|
+
}
|
|
319
|
+
function isToolInstalled(toolName) {
|
|
320
|
+
const config = loadConfig();
|
|
321
|
+
const installedTools = getPlatformTools(config);
|
|
322
|
+
const tool = getBundledTools().find((t) => t.name === toolName);
|
|
323
|
+
if (!tool) return false;
|
|
324
|
+
const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
|
|
325
|
+
return requiredSkills.some((skillName) => skillName in installedTools);
|
|
326
|
+
}
|
|
327
|
+
function getInstalledToolVersion(toolName) {
|
|
328
|
+
const config = loadConfig();
|
|
329
|
+
const installedTools = getPlatformTools(config);
|
|
330
|
+
const tool = getBundledTools().find((t) => t.name === toolName);
|
|
331
|
+
if (!tool) return null;
|
|
332
|
+
const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
|
|
333
|
+
for (const skillName of requiredSkills) {
|
|
334
|
+
if (installedTools[skillName]) {
|
|
335
|
+
return installedTools[skillName].version;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
function getToolUpdateStatus(toolName) {
|
|
341
|
+
const installedVersion = getInstalledToolVersion(toolName);
|
|
342
|
+
const tool = getBundledTools().find((t) => t.name === toolName);
|
|
343
|
+
const bundledVersion = tool?.version || null;
|
|
344
|
+
if (!installedVersion || !bundledVersion) {
|
|
345
|
+
return {
|
|
346
|
+
name: toolName,
|
|
347
|
+
hasUpdate: false,
|
|
348
|
+
installedVersion,
|
|
349
|
+
bundledVersion
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const hasUpdate = compareSemver(bundledVersion, installedVersion) > 0;
|
|
353
|
+
return {
|
|
354
|
+
name: toolName,
|
|
355
|
+
hasUpdate,
|
|
356
|
+
installedVersion,
|
|
357
|
+
bundledVersion
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function getToolsWithUpdates() {
|
|
361
|
+
const config = loadConfig();
|
|
362
|
+
const installedTools = getPlatformTools(config);
|
|
363
|
+
const bundledTools = getBundledTools();
|
|
364
|
+
const toolsWithUpdates = [];
|
|
365
|
+
for (const tool of bundledTools) {
|
|
366
|
+
const requiredSkills = tool.includes.skills.filter((s) => s.required).map((s) => s.name);
|
|
367
|
+
const isInstalled = requiredSkills.some((skillName) => skillName in installedTools);
|
|
368
|
+
if (isInstalled) {
|
|
369
|
+
const updateStatus = getToolUpdateStatus(tool.name);
|
|
370
|
+
if (updateStatus.hasUpdate) {
|
|
371
|
+
toolsWithUpdates.push(updateStatus);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return toolsWithUpdates;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/lib/agents.ts
|
|
379
|
+
var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
380
|
+
var BUNDLED_TOOLS_DIR2 = join5(__dirname3, "../tools");
|
|
381
|
+
function getAgentsInstallPath(platform) {
|
|
382
|
+
return getAgentsPath(platform);
|
|
383
|
+
}
|
|
384
|
+
function parseAgentFrontmatter(content) {
|
|
385
|
+
const trimmed = content.trimStart();
|
|
386
|
+
if (!trimmed.startsWith("---")) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const endMatch = trimmed.slice(3).indexOf("---");
|
|
390
|
+
if (endMatch === -1) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const frontmatterContent = trimmed.slice(3, 3 + endMatch);
|
|
394
|
+
try {
|
|
395
|
+
return YAML3.parse(frontmatterContent);
|
|
396
|
+
} catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function loadAgentManifest(agentPath) {
|
|
401
|
+
if (!existsSync3(agentPath)) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
const content = readFileSync4(agentPath, "utf-8");
|
|
405
|
+
const frontmatter = parseAgentFrontmatter(content);
|
|
406
|
+
if (!frontmatter || !frontmatter.name) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const toolDir = dirname3(dirname3(agentPath));
|
|
410
|
+
const toolManifest = loadToolManifest(toolDir);
|
|
411
|
+
return {
|
|
412
|
+
name: frontmatter.name,
|
|
413
|
+
description: frontmatter.description || "",
|
|
414
|
+
version: toolManifest?.version || "0.0.0",
|
|
415
|
+
status: toolManifest?.status,
|
|
416
|
+
mode: frontmatter.mode,
|
|
417
|
+
model: frontmatter.model,
|
|
418
|
+
color: frontmatter.color,
|
|
419
|
+
tools: frontmatter.tools
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function getInstalledAgentsDir() {
|
|
423
|
+
const config = loadConfig();
|
|
424
|
+
return getAgentsInstallPath(config.platform);
|
|
425
|
+
}
|
|
426
|
+
function isAgentInstalled(agentName) {
|
|
427
|
+
const config = loadConfig();
|
|
428
|
+
const agentsDir = getAgentsInstallPath(config.platform);
|
|
429
|
+
const agentPath = join5(agentsDir, `${agentName}.md`);
|
|
430
|
+
return existsSync3(agentPath);
|
|
431
|
+
}
|
|
432
|
+
function generateClaudeCodeAgent(manifest, agentContent) {
|
|
433
|
+
const lines = [
|
|
434
|
+
"---",
|
|
435
|
+
`name: ${manifest.name}`,
|
|
436
|
+
`description: ${manifest.description}`
|
|
437
|
+
];
|
|
438
|
+
if (manifest.tools && manifest.tools.length > 0) {
|
|
439
|
+
lines.push(`tools: ${manifest.tools.join(", ")}`);
|
|
440
|
+
}
|
|
441
|
+
if (manifest.color) {
|
|
442
|
+
lines.push(`color: ${manifest.color}`);
|
|
443
|
+
}
|
|
444
|
+
lines.push("---", "", agentContent.trim(), "");
|
|
445
|
+
return lines.join("\n");
|
|
446
|
+
}
|
|
447
|
+
function generateOpenCodeAgent(manifest, agentContent) {
|
|
448
|
+
const lines = [
|
|
449
|
+
"---",
|
|
450
|
+
`description: ${manifest.description}`,
|
|
451
|
+
`mode: ${manifest.mode || "subagent"}`
|
|
452
|
+
];
|
|
453
|
+
if (manifest.tools && manifest.tools.length > 0) {
|
|
454
|
+
lines.push("tools:");
|
|
455
|
+
for (const tool of manifest.tools) {
|
|
456
|
+
const toolName = tool.toLowerCase();
|
|
457
|
+
lines.push(` ${toolName}: true`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
lines.push("---", "", agentContent.trim(), "");
|
|
461
|
+
return lines.join("\n");
|
|
462
|
+
}
|
|
463
|
+
function installAgentFromPath(agentPath, agentName) {
|
|
464
|
+
const config = loadConfig();
|
|
465
|
+
const manifest = loadAgentManifest(agentPath);
|
|
466
|
+
if (!manifest) {
|
|
467
|
+
return { success: false, message: `Agent manifest not found: ${agentName}` };
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
const rawContent = readFileSync4(agentPath, "utf-8");
|
|
471
|
+
const frontmatterMatch = rawContent.match(/^---\n[\s\S]*?\n---\n?/);
|
|
472
|
+
const agentContent = frontmatterMatch ? rawContent.slice(frontmatterMatch[0].length) : rawContent;
|
|
473
|
+
const installedContent = config.platform === "claude-code" /* ClaudeCode */ ? generateClaudeCodeAgent(manifest, agentContent) : generateOpenCodeAgent(manifest, agentContent);
|
|
474
|
+
const agentsDir = getAgentsInstallPath(config.platform);
|
|
475
|
+
if (!existsSync3(agentsDir)) {
|
|
476
|
+
mkdirSync2(agentsDir, { recursive: true });
|
|
477
|
+
}
|
|
478
|
+
const outputPath = join5(agentsDir, `${agentName}.md`);
|
|
479
|
+
writeFileSync2(outputPath, installedContent);
|
|
480
|
+
const targetDir = config.platform === "claude-code" /* ClaudeCode */ ? "~/.claude/agents/" : "~/.config/opencode/agent/";
|
|
481
|
+
return { success: true, message: `Installed ${agentName} to ${targetDir}` };
|
|
482
|
+
} catch (error) {
|
|
483
|
+
return { success: false, message: `Failed to install agent: ${error}` };
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function uninstallAgent(agentName) {
|
|
487
|
+
const config = loadConfig();
|
|
488
|
+
const agentsDir = getAgentsInstallPath(config.platform);
|
|
489
|
+
const agentPath = join5(agentsDir, `${agentName}.md`);
|
|
490
|
+
if (!existsSync3(agentPath)) {
|
|
491
|
+
return { success: false, message: `Agent not installed: ${agentName}` };
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
unlinkSync(agentPath);
|
|
495
|
+
return { success: true, message: `Uninstalled ${agentName}` };
|
|
496
|
+
} catch (error) {
|
|
497
|
+
return { success: false, message: `Failed to uninstall agent: ${error}` };
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/lib/skills.ts
|
|
502
|
+
var DROID_SKILLS_START = "<!-- droid-skills-start -->";
|
|
503
|
+
var DROID_SKILLS_END = "<!-- droid-skills-end -->";
|
|
504
|
+
var __dirname4 = dirname4(fileURLToPath4(import.meta.url));
|
|
505
|
+
var BUNDLED_SKILLS_DIR = join6(__dirname4, "../tools");
|
|
506
|
+
function getSkillsInstallPath(platform) {
|
|
507
|
+
return getSkillsPath(platform);
|
|
508
|
+
}
|
|
509
|
+
function getCommandsInstallPath(platform) {
|
|
510
|
+
return getCommandsPath(platform);
|
|
511
|
+
}
|
|
512
|
+
function getPlatformConfigPath(platform) {
|
|
513
|
+
return getConfigPath2(platform);
|
|
53
514
|
}
|
|
54
|
-
|
|
55
|
-
|
|
515
|
+
function updatePlatformConfigSkills(platform, installedSkills) {
|
|
516
|
+
const configPath = getPlatformConfigPath(platform);
|
|
517
|
+
let content = "";
|
|
518
|
+
if (existsSync4(configPath)) {
|
|
519
|
+
content = readFileSync5(configPath, "utf-8");
|
|
520
|
+
}
|
|
521
|
+
const skillLines = installedSkills.map((name) => {
|
|
522
|
+
const relativePath = `skills/${name}/SKILL.md`;
|
|
523
|
+
return `- [${name}](${relativePath})`;
|
|
524
|
+
});
|
|
525
|
+
const skillsSection = installedSkills.length > 0 ? `${DROID_SKILLS_START}
|
|
526
|
+
## Droid Skills
|
|
527
|
+
|
|
528
|
+
${skillLines.join("\n")}
|
|
529
|
+
${DROID_SKILLS_END}` : "";
|
|
530
|
+
const startIdx = content.indexOf(DROID_SKILLS_START);
|
|
531
|
+
const endIdx = content.indexOf(DROID_SKILLS_END);
|
|
532
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
533
|
+
content = content.slice(0, startIdx) + skillsSection + content.slice(endIdx + DROID_SKILLS_END.length);
|
|
534
|
+
} else if (skillsSection) {
|
|
535
|
+
content = content.trim() + "\n\n" + skillsSection + "\n";
|
|
536
|
+
}
|
|
537
|
+
const configDir = dirname4(configPath);
|
|
538
|
+
if (!existsSync4(configDir)) {
|
|
539
|
+
mkdirSync3(configDir, { recursive: true });
|
|
540
|
+
}
|
|
541
|
+
writeFileSync3(configPath, content, "utf-8");
|
|
542
|
+
}
|
|
543
|
+
function parseSkillFrontmatter(content) {
|
|
544
|
+
const trimmed = content.trimStart();
|
|
545
|
+
if (!trimmed.startsWith("---")) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
const endMatch = trimmed.slice(3).indexOf("---");
|
|
549
|
+
if (endMatch === -1) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const frontmatterContent = trimmed.slice(3, 3 + endMatch);
|
|
553
|
+
try {
|
|
554
|
+
return YAML4.parse(frontmatterContent);
|
|
555
|
+
} catch {
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function loadSkillManifest(skillDir) {
|
|
560
|
+
const skillMdPath = join6(skillDir, "SKILL.md");
|
|
561
|
+
if (!existsSync4(skillMdPath)) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
const content = readFileSync5(skillMdPath, "utf-8");
|
|
565
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
566
|
+
if (!frontmatter || !frontmatter.name) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const toolDir = dirname4(dirname4(skillDir));
|
|
570
|
+
const toolManifest = loadToolManifest(toolDir);
|
|
571
|
+
return {
|
|
572
|
+
name: frontmatter.name,
|
|
573
|
+
description: frontmatter.description || "",
|
|
574
|
+
version: toolManifest?.version || "0.0.0",
|
|
575
|
+
status: toolManifest?.status,
|
|
576
|
+
dependencies: toolManifest?.dependencies,
|
|
577
|
+
config_schema: toolManifest?.config_schema
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function findSkillPath(skillName) {
|
|
581
|
+
if (!existsSync4(BUNDLED_SKILLS_DIR)) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
const toolDirs = readdirSync3(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
585
|
+
for (const toolName of toolDirs) {
|
|
586
|
+
const skillsDir = join6(BUNDLED_SKILLS_DIR, toolName, "skills");
|
|
587
|
+
if (!existsSync4(skillsDir)) continue;
|
|
588
|
+
const skillDir = join6(skillsDir, skillName);
|
|
589
|
+
if (existsSync4(skillDir) && existsSync4(join6(skillDir, "SKILL.md"))) {
|
|
590
|
+
return {
|
|
591
|
+
toolDir: join6(BUNDLED_SKILLS_DIR, toolName),
|
|
592
|
+
skillDir
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
function getBundledSkills() {
|
|
599
|
+
if (!existsSync4(BUNDLED_SKILLS_DIR)) {
|
|
600
|
+
return [];
|
|
601
|
+
}
|
|
602
|
+
const toolDirs = readdirSync3(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
603
|
+
const skills = [];
|
|
604
|
+
for (const toolName of toolDirs) {
|
|
605
|
+
const skillsDir = join6(BUNDLED_SKILLS_DIR, toolName, "skills");
|
|
606
|
+
if (!existsSync4(skillsDir)) continue;
|
|
607
|
+
const skillSubdirs = readdirSync3(skillsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
608
|
+
for (const skillName of skillSubdirs) {
|
|
609
|
+
const manifest = loadSkillManifest(join6(skillsDir, skillName));
|
|
610
|
+
if (manifest) {
|
|
611
|
+
skills.push(manifest);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return skills;
|
|
616
|
+
}
|
|
617
|
+
function isSkillInstalled(skillName) {
|
|
618
|
+
const config = loadConfig();
|
|
619
|
+
const tools = getPlatformTools(config);
|
|
620
|
+
return skillName in tools;
|
|
621
|
+
}
|
|
622
|
+
function getInstalledSkill(skillName) {
|
|
623
|
+
const config = loadConfig();
|
|
624
|
+
const tools = getPlatformTools(config);
|
|
625
|
+
return tools[skillName] || null;
|
|
626
|
+
}
|
|
627
|
+
function getSkillUpdateStatus(skillName) {
|
|
628
|
+
const installed = getInstalledSkill(skillName);
|
|
629
|
+
const skillPath = findSkillPath(skillName);
|
|
630
|
+
const manifest = skillPath ? loadSkillManifest(skillPath.skillDir) : null;
|
|
631
|
+
if (!installed || !manifest) {
|
|
632
|
+
return {
|
|
633
|
+
hasUpdate: false,
|
|
634
|
+
installedVersion: installed?.version || null,
|
|
635
|
+
bundledVersion: manifest?.version || null
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
hasUpdate: manifest.version !== installed.version,
|
|
640
|
+
installedVersion: installed.version,
|
|
641
|
+
bundledVersion: manifest.version
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function updateSkill(skillName) {
|
|
645
|
+
const status = getSkillUpdateStatus(skillName);
|
|
646
|
+
if (!status.installedVersion) {
|
|
647
|
+
return { success: false, message: `Skill '${skillName}' is not installed` };
|
|
648
|
+
}
|
|
649
|
+
if (!status.bundledVersion) {
|
|
650
|
+
return { success: false, message: `Skill '${skillName}' not found in bundled skills` };
|
|
651
|
+
}
|
|
652
|
+
if (!status.hasUpdate) {
|
|
653
|
+
return { success: false, message: `Skill '${skillName}' is already at latest version (${status.installedVersion})` };
|
|
654
|
+
}
|
|
655
|
+
const result = installSkill(skillName);
|
|
656
|
+
if (result.success) {
|
|
657
|
+
return {
|
|
658
|
+
success: true,
|
|
659
|
+
message: `Updated ${skillName} from ${status.installedVersion} to ${status.bundledVersion}`
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
function installSkill(skillName) {
|
|
665
|
+
const config = loadConfig();
|
|
666
|
+
const skillPath = findSkillPath(skillName);
|
|
667
|
+
if (!skillPath) {
|
|
668
|
+
return { success: false, message: `Skill '${skillName}' not found` };
|
|
669
|
+
}
|
|
670
|
+
const { toolDir, skillDir } = skillPath;
|
|
671
|
+
const manifest = loadSkillManifest(skillDir);
|
|
672
|
+
if (!manifest) {
|
|
673
|
+
return { success: false, message: `Invalid skill manifest for '${skillName}'` };
|
|
674
|
+
}
|
|
675
|
+
if (manifest.dependencies) {
|
|
676
|
+
for (const dep of manifest.dependencies) {
|
|
677
|
+
if (!isSkillInstalled(dep)) {
|
|
678
|
+
return {
|
|
679
|
+
success: false,
|
|
680
|
+
message: `Missing dependency: '${dep}'. Install it first with \`droid install ${dep}\``
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const skillsPath = getSkillsInstallPath(config.platform);
|
|
686
|
+
const targetSkillDir = join6(skillsPath, skillName);
|
|
687
|
+
const commandsPath = getCommandsInstallPath(config.platform);
|
|
688
|
+
const tools = getPlatformTools(config);
|
|
689
|
+
const commandsSource = join6(toolDir, "commands");
|
|
690
|
+
const agentsSource = join6(toolDir, "agents");
|
|
691
|
+
if (!tools[skillName]) {
|
|
692
|
+
if (existsSync4(commandsSource)) {
|
|
693
|
+
const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
|
|
694
|
+
for (const file of commandFiles) {
|
|
695
|
+
const targetCommandPath = join6(commandsPath, file);
|
|
696
|
+
if (existsSync4(targetCommandPath)) {
|
|
697
|
+
const commandName = file.replace(".md", "");
|
|
698
|
+
return {
|
|
699
|
+
success: false,
|
|
700
|
+
message: `Cannot install: command /${commandName} already exists at ${targetCommandPath}`
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (existsSync4(agentsSource)) {
|
|
706
|
+
const agentDirs = readdirSync3(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
707
|
+
for (const agentName of agentDirs) {
|
|
708
|
+
if (isAgentInstalled(agentName)) {
|
|
709
|
+
return {
|
|
710
|
+
success: false,
|
|
711
|
+
message: `Cannot install: agent '${agentName}' already exists at ${getInstalledAgentsDir()}`
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (!existsSync4(skillsPath)) {
|
|
718
|
+
mkdirSync3(skillsPath, { recursive: true });
|
|
719
|
+
}
|
|
720
|
+
const skillMdSource = join6(skillDir, "SKILL.md");
|
|
721
|
+
if (existsSync4(skillMdSource)) {
|
|
722
|
+
if (!existsSync4(targetSkillDir)) {
|
|
723
|
+
mkdirSync3(targetSkillDir, { recursive: true });
|
|
724
|
+
}
|
|
725
|
+
const skillMdTarget = join6(targetSkillDir, "SKILL.md");
|
|
726
|
+
const content = readFileSync5(skillMdSource, "utf-8");
|
|
727
|
+
writeFileSync3(skillMdTarget, content);
|
|
728
|
+
}
|
|
729
|
+
const referencesSource = join6(skillDir, "references");
|
|
730
|
+
if (existsSync4(referencesSource)) {
|
|
731
|
+
const targetReferencesDir = join6(targetSkillDir, "references");
|
|
732
|
+
if (!existsSync4(targetReferencesDir)) {
|
|
733
|
+
mkdirSync3(targetReferencesDir, { recursive: true });
|
|
734
|
+
}
|
|
735
|
+
const referenceFiles = readdirSync3(referencesSource).filter((f) => f.endsWith(".md"));
|
|
736
|
+
for (const file of referenceFiles) {
|
|
737
|
+
const sourcePath = join6(referencesSource, file);
|
|
738
|
+
const targetPath = join6(targetReferencesDir, file);
|
|
739
|
+
const content = readFileSync5(sourcePath, "utf-8");
|
|
740
|
+
writeFileSync3(targetPath, content);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (existsSync4(commandsSource)) {
|
|
744
|
+
if (!existsSync4(commandsPath)) {
|
|
745
|
+
mkdirSync3(commandsPath, { recursive: true });
|
|
746
|
+
}
|
|
747
|
+
const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
|
|
748
|
+
for (const file of commandFiles) {
|
|
749
|
+
const sourcePath = join6(commandsSource, file);
|
|
750
|
+
const targetPath = join6(commandsPath, file);
|
|
751
|
+
const content = readFileSync5(sourcePath, "utf-8");
|
|
752
|
+
writeFileSync3(targetPath, content);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const installedAgents = [];
|
|
756
|
+
if (existsSync4(agentsSource)) {
|
|
757
|
+
const agentDirs = readdirSync3(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
758
|
+
for (const agentName of agentDirs) {
|
|
759
|
+
const agentDir = join6(agentsSource, agentName);
|
|
760
|
+
const result = installAgentFromPath(agentDir, agentName);
|
|
761
|
+
if (result.success) {
|
|
762
|
+
installedAgents.push(agentName);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const updatedTools = {
|
|
767
|
+
...tools,
|
|
768
|
+
[skillName]: {
|
|
769
|
+
version: manifest.version,
|
|
770
|
+
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
771
|
+
...installedAgents.length > 0 && { bundled_agents: installedAgents }
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
setPlatformTools(config, updatedTools);
|
|
775
|
+
saveConfig(config);
|
|
776
|
+
const installedSkillNames = Object.keys(updatedTools);
|
|
777
|
+
updatePlatformConfigSkills(config.platform, installedSkillNames);
|
|
778
|
+
return { success: true, message: `Installed ${skillName} v${manifest.version}` };
|
|
779
|
+
}
|
|
780
|
+
function uninstallSkill(skillName) {
|
|
781
|
+
const config = loadConfig();
|
|
782
|
+
const tools = getPlatformTools(config);
|
|
783
|
+
if (!isSkillInstalled(skillName)) {
|
|
784
|
+
return { success: false, message: `Skill '${skillName}' is not installed` };
|
|
785
|
+
}
|
|
786
|
+
const skillsPath = getSkillsInstallPath(config.platform);
|
|
787
|
+
const skillDir = join6(skillsPath, skillName);
|
|
788
|
+
if (existsSync4(skillDir)) {
|
|
789
|
+
rmSync(skillDir, { recursive: true });
|
|
790
|
+
}
|
|
791
|
+
const skillPath = findSkillPath(skillName);
|
|
792
|
+
const commandsPath = getCommandsInstallPath(config.platform);
|
|
793
|
+
const commandsSource = skillPath ? join6(skillPath.toolDir, "commands") : null;
|
|
794
|
+
if (commandsSource && existsSync4(commandsSource)) {
|
|
795
|
+
const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
|
|
796
|
+
for (const file of commandFiles) {
|
|
797
|
+
const commandPath = join6(commandsPath, file);
|
|
798
|
+
if (existsSync4(commandPath)) {
|
|
799
|
+
rmSync(commandPath);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const installedSkillInfo = tools[skillName];
|
|
804
|
+
if (installedSkillInfo?.bundled_agents) {
|
|
805
|
+
for (const agentName of installedSkillInfo.bundled_agents) {
|
|
806
|
+
uninstallAgent(agentName);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const { [skillName]: removed, ...remainingTools } = tools;
|
|
810
|
+
setPlatformTools(config, remainingTools);
|
|
811
|
+
saveConfig(config);
|
|
812
|
+
const installedSkillNames = Object.keys(remainingTools);
|
|
813
|
+
updatePlatformConfigSkills(config.platform, installedSkillNames);
|
|
814
|
+
return { success: true, message: `Uninstalled ${skillName}` };
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/commands/setup.ts
|
|
818
|
+
var DROID_PERMISSIONS = [
|
|
819
|
+
"Read(~/.droid/**)",
|
|
820
|
+
"Write(~/.droid/**)",
|
|
821
|
+
"Edit(~/.droid/**)",
|
|
822
|
+
"Glob(~/.droid/**)",
|
|
823
|
+
"Grep(~/.droid/**)"
|
|
824
|
+
];
|
|
825
|
+
function detectPlatform() {
|
|
826
|
+
try {
|
|
827
|
+
execSync2("claude --version", { stdio: "ignore" });
|
|
828
|
+
return "claude-code" /* ClaudeCode */;
|
|
829
|
+
} catch {
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
execSync2("opencode --version", { stdio: "ignore" });
|
|
833
|
+
return "opencode" /* OpenCode */;
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
function detectGitUsername() {
|
|
839
|
+
try {
|
|
840
|
+
return execSync2("git config user.name", { encoding: "utf-8" }).trim();
|
|
841
|
+
} catch {
|
|
842
|
+
return "";
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
var OPENCODE_SKILLS_PLUGIN = "opencode-skills";
|
|
846
|
+
function configurePlatformPermissions(platform) {
|
|
847
|
+
const added = [];
|
|
848
|
+
if (platform === "claude-code" /* ClaudeCode */) {
|
|
849
|
+
const settingsPath = join7(homedir3(), ".claude", "settings.json");
|
|
850
|
+
const claudeDir = join7(homedir3(), ".claude");
|
|
851
|
+
if (!existsSync5(claudeDir)) {
|
|
852
|
+
mkdirSync4(claudeDir, { recursive: true });
|
|
853
|
+
}
|
|
854
|
+
let settings = {};
|
|
855
|
+
if (existsSync5(settingsPath)) {
|
|
856
|
+
try {
|
|
857
|
+
settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
858
|
+
} catch {
|
|
859
|
+
console.warn(chalk2.yellow("\u26A0 Claude Code settings.json appears corrupted, resetting permissions"));
|
|
860
|
+
settings = {};
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (!settings.permissions) {
|
|
864
|
+
settings.permissions = {};
|
|
865
|
+
}
|
|
866
|
+
if (!Array.isArray(settings.permissions.allow)) {
|
|
867
|
+
settings.permissions.allow = [];
|
|
868
|
+
}
|
|
869
|
+
for (const perm of DROID_PERMISSIONS) {
|
|
870
|
+
if (!settings.permissions.allow.includes(perm)) {
|
|
871
|
+
settings.permissions.allow.push(perm);
|
|
872
|
+
added.push(perm);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (added.length > 0) {
|
|
876
|
+
try {
|
|
877
|
+
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
878
|
+
} catch (e) {
|
|
879
|
+
const message = e instanceof Error ? e.message : "Unknown error";
|
|
880
|
+
return { added: [], alreadyPresent: false, error: `Failed to update Claude Code settings: ${message}` };
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return { added, alreadyPresent: added.length === 0 };
|
|
884
|
+
}
|
|
885
|
+
if (platform === "opencode" /* OpenCode */) {
|
|
886
|
+
const globalConfigDir = join7(homedir3(), ".config", "opencode");
|
|
887
|
+
const globalConfigPath = join7(globalConfigDir, "opencode.json");
|
|
888
|
+
if (!existsSync5(globalConfigDir)) {
|
|
889
|
+
mkdirSync4(globalConfigDir, { recursive: true });
|
|
890
|
+
}
|
|
891
|
+
let config = {};
|
|
892
|
+
if (existsSync5(globalConfigPath)) {
|
|
893
|
+
try {
|
|
894
|
+
config = JSON.parse(readFileSync6(globalConfigPath, "utf-8"));
|
|
895
|
+
} catch {
|
|
896
|
+
console.warn(chalk2.yellow("\u26A0 OpenCode config appears corrupted, resetting"));
|
|
897
|
+
config = {};
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (!Array.isArray(config.plugin)) {
|
|
901
|
+
config.plugin = [];
|
|
902
|
+
}
|
|
903
|
+
if (!config.plugin.includes(OPENCODE_SKILLS_PLUGIN)) {
|
|
904
|
+
config.plugin.push(OPENCODE_SKILLS_PLUGIN);
|
|
905
|
+
added.push(OPENCODE_SKILLS_PLUGIN);
|
|
906
|
+
}
|
|
907
|
+
if (added.length > 0) {
|
|
908
|
+
try {
|
|
909
|
+
writeFileSync4(globalConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
910
|
+
} catch (e) {
|
|
911
|
+
const message = e instanceof Error ? e.message : "Unknown error";
|
|
912
|
+
return { added: [], alreadyPresent: false, error: `Failed to update OpenCode config: ${message}` };
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return { added, alreadyPresent: added.length === 0 };
|
|
916
|
+
}
|
|
917
|
+
return { added: [], alreadyPresent: true };
|
|
918
|
+
}
|
|
919
|
+
function getOutputOptions() {
|
|
920
|
+
const options = [
|
|
921
|
+
{ name: "Terminal (display in CLI)", value: "terminal" /* Terminal */ },
|
|
922
|
+
{ name: "Editor ($EDITOR)", value: "editor" /* Editor */ }
|
|
923
|
+
];
|
|
924
|
+
const skills = getBundledSkills();
|
|
925
|
+
for (const skill of skills) {
|
|
926
|
+
if (skill.provides_output) {
|
|
927
|
+
options.push({
|
|
928
|
+
name: `${skill.name} (${skill.description})`,
|
|
929
|
+
value: skill.name
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return options;
|
|
934
|
+
}
|
|
935
|
+
async function setupCommand() {
|
|
936
|
+
console.log(chalk2.bold("\n\u{1F916} Droid Setup\n"));
|
|
937
|
+
const hasExistingConfig = configExists();
|
|
938
|
+
if (hasExistingConfig) {
|
|
939
|
+
const { overwrite } = await inquirer.prompt([
|
|
940
|
+
{
|
|
941
|
+
type: "confirm",
|
|
942
|
+
name: "overwrite",
|
|
943
|
+
message: "Config already exists. Overwrite?",
|
|
944
|
+
default: false
|
|
945
|
+
}
|
|
946
|
+
]);
|
|
947
|
+
if (!overwrite) {
|
|
948
|
+
console.log(chalk2.gray("Setup cancelled."));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
const detectedPlatform = detectPlatform();
|
|
953
|
+
if (detectedPlatform) {
|
|
954
|
+
console.log(chalk2.green(`\u2713 Detected ${detectedPlatform}
|
|
955
|
+
`));
|
|
956
|
+
} else {
|
|
957
|
+
console.log(chalk2.yellow("\u26A0 No platform detected (Claude Code or OpenCode)\n"));
|
|
958
|
+
}
|
|
959
|
+
const detectedGitUsername = detectGitUsername();
|
|
960
|
+
const outputOptions = getOutputOptions();
|
|
961
|
+
const answers = await inquirer.prompt([
|
|
962
|
+
{
|
|
963
|
+
type: "list",
|
|
964
|
+
name: "platform",
|
|
965
|
+
message: "Which platform are you using?",
|
|
966
|
+
choices: [
|
|
967
|
+
{ name: "Claude Code", value: "claude-code" /* ClaudeCode */ },
|
|
968
|
+
{ name: "OpenCode", value: "opencode" /* OpenCode */ }
|
|
969
|
+
],
|
|
970
|
+
default: detectedPlatform || "claude-code" /* ClaudeCode */
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
type: "input",
|
|
974
|
+
name: "user_mention",
|
|
975
|
+
message: "What @mention should be used for you?",
|
|
976
|
+
default: "@user",
|
|
977
|
+
filter: (input) => {
|
|
978
|
+
return input.startsWith("@") ? input : `@${input}`;
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
type: "list",
|
|
983
|
+
name: "output_preference",
|
|
984
|
+
message: "Default output preference for skill results?",
|
|
985
|
+
choices: outputOptions,
|
|
986
|
+
default: "terminal" /* Terminal */
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
type: "input",
|
|
990
|
+
name: "git_username",
|
|
991
|
+
message: "Git username for attribution?",
|
|
992
|
+
default: detectedGitUsername || ""
|
|
993
|
+
}
|
|
994
|
+
]);
|
|
995
|
+
const existingConfig = loadConfig();
|
|
996
|
+
const config = {
|
|
997
|
+
...existingConfig,
|
|
998
|
+
platform: answers.platform,
|
|
999
|
+
user_mention: answers.user_mention,
|
|
1000
|
+
output_preference: answers.output_preference,
|
|
1001
|
+
git_username: answers.git_username
|
|
1002
|
+
};
|
|
1003
|
+
saveConfig(config);
|
|
1004
|
+
console.log(chalk2.green("\n\u2713 Config saved to ~/.droid/config.yaml"));
|
|
1005
|
+
const { added, alreadyPresent, error } = configurePlatformPermissions(answers.platform);
|
|
1006
|
+
if (error) {
|
|
1007
|
+
console.log(chalk2.red(`\u2717 ${error}`));
|
|
1008
|
+
console.log(chalk2.yellow(" You may need to manually configure your platform"));
|
|
1009
|
+
} else if (answers.platform === "claude-code" /* ClaudeCode */) {
|
|
1010
|
+
if (added.length > 0) {
|
|
1011
|
+
console.log(chalk2.green(`\u2713 Added droid permissions to Claude Code settings`));
|
|
1012
|
+
} else if (alreadyPresent) {
|
|
1013
|
+
console.log(chalk2.gray(` Droid permissions already configured in Claude Code`));
|
|
1014
|
+
}
|
|
1015
|
+
} else if (answers.platform === "opencode" /* OpenCode */) {
|
|
1016
|
+
if (added.length > 0) {
|
|
1017
|
+
console.log(chalk2.green(`\u2713 Added opencode-skills plugin to OpenCode config`));
|
|
1018
|
+
console.log(chalk2.gray(` This enables Claude Code-style skills in OpenCode`));
|
|
1019
|
+
console.log(chalk2.gray(` Restart OpenCode to activate the plugin`));
|
|
1020
|
+
} else if (alreadyPresent) {
|
|
1021
|
+
console.log(chalk2.gray(` opencode-skills plugin already configured in OpenCode`));
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
console.log(chalk2.gray("\nRun `droid skills` to browse and install skills."));
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/commands/config.ts
|
|
1028
|
+
import chalk3 from "chalk";
|
|
1029
|
+
import { execSync as execSync3 } from "child_process";
|
|
1030
|
+
async function configCommand(options) {
|
|
1031
|
+
if (options.edit) {
|
|
1032
|
+
const configPath = getConfigPath();
|
|
1033
|
+
const editor = process.env.EDITOR || "vim";
|
|
1034
|
+
if (!configExists()) {
|
|
1035
|
+
console.log(chalk3.yellow("No config file exists yet. Run `droid setup` first."));
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
console.log(chalk3.gray(`Opening ${configPath} in ${editor}...`));
|
|
1039
|
+
try {
|
|
1040
|
+
execSync3(`${editor} "${configPath}"`, { stdio: "inherit" });
|
|
1041
|
+
} catch {
|
|
1042
|
+
console.error(chalk3.red("Failed to open editor"));
|
|
1043
|
+
}
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (options.get) {
|
|
1047
|
+
const value = getConfigValue(options.get);
|
|
1048
|
+
if (value === void 0) {
|
|
1049
|
+
console.log(chalk3.yellow(`Config key '${options.get}' not found`));
|
|
1050
|
+
process.exit(1);
|
|
1051
|
+
}
|
|
1052
|
+
if (typeof value === "object") {
|
|
1053
|
+
console.log(JSON.stringify(value, null, 2));
|
|
1054
|
+
} else {
|
|
1055
|
+
console.log(value);
|
|
1056
|
+
}
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (options.set) {
|
|
1060
|
+
const match = options.set.match(/^([^=]+)=(.*)$/);
|
|
1061
|
+
if (!match) {
|
|
1062
|
+
console.error(chalk3.red("Invalid format. Use: --set key=value"));
|
|
1063
|
+
process.exit(1);
|
|
1064
|
+
}
|
|
1065
|
+
const [, key, rawValue] = match;
|
|
1066
|
+
let value;
|
|
1067
|
+
try {
|
|
1068
|
+
value = JSON.parse(rawValue);
|
|
1069
|
+
} catch {
|
|
1070
|
+
value = rawValue;
|
|
1071
|
+
}
|
|
1072
|
+
setConfigValue(key, value);
|
|
1073
|
+
console.log(chalk3.green(`\u2713 Set ${key} = ${JSON.stringify(value)}`));
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (!configExists()) {
|
|
1077
|
+
console.log(chalk3.yellow("No config file exists yet. Run `droid setup` first."));
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const config = loadConfig();
|
|
1081
|
+
console.log(chalk3.bold("\n\u{1F4CB} Droid Config\n"));
|
|
1082
|
+
console.log(chalk3.gray(`Path: ${getConfigPath()}
|
|
1083
|
+
`));
|
|
1084
|
+
console.log(JSON.stringify(config, null, 2));
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/commands/skills.ts
|
|
1088
|
+
import inquirer3 from "inquirer";
|
|
1089
|
+
import chalk5 from "chalk";
|
|
1090
|
+
|
|
1091
|
+
// src/lib/skill-config.ts
|
|
1092
|
+
import chalk4 from "chalk";
|
|
1093
|
+
import inquirer2 from "inquirer";
|
|
1094
|
+
async function promptForSkillConfig(skillName, configSchema, askFirst = true) {
|
|
1095
|
+
const globalConfig = loadConfig();
|
|
1096
|
+
if (askFirst) {
|
|
1097
|
+
const { wantsConfigure } = await inquirer2.prompt([
|
|
1098
|
+
{
|
|
1099
|
+
type: "confirm",
|
|
1100
|
+
name: "wantsConfigure",
|
|
1101
|
+
message: "Would you like to configure this skill now?",
|
|
1102
|
+
default: false
|
|
1103
|
+
}
|
|
1104
|
+
]);
|
|
1105
|
+
if (!wantsConfigure) {
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
console.log(chalk4.bold(`
|
|
1110
|
+
\u2699\uFE0F Configure ${skillName}
|
|
1111
|
+
`));
|
|
1112
|
+
const existingOverrides = loadSkillOverrides(skillName);
|
|
1113
|
+
const questions = Object.entries(configSchema).map(([key, option]) => {
|
|
1114
|
+
const baseQuestion = {
|
|
1115
|
+
name: key,
|
|
1116
|
+
message: option.description
|
|
1117
|
+
};
|
|
1118
|
+
const existingValue = existingOverrides[key];
|
|
1119
|
+
switch (option.type) {
|
|
1120
|
+
case "boolean" /* Boolean */:
|
|
1121
|
+
return {
|
|
1122
|
+
...baseQuestion,
|
|
1123
|
+
type: "confirm",
|
|
1124
|
+
default: existingValue ?? option.default ?? false
|
|
1125
|
+
};
|
|
1126
|
+
case "select" /* Select */:
|
|
1127
|
+
return {
|
|
1128
|
+
...baseQuestion,
|
|
1129
|
+
type: "list",
|
|
1130
|
+
choices: option.options || [],
|
|
1131
|
+
default: existingValue ?? option.default
|
|
1132
|
+
};
|
|
1133
|
+
case "string" /* String */:
|
|
1134
|
+
default: {
|
|
1135
|
+
let defaultValue = existingValue ?? option.default ?? "";
|
|
1136
|
+
if (key === "user_mention" && !existingValue && globalConfig.user_mention) {
|
|
1137
|
+
defaultValue = globalConfig.user_mention;
|
|
1138
|
+
}
|
|
1139
|
+
return {
|
|
1140
|
+
...baseQuestion,
|
|
1141
|
+
type: "input",
|
|
1142
|
+
default: defaultValue
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
const answers = await inquirer2.prompt(questions);
|
|
1148
|
+
const overrides = {};
|
|
1149
|
+
for (const [key, value] of Object.entries(answers)) {
|
|
1150
|
+
const schema = configSchema[key];
|
|
1151
|
+
if (value !== schema.default && value !== "" && value !== false) {
|
|
1152
|
+
overrides[key] = value;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
if (Object.keys(overrides).length > 0) {
|
|
1156
|
+
saveSkillOverrides(skillName, overrides);
|
|
1157
|
+
console.log(chalk4.green(`
|
|
1158
|
+
\u2713 Configuration saved to ~/.droid/skills/${skillName}/overrides.yaml`));
|
|
1159
|
+
} else {
|
|
1160
|
+
console.log(chalk4.gray("\nNo custom configuration set (using defaults)."));
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/commands/skills.ts
|
|
1165
|
+
function formatToolChoice(tool) {
|
|
1166
|
+
const installed = isToolInstalled(tool.name);
|
|
1167
|
+
const installedVersion = getInstalledToolVersion(tool.name);
|
|
1168
|
+
let line = `${tool.name}`;
|
|
1169
|
+
if (installed && installedVersion) {
|
|
1170
|
+
line += chalk5.green(` [installed] v${installedVersion}`);
|
|
1171
|
+
} else {
|
|
1172
|
+
line += chalk5.gray(` v${tool.version}`);
|
|
1173
|
+
}
|
|
1174
|
+
if (tool.status) {
|
|
1175
|
+
const statusColor = tool.status === "alpha" /* Alpha */ ? chalk5.red : tool.status === "beta" /* Beta */ ? chalk5.yellow : chalk5.white;
|
|
1176
|
+
line += statusColor(` [${tool.status}]`);
|
|
1177
|
+
}
|
|
1178
|
+
return line;
|
|
1179
|
+
}
|
|
1180
|
+
async function skillsCommand() {
|
|
1181
|
+
const tools = getBundledTools();
|
|
1182
|
+
if (tools.length === 0) {
|
|
1183
|
+
console.log(chalk5.yellow("\nNo tools available yet."));
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
console.log(chalk5.bold("\n\u{1F916} Available Tools\n"));
|
|
1187
|
+
const toolChoices = tools.map((tool2) => ({
|
|
1188
|
+
name: formatToolChoice(tool2),
|
|
1189
|
+
value: tool2.name,
|
|
1190
|
+
short: tool2.name
|
|
1191
|
+
}));
|
|
1192
|
+
const choices = [
|
|
1193
|
+
{
|
|
1194
|
+
name: chalk5.gray("\u2190 Exit"),
|
|
1195
|
+
value: "__exit__",
|
|
1196
|
+
short: "Exit"
|
|
1197
|
+
},
|
|
1198
|
+
...toolChoices
|
|
1199
|
+
];
|
|
1200
|
+
const { selectedTool } = await inquirer3.prompt([
|
|
1201
|
+
{
|
|
1202
|
+
type: "list",
|
|
1203
|
+
name: "selectedTool",
|
|
1204
|
+
message: "Select a tool to view details or install:",
|
|
1205
|
+
choices,
|
|
1206
|
+
pageSize: 15
|
|
1207
|
+
}
|
|
1208
|
+
]);
|
|
1209
|
+
if (selectedTool === "__exit__") {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const tool = tools.find((t) => t.name === selectedTool);
|
|
1213
|
+
if (!tool) {
|
|
1214
|
+
console.error(chalk5.red("Tool not found"));
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
console.log(chalk5.bold(`
|
|
1218
|
+
\u{1F4E6} ${tool.name}`));
|
|
1219
|
+
console.log(chalk5.gray(`Version: ${tool.version}`));
|
|
1220
|
+
if (tool.status) {
|
|
1221
|
+
console.log(chalk5.gray(`Status: ${tool.status}`));
|
|
1222
|
+
}
|
|
1223
|
+
console.log(`
|
|
1224
|
+
${tool.description}`);
|
|
1225
|
+
const skillNames = tool.includes.skills.map((s) => s.name);
|
|
1226
|
+
const commandNames = tool.includes.commands.map((c) => `/${c}`);
|
|
1227
|
+
const agentNames = tool.includes.agents;
|
|
1228
|
+
if (skillNames.length > 0) {
|
|
1229
|
+
console.log(chalk5.gray(`
|
|
1230
|
+
Skills: ${skillNames.join(", ")}`));
|
|
1231
|
+
}
|
|
1232
|
+
if (commandNames.length > 0) {
|
|
1233
|
+
console.log(chalk5.gray(`Commands: ${commandNames.join(", ")}`));
|
|
1234
|
+
}
|
|
1235
|
+
if (agentNames.length > 0) {
|
|
1236
|
+
console.log(chalk5.gray(`Agents: ${agentNames.join(", ")}`));
|
|
1237
|
+
}
|
|
1238
|
+
const installed = isToolInstalled(tool.name);
|
|
1239
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || tool.name;
|
|
1240
|
+
const actions = installed ? [
|
|
1241
|
+
{ name: "\u2190 Back", value: "back" },
|
|
1242
|
+
{ name: "Configure", value: "configure" },
|
|
1243
|
+
{ name: "Uninstall", value: "uninstall" }
|
|
1244
|
+
] : [
|
|
1245
|
+
{ name: "\u2190 Back", value: "back" },
|
|
1246
|
+
{ name: "Install", value: "install" }
|
|
1247
|
+
];
|
|
1248
|
+
const { action } = await inquirer3.prompt([
|
|
1249
|
+
{
|
|
1250
|
+
type: "list",
|
|
1251
|
+
name: "action",
|
|
1252
|
+
message: "What would you like to do?",
|
|
1253
|
+
choices: actions
|
|
1254
|
+
}
|
|
1255
|
+
]);
|
|
1256
|
+
switch (action) {
|
|
1257
|
+
case "install": {
|
|
1258
|
+
const result = installSkill(primarySkill);
|
|
1259
|
+
if (result.success) {
|
|
1260
|
+
console.log(chalk5.green(`
|
|
1261
|
+
\u2713 Installed ${tool.name}`));
|
|
1262
|
+
if (tool.config_schema && Object.keys(tool.config_schema).length > 0) {
|
|
1263
|
+
const hasRequiredConfig = Object.values(tool.config_schema).some(
|
|
1264
|
+
(option) => option.default === void 0
|
|
1265
|
+
);
|
|
1266
|
+
await promptForSkillConfig(primarySkill, tool.config_schema, !hasRequiredConfig);
|
|
1267
|
+
}
|
|
1268
|
+
} else {
|
|
1269
|
+
console.log(chalk5.red(`
|
|
1270
|
+
\u2717 ${result.message}`));
|
|
1271
|
+
}
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
case "configure": {
|
|
1275
|
+
if (tool.config_schema && Object.keys(tool.config_schema).length > 0) {
|
|
1276
|
+
await promptForSkillConfig(primarySkill, tool.config_schema, false);
|
|
1277
|
+
} else {
|
|
1278
|
+
console.log(chalk5.gray("\nThis tool has no configuration options."));
|
|
1279
|
+
}
|
|
1280
|
+
break;
|
|
1281
|
+
}
|
|
1282
|
+
case "uninstall": {
|
|
1283
|
+
const result = uninstallSkill(primarySkill);
|
|
1284
|
+
if (result.success) {
|
|
1285
|
+
console.log(chalk5.green(`
|
|
1286
|
+
\u2713 Uninstalled ${tool.name}`));
|
|
1287
|
+
} else {
|
|
1288
|
+
console.log(chalk5.red(`
|
|
1289
|
+
\u2717 ${result.message}`));
|
|
1290
|
+
}
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
case "back":
|
|
1294
|
+
await skillsCommand();
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// src/commands/install.ts
|
|
1300
|
+
import chalk6 from "chalk";
|
|
1301
|
+
async function installCommand(toolName) {
|
|
1302
|
+
const tools = getBundledTools();
|
|
1303
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
1304
|
+
if (!tool) {
|
|
1305
|
+
console.error(chalk6.red(`
|
|
1306
|
+
\u2717 Tool '${toolName}' not found`));
|
|
1307
|
+
console.log(chalk6.gray("\nAvailable tools:"));
|
|
1308
|
+
for (const t of tools) {
|
|
1309
|
+
console.log(chalk6.gray(` - ${t.name}`));
|
|
1310
|
+
}
|
|
1311
|
+
process.exit(1);
|
|
1312
|
+
}
|
|
1313
|
+
if (isToolInstalled(toolName)) {
|
|
1314
|
+
console.log(chalk6.yellow(`
|
|
1315
|
+
\u26A0 Tool '${toolName}' is already installed`));
|
|
1316
|
+
console.log(chalk6.gray("Use `droid update` to update it, or uninstall first."));
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
console.log(chalk6.bold(`
|
|
1320
|
+
\u{1F916} Installing ${toolName}...
|
|
1321
|
+
`));
|
|
1322
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || toolName;
|
|
1323
|
+
const result = installSkill(primarySkill);
|
|
1324
|
+
if (result.success) {
|
|
1325
|
+
console.log(chalk6.green(`\u2713 Installed ${toolName}`));
|
|
1326
|
+
if (tool.config_schema && Object.keys(tool.config_schema).length > 0) {
|
|
1327
|
+
const hasRequiredConfig = Object.values(tool.config_schema).some(
|
|
1328
|
+
(option) => option.default === void 0
|
|
1329
|
+
);
|
|
1330
|
+
await promptForSkillConfig(primarySkill, tool.config_schema, !hasRequiredConfig);
|
|
1331
|
+
}
|
|
1332
|
+
console.log(chalk6.gray("\nNext steps:"));
|
|
1333
|
+
console.log(chalk6.gray(" - Run your AI tool to start using it"));
|
|
1334
|
+
console.log(chalk6.gray(` - Reconfigure anytime with \`droid\` \u2192 Configure`));
|
|
1335
|
+
} else {
|
|
1336
|
+
console.error(chalk6.red(`\u2717 ${result.message}`));
|
|
1337
|
+
process.exit(1);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// src/commands/uninstall.ts
|
|
1342
|
+
import chalk7 from "chalk";
|
|
1343
|
+
async function uninstallCommand(toolName) {
|
|
1344
|
+
const tools = getBundledTools();
|
|
1345
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
1346
|
+
if (!tool) {
|
|
1347
|
+
console.error(chalk7.red(`
|
|
1348
|
+
\u2717 Tool '${toolName}' not found`));
|
|
1349
|
+
process.exit(1);
|
|
1350
|
+
}
|
|
1351
|
+
if (!isToolInstalled(toolName)) {
|
|
1352
|
+
console.error(chalk7.red(`
|
|
1353
|
+
\u2717 Tool '${toolName}' is not installed`));
|
|
1354
|
+
process.exit(1);
|
|
1355
|
+
}
|
|
1356
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || toolName;
|
|
1357
|
+
const result = uninstallSkill(primarySkill);
|
|
1358
|
+
if (result.success) {
|
|
1359
|
+
console.log(chalk7.green(`
|
|
1360
|
+
\u2713 Uninstalled ${toolName}`));
|
|
1361
|
+
} else {
|
|
1362
|
+
console.error(chalk7.red(`
|
|
1363
|
+
\u2717 ${result.message}`));
|
|
1364
|
+
process.exit(1);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// src/commands/update.ts
|
|
1369
|
+
import chalk8 from "chalk";
|
|
1370
|
+
import { execSync as execSync4 } from "child_process";
|
|
1371
|
+
async function updateCommand(tool, options) {
|
|
1372
|
+
if (tool) {
|
|
1373
|
+
console.log(chalk8.yellow("\n\u26A0 Per-tool updates not implemented yet"));
|
|
1374
|
+
console.log(chalk8.gray("Tools are bundled with the CLI - run `droid update` to update all."));
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (options?.tools) {
|
|
1378
|
+
console.log(chalk8.yellow("\n\u26A0 Tool-only updates not implemented yet"));
|
|
1379
|
+
console.log(chalk8.gray("Tools are bundled with the CLI - run `droid update` to update all."));
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
console.log(chalk8.bold("\n\u{1F916} Updating Droid...\n"));
|
|
1383
|
+
const currentVersion = getVersion();
|
|
1384
|
+
console.log(chalk8.gray(`Current version: ${currentVersion}`));
|
|
1385
|
+
try {
|
|
1386
|
+
const latestVersion = execSync4("npm view @orderful/droid version 2>/dev/null", {
|
|
1387
|
+
encoding: "utf-8"
|
|
1388
|
+
}).trim();
|
|
1389
|
+
if (!latestVersion) {
|
|
1390
|
+
console.log(chalk8.yellow("\n\u26A0 Could not check for updates"));
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (latestVersion === currentVersion) {
|
|
1394
|
+
console.log(chalk8.green("\n\u2713 Already on latest version"));
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
console.log(chalk8.gray(`Latest version: ${latestVersion}`));
|
|
1398
|
+
console.log(chalk8.gray("\nUpdating..."));
|
|
1399
|
+
execSync4("npm install -g @orderful/droid@latest", { stdio: "inherit" });
|
|
1400
|
+
console.log(chalk8.green(`
|
|
1401
|
+
\u2713 Updated to v${latestVersion}`));
|
|
1402
|
+
} catch {
|
|
1403
|
+
console.log(chalk8.yellow("\n\u26A0 Could not check for updates"));
|
|
1404
|
+
console.log(chalk8.gray("Package may not be published yet."));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/commands/tui.tsx
|
|
1409
|
+
import { render, Box as Box12, Text as Text13, useInput as useInput7, useApp as useApp2 } from "ink";
|
|
1410
|
+
import { useState as useState9 } from "react";
|
|
1411
|
+
|
|
1412
|
+
// src/commands/tui/constants.ts
|
|
1413
|
+
var colors = {
|
|
1414
|
+
primary: "#6366f1",
|
|
1415
|
+
bgSelected: "#2d2d2d",
|
|
1416
|
+
border: "#3a3a3a",
|
|
1417
|
+
text: "#e8e8e8",
|
|
1418
|
+
textMuted: "#999999",
|
|
1419
|
+
textDim: "#6a6a6a",
|
|
1420
|
+
success: "#4ade80",
|
|
1421
|
+
error: "#f87171",
|
|
1422
|
+
// Component type badges
|
|
1423
|
+
skill: "#ec4899",
|
|
1424
|
+
// pink/magenta
|
|
1425
|
+
command: "#22d3ee",
|
|
1426
|
+
// cyan
|
|
1427
|
+
agent: "#fbbf24"
|
|
1428
|
+
// yellow/amber
|
|
1429
|
+
};
|
|
1430
|
+
var MAX_VISIBLE_ITEMS = 6;
|
|
1431
|
+
var MAX_VISIBLE_CONFIG_ITEMS = 4;
|
|
1432
|
+
|
|
1433
|
+
// src/commands/tui/components/TabBar.tsx
|
|
1434
|
+
import { Box, Text } from "ink";
|
|
1435
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1436
|
+
function TabBar({ tabs, activeTab }) {
|
|
1437
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
|
|
1438
|
+
Text,
|
|
1439
|
+
{
|
|
1440
|
+
backgroundColor: tab.id === activeTab ? colors.primary : void 0,
|
|
1441
|
+
color: tab.id === activeTab ? "#ffffff" : colors.textMuted,
|
|
1442
|
+
bold: tab.id === activeTab,
|
|
1443
|
+
wrap: "truncate",
|
|
1444
|
+
children: [
|
|
1445
|
+
" ",
|
|
1446
|
+
tab.label,
|
|
1447
|
+
" "
|
|
1448
|
+
]
|
|
1449
|
+
},
|
|
1450
|
+
tab.id
|
|
1451
|
+
)) });
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// src/commands/tui/components/ToolItem.tsx
|
|
1455
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
1456
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1457
|
+
function ToolItem({
|
|
1458
|
+
tool,
|
|
1459
|
+
isSelected,
|
|
1460
|
+
isActive,
|
|
1461
|
+
wasAutoUpdated
|
|
1462
|
+
}) {
|
|
1463
|
+
const installed = isToolInstalled(tool.name);
|
|
1464
|
+
const installedVersion = getInstalledToolVersion(tool.name);
|
|
1465
|
+
const updateStatus = getToolUpdateStatus(tool.name);
|
|
1466
|
+
return /* @__PURE__ */ jsx2(Box2, { paddingX: 1, backgroundColor: isActive ? colors.bgSelected : void 0, children: /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
1467
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.textDim, children: [
|
|
1468
|
+
isSelected ? ">" : " ",
|
|
1469
|
+
" "
|
|
1470
|
+
] }),
|
|
1471
|
+
/* @__PURE__ */ jsx2(Text2, { color: isSelected || isActive ? colors.text : colors.textMuted, children: tool.name }),
|
|
1472
|
+
installed && installedVersion && /* @__PURE__ */ jsxs2(Text2, { color: colors.textDim, children: [
|
|
1473
|
+
" v",
|
|
1474
|
+
installedVersion
|
|
1475
|
+
] }),
|
|
1476
|
+
installed && /* @__PURE__ */ jsx2(Text2, { color: colors.success, children: " \u2713" }),
|
|
1477
|
+
wasAutoUpdated && /* @__PURE__ */ jsx2(Text2, { color: colors.success, children: " \u2191" }),
|
|
1478
|
+
updateStatus.hasUpdate && !wasAutoUpdated && /* @__PURE__ */ jsx2(Text2, { color: colors.primary, children: " \u2191" }),
|
|
1479
|
+
/* @__PURE__ */ jsx2(Text2, { children: " " }),
|
|
1480
|
+
tool.includes.skills.length > 0 && /* @__PURE__ */ jsx2(Text2, { color: colors.skill, children: "\u25CF " }),
|
|
1481
|
+
tool.includes.commands.length > 0 && /* @__PURE__ */ jsx2(Text2, { color: colors.command, children: "\u25CF " }),
|
|
1482
|
+
tool.includes.agents.length > 0 && /* @__PURE__ */ jsx2(Text2, { color: colors.agent, children: "\u25CF" })
|
|
1483
|
+
] }) });
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// src/commands/tui/components/ToolDetails.tsx
|
|
1487
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
1488
|
+
|
|
1489
|
+
// src/commands/tui/components/Badge.tsx
|
|
1490
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
1491
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1492
|
+
function ComponentBadges({ tool, compact = false }) {
|
|
1493
|
+
const hasSkills = tool.includes.skills.length > 0;
|
|
1494
|
+
const hasCommands = tool.includes.commands.length > 0;
|
|
1495
|
+
const hasAgents = tool.includes.agents.length > 0;
|
|
1496
|
+
if (compact) {
|
|
1497
|
+
const parts = [];
|
|
1498
|
+
if (hasSkills) parts.push("skill");
|
|
1499
|
+
if (hasCommands) parts.push("command");
|
|
1500
|
+
if (hasAgents) parts.push("agent");
|
|
1501
|
+
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "row", children: parts.map((type, i) => /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1502
|
+
/* @__PURE__ */ jsx3(Text3, { backgroundColor: colors[type], children: " " }),
|
|
1503
|
+
i < parts.length - 1 && " "
|
|
1504
|
+
] }, type)) });
|
|
1505
|
+
}
|
|
1506
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
|
|
1507
|
+
hasSkills && /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsx3(Text3, { backgroundColor: colors.skill, color: "#000000", bold: true, children: ` ${tool.includes.skills.length} skill${tool.includes.skills.length > 1 ? "s" : ""} ` }) }),
|
|
1508
|
+
hasCommands && /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsx3(Text3, { backgroundColor: colors.command, color: "#000000", bold: true, children: ` ${tool.includes.commands.length} cmd${tool.includes.commands.length > 1 ? "s" : ""} ` }) }),
|
|
1509
|
+
hasAgents && /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsx3(Text3, { backgroundColor: colors.agent, color: "#000000", bold: true, children: ` ${tool.includes.agents.length} agent${tool.includes.agents.length > 1 ? "s" : ""} ` }) })
|
|
1510
|
+
] });
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// src/commands/tui/components/ToolDetails.tsx
|
|
1514
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1515
|
+
function ToolDetails({
|
|
1516
|
+
tool,
|
|
1517
|
+
isFocused,
|
|
1518
|
+
selectedAction
|
|
1519
|
+
}) {
|
|
1520
|
+
if (!tool) {
|
|
1521
|
+
return /* @__PURE__ */ jsx4(Box4, { paddingLeft: 2, flexGrow: 1, children: /* @__PURE__ */ jsx4(Text4, { color: colors.textDim, children: "Select a tool" }) });
|
|
1522
|
+
}
|
|
1523
|
+
const installed = isToolInstalled(tool.name);
|
|
1524
|
+
const installedVersion = getInstalledToolVersion(tool.name);
|
|
1525
|
+
const updateStatus = getToolUpdateStatus(tool.name);
|
|
1526
|
+
const isSystemTool = tool.system === true;
|
|
1527
|
+
const actions = installed ? [
|
|
1528
|
+
{ id: "explore", label: "Explore", variant: "default" },
|
|
1529
|
+
...updateStatus.hasUpdate ? [{ id: "update", label: `Update (${updateStatus.bundledVersion})`, variant: "primary" }] : [],
|
|
1530
|
+
{ id: "configure", label: "Configure", variant: "default" },
|
|
1531
|
+
// System tools can't be uninstalled
|
|
1532
|
+
...!isSystemTool ? [{ id: "uninstall", label: "Uninstall", variant: "danger" }] : []
|
|
1533
|
+
] : [
|
|
1534
|
+
{ id: "explore", label: "Explore", variant: "default" },
|
|
1535
|
+
// System tools auto-install, but show Install for manual trigger if needed
|
|
1536
|
+
{ id: "install", label: "Install", variant: "primary" }
|
|
1537
|
+
];
|
|
1538
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
|
|
1539
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.text, bold: true, children: tool.name }),
|
|
1540
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: colors.textDim, children: [
|
|
1541
|
+
tool.version,
|
|
1542
|
+
tool.status && ` \xB7 ${tool.status}`,
|
|
1543
|
+
installed && /* @__PURE__ */ jsx4(Text4, { color: colors.success, children: " \xB7 installed" }),
|
|
1544
|
+
updateStatus.hasUpdate && /* @__PURE__ */ jsxs4(Text4, { color: colors.primary, children: [
|
|
1545
|
+
" \xB7 update (",
|
|
1546
|
+
installedVersion,
|
|
1547
|
+
" \u2192 ",
|
|
1548
|
+
updateStatus.bundledVersion,
|
|
1549
|
+
")"
|
|
1550
|
+
] })
|
|
1551
|
+
] }) }),
|
|
1552
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.description }) }),
|
|
1553
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
|
|
1554
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.textDim, children: "Includes:" }),
|
|
1555
|
+
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(ComponentBadges, { tool }) }),
|
|
1556
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [
|
|
1557
|
+
tool.includes.skills.length > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1558
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.skill, children: "Skills: " }),
|
|
1559
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.includes.skills.map((s) => s.name).join(", ") })
|
|
1560
|
+
] }),
|
|
1561
|
+
tool.includes.commands.length > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1562
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.command, children: "Commands: " }),
|
|
1563
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.includes.commands.map((c) => `/${c}`).join(", ") })
|
|
1564
|
+
] }),
|
|
1565
|
+
tool.includes.agents.length > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1566
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.agent, children: "Agents: " }),
|
|
1567
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.includes.agents.join(", ") })
|
|
1568
|
+
] })
|
|
1569
|
+
] })
|
|
1570
|
+
] }),
|
|
1571
|
+
isFocused && /* @__PURE__ */ jsx4(Box4, { flexDirection: "row", marginTop: 1, children: actions.map((action, index) => /* @__PURE__ */ jsxs4(
|
|
1572
|
+
Text4,
|
|
1573
|
+
{
|
|
1574
|
+
backgroundColor: selectedAction === index ? action.variant === "danger" ? colors.error : colors.primary : colors.bgSelected,
|
|
1575
|
+
color: selectedAction === index ? "#ffffff" : colors.textMuted,
|
|
1576
|
+
bold: selectedAction === index,
|
|
1577
|
+
children: [
|
|
1578
|
+
" ",
|
|
1579
|
+
action.label,
|
|
1580
|
+
" "
|
|
1581
|
+
]
|
|
1582
|
+
},
|
|
1583
|
+
action.id
|
|
1584
|
+
)) })
|
|
1585
|
+
] });
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// src/commands/tui/components/SettingsDetails.tsx
|
|
1589
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
1590
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1591
|
+
function SettingsDetails({
|
|
1592
|
+
isFocused
|
|
1593
|
+
}) {
|
|
1594
|
+
const config = loadConfig();
|
|
1595
|
+
const autoUpdateConfig = getAutoUpdateConfig();
|
|
1596
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
|
|
1597
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, bold: true, children: "Settings" }),
|
|
1598
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
1599
|
+
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
1600
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "Platform: " }),
|
|
1601
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: config.platform === "claude-code" /* ClaudeCode */ ? "Claude Code" : "OpenCode" })
|
|
1602
|
+
] }),
|
|
1603
|
+
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
1604
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "Your @mention: " }),
|
|
1605
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: config.user_mention })
|
|
1606
|
+
] }),
|
|
1607
|
+
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
1608
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "Auto-update tools: " }),
|
|
1609
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: autoUpdateConfig.tools ? "enabled" : "disabled" })
|
|
1610
|
+
] }),
|
|
1611
|
+
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
1612
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "Auto-update app: " }),
|
|
1613
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: autoUpdateConfig.app ? "enabled" : "disabled" })
|
|
1614
|
+
] })
|
|
1615
|
+
] }),
|
|
1616
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 2, children: /* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "Config: ~/.droid/config.yaml" }) }),
|
|
1617
|
+
isFocused && /* @__PURE__ */ jsx5(Box5, { marginTop: 2, children: /* @__PURE__ */ jsxs5(
|
|
1618
|
+
Text5,
|
|
1619
|
+
{
|
|
1620
|
+
backgroundColor: colors.primary,
|
|
1621
|
+
color: "#ffffff",
|
|
1622
|
+
bold: true,
|
|
1623
|
+
children: [
|
|
1624
|
+
" ",
|
|
1625
|
+
"Edit",
|
|
1626
|
+
" "
|
|
1627
|
+
]
|
|
1628
|
+
}
|
|
1629
|
+
) }),
|
|
1630
|
+
isFocused && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "enter edit \xB7 esc back" }) }),
|
|
1631
|
+
!isFocused && /* @__PURE__ */ jsx5(Box5, { marginTop: 2, children: /* @__PURE__ */ jsx5(Text5, { color: colors.textDim, children: "press enter to edit" }) })
|
|
1632
|
+
] });
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// src/commands/tui/views/WelcomeScreen.tsx
|
|
1636
|
+
import { Box as Box6, Text as Text6, useInput } from "ink";
|
|
1637
|
+
import { useState, useMemo } from "react";
|
|
1638
|
+
|
|
1639
|
+
// src/lib/quotes.ts
|
|
1640
|
+
var quotes = [
|
|
1641
|
+
"I'm probably the droid you are looking for.",
|
|
1642
|
+
"These are the skills you're looking for.",
|
|
1643
|
+
"I have a bad feeling about this, we might be too efficient.",
|
|
1644
|
+
"May the source be with you.",
|
|
1645
|
+
"The Force is strong with this one.",
|
|
1646
|
+
"This is the way.",
|
|
1647
|
+
"I'm fluent in over six million forms of communication. - `/comments`",
|
|
1648
|
+
"Much to learn, you still have. - `/coach`",
|
|
1649
|
+
"If into the archives you go, only knowledge will you find. - `/project`"
|
|
1650
|
+
];
|
|
1651
|
+
function getRandomQuote() {
|
|
1652
|
+
const index = Math.floor(Math.random() * quotes.length);
|
|
1653
|
+
return quotes[index];
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// src/commands/tui/views/WelcomeScreen.tsx
|
|
1657
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1658
|
+
function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isUpdating }) {
|
|
1659
|
+
const [selectedButton, setSelectedButton] = useState(0);
|
|
1660
|
+
const welcomeQuote = useMemo(() => getRandomQuote(), []);
|
|
1661
|
+
useInput((input, key) => {
|
|
1662
|
+
if (isUpdating) return;
|
|
1663
|
+
if (updateInfo.hasUpdate) {
|
|
1664
|
+
if (key.leftArrow) {
|
|
1665
|
+
setSelectedButton((prev) => Math.max(0, prev - 1));
|
|
1666
|
+
}
|
|
1667
|
+
if (key.rightArrow) {
|
|
1668
|
+
setSelectedButton((prev) => Math.min(2, prev + 1));
|
|
1669
|
+
}
|
|
1670
|
+
if (key.return) {
|
|
1671
|
+
if (selectedButton === 0) {
|
|
1672
|
+
onUpdate();
|
|
1673
|
+
} else if (selectedButton === 1) {
|
|
1674
|
+
onAlways();
|
|
1675
|
+
} else {
|
|
1676
|
+
onContinue();
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
if (input === "q") {
|
|
1680
|
+
onExit();
|
|
1681
|
+
}
|
|
1682
|
+
} else {
|
|
1683
|
+
if (key.return) {
|
|
1684
|
+
onContinue();
|
|
1685
|
+
}
|
|
1686
|
+
if (input === "q") {
|
|
1687
|
+
onExit();
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1691
|
+
const hasUpdate = updateInfo.hasUpdate && updateInfo.latestVersion;
|
|
1692
|
+
return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs6(
|
|
1693
|
+
Box6,
|
|
1694
|
+
{
|
|
1695
|
+
flexDirection: "column",
|
|
1696
|
+
alignItems: "center",
|
|
1697
|
+
borderStyle: "single",
|
|
1698
|
+
borderColor: hasUpdate ? "#eab308" : colors.border,
|
|
1699
|
+
paddingX: 4,
|
|
1700
|
+
paddingY: 1,
|
|
1701
|
+
children: [
|
|
1702
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
1703
|
+
/* @__PURE__ */ jsxs6(Text6, { children: [
|
|
1704
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
|
|
1705
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.text, children: "droid" }),
|
|
1706
|
+
/* @__PURE__ */ jsxs6(Text6, { color: colors.textDim, children: [
|
|
1707
|
+
" v",
|
|
1708
|
+
updateInfo.currentVersion
|
|
1709
|
+
] })
|
|
1710
|
+
] }),
|
|
1711
|
+
/* @__PURE__ */ jsxs6(Text6, { children: [
|
|
1712
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "\u2551 " }),
|
|
1713
|
+
/* @__PURE__ */ jsx6(Text6, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
|
|
1714
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " " }),
|
|
1715
|
+
/* @__PURE__ */ jsx6(Text6, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
|
|
1716
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " \u2551 " }),
|
|
1717
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textMuted, children: "Droid, teaching your AI new tricks" })
|
|
1718
|
+
] }),
|
|
1719
|
+
/* @__PURE__ */ jsxs6(Text6, { children: [
|
|
1720
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D " }),
|
|
1721
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "github.com/Orderful/droid" })
|
|
1722
|
+
] })
|
|
1723
|
+
] }),
|
|
1724
|
+
hasUpdate ? /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
1725
|
+
/* @__PURE__ */ jsxs6(Box6, { marginTop: 2, marginBottom: 1, flexDirection: "column", alignItems: "center", children: [
|
|
1726
|
+
/* @__PURE__ */ jsx6(Text6, { color: "#eab308", italic: true, children: '"The odds of functioning optimally without this' }),
|
|
1727
|
+
/* @__PURE__ */ jsx6(Text6, { color: "#eab308", italic: true, children: 'update are approximately 3,720 to 1."' })
|
|
1728
|
+
] }),
|
|
1729
|
+
/* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.textMuted, children: [
|
|
1730
|
+
"v",
|
|
1731
|
+
updateInfo.currentVersion,
|
|
1732
|
+
" \u2192 v",
|
|
1733
|
+
updateInfo.latestVersion
|
|
1734
|
+
] }) }),
|
|
1735
|
+
isUpdating ? /* @__PURE__ */ jsx6(Text6, { color: "#eab308", children: "Updating..." }) : /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", children: [
|
|
1736
|
+
/* @__PURE__ */ jsxs6(
|
|
1737
|
+
Text6,
|
|
1738
|
+
{
|
|
1739
|
+
backgroundColor: selectedButton === 0 ? "#eab308" : colors.bgSelected,
|
|
1740
|
+
color: selectedButton === 0 ? "#000000" : colors.textMuted,
|
|
1741
|
+
bold: selectedButton === 0,
|
|
1742
|
+
children: [
|
|
1743
|
+
" ",
|
|
1744
|
+
"Update",
|
|
1745
|
+
" "
|
|
1746
|
+
]
|
|
1747
|
+
}
|
|
1748
|
+
),
|
|
1749
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
1750
|
+
/* @__PURE__ */ jsxs6(
|
|
1751
|
+
Text6,
|
|
1752
|
+
{
|
|
1753
|
+
backgroundColor: selectedButton === 1 ? "#eab308" : colors.bgSelected,
|
|
1754
|
+
color: selectedButton === 1 ? "#000000" : colors.textMuted,
|
|
1755
|
+
bold: selectedButton === 1,
|
|
1756
|
+
children: [
|
|
1757
|
+
" ",
|
|
1758
|
+
"Always",
|
|
1759
|
+
" "
|
|
1760
|
+
]
|
|
1761
|
+
}
|
|
1762
|
+
),
|
|
1763
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
1764
|
+
/* @__PURE__ */ jsxs6(
|
|
1765
|
+
Text6,
|
|
1766
|
+
{
|
|
1767
|
+
backgroundColor: selectedButton === 2 ? colors.bgSelected : void 0,
|
|
1768
|
+
color: selectedButton === 2 ? colors.text : colors.textMuted,
|
|
1769
|
+
bold: selectedButton === 2,
|
|
1770
|
+
children: [
|
|
1771
|
+
" ",
|
|
1772
|
+
"Skip",
|
|
1773
|
+
" "
|
|
1774
|
+
]
|
|
1775
|
+
}
|
|
1776
|
+
)
|
|
1777
|
+
] }),
|
|
1778
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
|
|
1779
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
1780
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 2, marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: ` ${welcomeQuote} ` }) }),
|
|
1781
|
+
/* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter" })
|
|
1782
|
+
] })
|
|
1783
|
+
]
|
|
1784
|
+
}
|
|
1785
|
+
) });
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// src/commands/tui/views/ToolUpdatePrompt.tsx
|
|
1789
|
+
import { Box as Box7, Text as Text7, useInput as useInput2 } from "ink";
|
|
1790
|
+
import { useState as useState2 } from "react";
|
|
1791
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1792
|
+
function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdating }) {
|
|
1793
|
+
const [selectedButton, setSelectedButton] = useState2(0);
|
|
1794
|
+
useInput2((input, key) => {
|
|
1795
|
+
if (isUpdating) return;
|
|
1796
|
+
if (key.leftArrow) {
|
|
1797
|
+
setSelectedButton((prev) => Math.max(0, prev - 1));
|
|
1798
|
+
}
|
|
1799
|
+
if (key.rightArrow) {
|
|
1800
|
+
setSelectedButton((prev) => Math.min(2, prev + 1));
|
|
1801
|
+
}
|
|
1802
|
+
if (key.return) {
|
|
1803
|
+
if (selectedButton === 0) {
|
|
1804
|
+
onUpdateAll();
|
|
1805
|
+
} else if (selectedButton === 1) {
|
|
1806
|
+
onAlways();
|
|
1807
|
+
} else {
|
|
1808
|
+
onSkip();
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (input === "q") {
|
|
1812
|
+
onSkip();
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
const buttons = [
|
|
1816
|
+
{ label: "Update All", action: onUpdateAll },
|
|
1817
|
+
{ label: "Always", action: onAlways },
|
|
1818
|
+
{ label: "Skip", action: onSkip }
|
|
1819
|
+
];
|
|
1820
|
+
return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs7(
|
|
1821
|
+
Box7,
|
|
1822
|
+
{
|
|
1823
|
+
flexDirection: "column",
|
|
1824
|
+
alignItems: "center",
|
|
1825
|
+
borderStyle: "single",
|
|
1826
|
+
borderColor: colors.primary,
|
|
1827
|
+
paddingX: 4,
|
|
1828
|
+
paddingY: 1,
|
|
1829
|
+
children: [
|
|
1830
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
1831
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
1832
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
|
|
1833
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.text, children: "Tool Updates" })
|
|
1834
|
+
] }),
|
|
1835
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
1836
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u2551 " }),
|
|
1837
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.primary, children: "\u25CF" }),
|
|
1838
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: " " }),
|
|
1839
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.primary, children: "\u25CF" }),
|
|
1840
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: " \u2551 " }),
|
|
1841
|
+
/* @__PURE__ */ jsxs7(Text7, { color: colors.textMuted, children: [
|
|
1842
|
+
toolUpdates.length,
|
|
1843
|
+
" tool",
|
|
1844
|
+
toolUpdates.length > 1 ? "s have" : " has",
|
|
1845
|
+
" updates available"
|
|
1846
|
+
] })
|
|
1847
|
+
] }),
|
|
1848
|
+
/* @__PURE__ */ jsx7(Text7, { children: /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D" }) })
|
|
1849
|
+
] }),
|
|
1850
|
+
/* @__PURE__ */ jsxs7(Box7, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [
|
|
1851
|
+
toolUpdates.slice(0, 5).map((tool) => /* @__PURE__ */ jsxs7(Text7, { color: colors.textMuted, children: [
|
|
1852
|
+
/* @__PURE__ */ jsx7(Text7, { color: colors.primary, children: "\u2191" }),
|
|
1853
|
+
" ",
|
|
1854
|
+
tool.name,
|
|
1855
|
+
/* @__PURE__ */ jsxs7(Text7, { color: colors.textDim, children: [
|
|
1856
|
+
" ",
|
|
1857
|
+
tool.installedVersion,
|
|
1858
|
+
" \u2192 ",
|
|
1859
|
+
tool.bundledVersion
|
|
1860
|
+
] })
|
|
1861
|
+
] }, tool.name)),
|
|
1862
|
+
toolUpdates.length > 5 && /* @__PURE__ */ jsxs7(Text7, { color: colors.textDim, children: [
|
|
1863
|
+
"...and ",
|
|
1864
|
+
toolUpdates.length - 5,
|
|
1865
|
+
" more"
|
|
1866
|
+
] })
|
|
1867
|
+
] }),
|
|
1868
|
+
isUpdating ? /* @__PURE__ */ jsx7(Text7, { color: colors.primary, children: "Updating tools..." }) : /* @__PURE__ */ jsx7(Box7, { flexDirection: "row", children: buttons.map((button, index) => /* @__PURE__ */ jsxs7(
|
|
1869
|
+
Text7,
|
|
1870
|
+
{
|
|
1871
|
+
backgroundColor: selectedButton === index ? colors.primary : colors.bgSelected,
|
|
1872
|
+
color: selectedButton === index ? "#000000" : colors.textMuted,
|
|
1873
|
+
bold: selectedButton === index,
|
|
1874
|
+
children: [
|
|
1875
|
+
" ",
|
|
1876
|
+
button.label,
|
|
1877
|
+
" "
|
|
1878
|
+
]
|
|
1879
|
+
},
|
|
1880
|
+
button.label
|
|
1881
|
+
)) }),
|
|
1882
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
|
|
1883
|
+
]
|
|
1884
|
+
}
|
|
1885
|
+
) });
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// src/commands/tui/views/SetupScreen.tsx
|
|
1889
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
1890
|
+
import TextInput from "ink-text-input";
|
|
1891
|
+
import { useState as useState3 } from "react";
|
|
1892
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1893
|
+
var platformOptions = [
|
|
1894
|
+
{ label: "Claude Code", value: "claude-code" /* ClaudeCode */ },
|
|
1895
|
+
{ label: "OpenCode", value: "opencode" /* OpenCode */ }
|
|
1896
|
+
];
|
|
1897
|
+
function SetupScreen({ onComplete, onSkip, initialConfig }) {
|
|
1898
|
+
const [step, setStep] = useState3("platform");
|
|
1899
|
+
const [platform, setPlatform] = useState3(
|
|
1900
|
+
initialConfig?.platform || "claude-code" /* ClaudeCode */
|
|
1901
|
+
);
|
|
1902
|
+
const [userMention, setUserMention] = useState3(initialConfig?.user_mention || "@user");
|
|
1903
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1904
|
+
const [error, setError] = useState3(null);
|
|
1905
|
+
const existingAutoUpdate = getAutoUpdateConfig();
|
|
1906
|
+
const [autoUpdateTools, setAutoUpdateTools] = useState3(existingAutoUpdate.tools);
|
|
1907
|
+
const [autoUpdateApp, setAutoUpdateApp] = useState3(existingAutoUpdate.app);
|
|
1908
|
+
const [autoUpdateSelectedIndex, setAutoUpdateSelectedIndex] = useState3(0);
|
|
1909
|
+
const steps = ["platform", "user_mention", "auto_update", "confirm"];
|
|
1910
|
+
const stepIndex = steps.indexOf(step);
|
|
1911
|
+
const totalSteps = steps.length - 1;
|
|
1912
|
+
const handleUserMentionSubmit = () => {
|
|
1913
|
+
const mention = userMention.startsWith("@") ? userMention : `@${userMention}`;
|
|
1914
|
+
setUserMention(mention);
|
|
1915
|
+
setError(null);
|
|
1916
|
+
setStep("auto_update");
|
|
1917
|
+
setAutoUpdateSelectedIndex(0);
|
|
1918
|
+
};
|
|
1919
|
+
useInput3((input, key) => {
|
|
1920
|
+
if (key.escape) {
|
|
1921
|
+
setStep("platform");
|
|
1922
|
+
setSelectedIndex(0);
|
|
1923
|
+
}
|
|
1924
|
+
}, { isActive: step === "user_mention" });
|
|
1925
|
+
useInput3((input, key) => {
|
|
1926
|
+
if (key.escape) {
|
|
1927
|
+
if (step === "platform") {
|
|
1928
|
+
onSkip();
|
|
1929
|
+
} else if (step === "auto_update") {
|
|
1930
|
+
setStep("user_mention");
|
|
1931
|
+
} else if (step === "confirm") {
|
|
1932
|
+
setStep("auto_update");
|
|
1933
|
+
setAutoUpdateSelectedIndex(0);
|
|
1934
|
+
}
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
if (step === "platform") {
|
|
1938
|
+
if (key.upArrow) setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1939
|
+
if (key.downArrow) setSelectedIndex((prev) => Math.min(platformOptions.length - 1, prev + 1));
|
|
1940
|
+
if (key.return) {
|
|
1941
|
+
setPlatform(platformOptions[selectedIndex].value);
|
|
1942
|
+
setStep("user_mention");
|
|
1943
|
+
}
|
|
1944
|
+
} else if (step === "auto_update") {
|
|
1945
|
+
if (key.upArrow) setAutoUpdateSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1946
|
+
if (key.downArrow) setAutoUpdateSelectedIndex((prev) => Math.min(2, prev + 1));
|
|
1947
|
+
if (key.return) {
|
|
1948
|
+
if (autoUpdateSelectedIndex === 0) {
|
|
1949
|
+
setAutoUpdateTools(!autoUpdateTools);
|
|
1950
|
+
} else if (autoUpdateSelectedIndex === 1) {
|
|
1951
|
+
setAutoUpdateApp(!autoUpdateApp);
|
|
1952
|
+
} else {
|
|
1953
|
+
setStep("confirm");
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
} else if (step === "confirm") {
|
|
1957
|
+
if (key.return) {
|
|
1958
|
+
const existingConfig = loadConfig();
|
|
1959
|
+
const config = {
|
|
1960
|
+
...existingConfig,
|
|
1961
|
+
platform,
|
|
1962
|
+
user_mention: userMention,
|
|
1963
|
+
output_preference: "terminal" /* Terminal */
|
|
1964
|
+
// Default to terminal
|
|
1965
|
+
};
|
|
1966
|
+
saveConfig(config);
|
|
1967
|
+
setAutoUpdateConfig({ tools: autoUpdateTools, app: autoUpdateApp });
|
|
1968
|
+
configurePlatformPermissions(platform);
|
|
1969
|
+
onComplete();
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}, { isActive: step !== "user_mention" });
|
|
1973
|
+
const renderHeader = () => /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1974
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "[" }),
|
|
1975
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "\u25CF" }),
|
|
1976
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " " }),
|
|
1977
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "\u25CF" }),
|
|
1978
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "] " }),
|
|
1979
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, bold: true, children: "droid setup" }),
|
|
1980
|
+
/* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
|
|
1981
|
+
" \xB7 Step ",
|
|
1982
|
+
Math.min(stepIndex + 1, totalSteps),
|
|
1983
|
+
" of ",
|
|
1984
|
+
totalSteps
|
|
1985
|
+
] })
|
|
1986
|
+
] }) });
|
|
1987
|
+
if (step === "platform") {
|
|
1988
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
1989
|
+
renderHeader(),
|
|
1990
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: "Which platform are you using?" }),
|
|
1991
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginTop: 1, children: platformOptions.map((option, index) => /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1992
|
+
/* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
|
|
1993
|
+
index === selectedIndex ? ">" : " ",
|
|
1994
|
+
" "
|
|
1995
|
+
] }),
|
|
1996
|
+
/* @__PURE__ */ jsx8(Text8, { color: index === selectedIndex ? colors.text : colors.textMuted, children: option.label })
|
|
1997
|
+
] }, option.value)) }),
|
|
1998
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2191\u2193 select \xB7 enter next \xB7 esc skip" }) })
|
|
1999
|
+
] });
|
|
2000
|
+
}
|
|
2001
|
+
if (step === "user_mention") {
|
|
2002
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
2003
|
+
renderHeader(),
|
|
2004
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: "What @mention should be used for you?" }),
|
|
2005
|
+
/* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
|
|
2006
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "> " }),
|
|
2007
|
+
/* @__PURE__ */ jsx8(
|
|
2008
|
+
TextInput,
|
|
2009
|
+
{
|
|
2010
|
+
value: userMention,
|
|
2011
|
+
onChange: setUserMention,
|
|
2012
|
+
onSubmit: handleUserMentionSubmit,
|
|
2013
|
+
placeholder: "@user"
|
|
2014
|
+
}
|
|
2015
|
+
)
|
|
2016
|
+
] }),
|
|
2017
|
+
error && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.error, children: error }) }),
|
|
2018
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "enter next \xB7 esc back" }) })
|
|
2019
|
+
] });
|
|
2020
|
+
}
|
|
2021
|
+
if (step === "auto_update") {
|
|
2022
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
2023
|
+
renderHeader(),
|
|
2024
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: "Configure auto-update behaviour" }),
|
|
2025
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
2026
|
+
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2027
|
+
/* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
|
|
2028
|
+
autoUpdateSelectedIndex === 0 ? ">" : " ",
|
|
2029
|
+
" "
|
|
2030
|
+
] }),
|
|
2031
|
+
/* @__PURE__ */ jsxs8(Text8, { color: autoUpdateSelectedIndex === 0 ? colors.text : colors.textMuted, children: [
|
|
2032
|
+
"[",
|
|
2033
|
+
autoUpdateTools ? "x" : " ",
|
|
2034
|
+
"] Auto-update tools"
|
|
2035
|
+
] })
|
|
2036
|
+
] }) }),
|
|
2037
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " Update tools automatically when droid starts" }),
|
|
2038
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2039
|
+
/* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
|
|
2040
|
+
autoUpdateSelectedIndex === 1 ? ">" : " ",
|
|
2041
|
+
" "
|
|
2042
|
+
] }),
|
|
2043
|
+
/* @__PURE__ */ jsxs8(Text8, { color: autoUpdateSelectedIndex === 1 ? colors.text : colors.textMuted, children: [
|
|
2044
|
+
"[",
|
|
2045
|
+
autoUpdateApp ? "x" : " ",
|
|
2046
|
+
"] Auto-update app"
|
|
2047
|
+
] })
|
|
2048
|
+
] }) }),
|
|
2049
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " Update droid automatically when a new version is available" })
|
|
2050
|
+
] }),
|
|
2051
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 2, children: /* @__PURE__ */ jsxs8(
|
|
2052
|
+
Text8,
|
|
2053
|
+
{
|
|
2054
|
+
backgroundColor: autoUpdateSelectedIndex === 2 ? colors.primary : colors.bgSelected,
|
|
2055
|
+
color: autoUpdateSelectedIndex === 2 ? "#ffffff" : colors.textMuted,
|
|
2056
|
+
bold: autoUpdateSelectedIndex === 2,
|
|
2057
|
+
children: [
|
|
2058
|
+
" ",
|
|
2059
|
+
"Next",
|
|
2060
|
+
" "
|
|
2061
|
+
]
|
|
2062
|
+
}
|
|
2063
|
+
) }),
|
|
2064
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2191\u2193 select \xB7 enter toggle/next \xB7 esc back" }) })
|
|
2065
|
+
] });
|
|
2066
|
+
}
|
|
2067
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
2068
|
+
renderHeader(),
|
|
2069
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, bold: true, children: "Review your settings" }),
|
|
2070
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
2071
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2072
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "Platform: " }),
|
|
2073
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: platform === "claude-code" /* ClaudeCode */ ? "Claude Code" : "OpenCode" })
|
|
2074
|
+
] }),
|
|
2075
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2076
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "Your @mention: " }),
|
|
2077
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: userMention })
|
|
2078
|
+
] }),
|
|
2079
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2080
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "Auto-update tools: " }),
|
|
2081
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: autoUpdateTools ? "enabled" : "disabled" })
|
|
2082
|
+
] }),
|
|
2083
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2084
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "Auto-update app: " }),
|
|
2085
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.text, children: autoUpdateApp ? "enabled" : "disabled" })
|
|
2086
|
+
] })
|
|
2087
|
+
] }),
|
|
2088
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 2, children: /* @__PURE__ */ jsxs8(Text8, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: [
|
|
2089
|
+
" ",
|
|
2090
|
+
"Save",
|
|
2091
|
+
" "
|
|
2092
|
+
] }) }),
|
|
2093
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "enter save \xB7 esc back" }) })
|
|
2094
|
+
] });
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// src/commands/tui/views/ReadmeViewer.tsx
|
|
2098
|
+
import { Box as Box9, Text as Text10, useInput as useInput4 } from "ink";
|
|
2099
|
+
import { useState as useState4, useMemo as useMemo2 } from "react";
|
|
2100
|
+
|
|
2101
|
+
// src/commands/tui/components/Markdown.tsx
|
|
2102
|
+
import { Text as Text9 } from "ink";
|
|
2103
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2104
|
+
function MarkdownLine({ line, inCodeBlock }) {
|
|
2105
|
+
if (inCodeBlock) {
|
|
2106
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "#a5d6ff", children: line || " " });
|
|
2107
|
+
}
|
|
2108
|
+
if (line.startsWith("```")) {
|
|
2109
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: line });
|
|
2110
|
+
}
|
|
2111
|
+
if (line.startsWith("# ")) {
|
|
2112
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.text, bold: true, children: line.slice(2) });
|
|
2113
|
+
}
|
|
2114
|
+
if (line.startsWith("## ")) {
|
|
2115
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.text, bold: true, children: line.slice(3) });
|
|
2116
|
+
}
|
|
2117
|
+
if (line.startsWith("### ")) {
|
|
2118
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "#c9d1d9", bold: true, children: line.slice(4) });
|
|
2119
|
+
}
|
|
2120
|
+
if (line === "---") {
|
|
2121
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: line });
|
|
2122
|
+
}
|
|
2123
|
+
if (line.match(/^[\s]*[-*]\s/)) {
|
|
2124
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.textMuted, children: line });
|
|
2125
|
+
}
|
|
2126
|
+
if (line.startsWith(">")) {
|
|
2127
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "#8b949e", italic: true, children: line });
|
|
2128
|
+
}
|
|
2129
|
+
if (line.includes("|")) {
|
|
2130
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.textMuted, children: line });
|
|
2131
|
+
}
|
|
2132
|
+
return /* @__PURE__ */ jsx9(Text9, { color: colors.textMuted, children: line || " " });
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// src/commands/tui/views/ReadmeViewer.tsx
|
|
2136
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2137
|
+
function ReadmeViewer({ title, content, onClose }) {
|
|
2138
|
+
const [scrollOffset, setScrollOffset] = useState4(0);
|
|
2139
|
+
const lines = useMemo2(() => content.split("\n"), [content]);
|
|
2140
|
+
const maxVisible = 20;
|
|
2141
|
+
const lineStates = useMemo2(() => {
|
|
2142
|
+
const states = [];
|
|
2143
|
+
let inCode = false;
|
|
2144
|
+
for (const line of lines) {
|
|
2145
|
+
if (line.startsWith("```")) {
|
|
2146
|
+
states.push(false);
|
|
2147
|
+
inCode = !inCode;
|
|
2148
|
+
} else {
|
|
2149
|
+
states.push(inCode);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return states;
|
|
2153
|
+
}, [lines]);
|
|
2154
|
+
const maxOffset = Math.max(0, lines.length - maxVisible + 1);
|
|
2155
|
+
useInput4((input, key) => {
|
|
2156
|
+
if (key.escape) {
|
|
2157
|
+
onClose();
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
if (key.upArrow) {
|
|
2161
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
2162
|
+
}
|
|
2163
|
+
if (key.downArrow) {
|
|
2164
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + 1));
|
|
2165
|
+
}
|
|
2166
|
+
if (key.pageDown || input === " ") {
|
|
2167
|
+
setScrollOffset((prev) => Math.min(maxOffset, prev + maxVisible));
|
|
2168
|
+
}
|
|
2169
|
+
if (key.pageUp) {
|
|
2170
|
+
setScrollOffset((prev) => Math.max(0, prev - maxVisible));
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
const showTopIndicator = scrollOffset > 0;
|
|
2174
|
+
const contentLines = maxVisible - (showTopIndicator ? 1 : 0);
|
|
2175
|
+
const endIndex = Math.min(scrollOffset + contentLines, lines.length);
|
|
2176
|
+
const showBottomIndicator = endIndex < lines.length;
|
|
2177
|
+
const actualContentLines = contentLines - (showBottomIndicator ? 1 : 0);
|
|
2178
|
+
const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
|
|
2179
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
|
|
2180
|
+
/* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
|
|
2181
|
+
/* @__PURE__ */ jsx10(Text10, { color: colors.text, bold: true, children: title }),
|
|
2182
|
+
/* @__PURE__ */ jsxs9(Text10, { color: colors.textDim, children: [
|
|
2183
|
+
" \xB7 ",
|
|
2184
|
+
lines.length,
|
|
2185
|
+
" lines"
|
|
2186
|
+
] })
|
|
2187
|
+
] }),
|
|
2188
|
+
/* @__PURE__ */ jsxs9(
|
|
2189
|
+
Box9,
|
|
2190
|
+
{
|
|
2191
|
+
flexDirection: "column",
|
|
2192
|
+
borderStyle: "single",
|
|
2193
|
+
borderColor: colors.border,
|
|
2194
|
+
paddingX: 1,
|
|
2195
|
+
children: [
|
|
2196
|
+
showTopIndicator && /* @__PURE__ */ jsxs9(Text10, { color: colors.textDim, children: [
|
|
2197
|
+
"\u2191 ",
|
|
2198
|
+
scrollOffset,
|
|
2199
|
+
" more lines"
|
|
2200
|
+
] }),
|
|
2201
|
+
visibleLines.map((line, i) => /* @__PURE__ */ jsx10(MarkdownLine, { line, inCodeBlock: lineStates[scrollOffset + i] }, scrollOffset + i)),
|
|
2202
|
+
showBottomIndicator && /* @__PURE__ */ jsxs9(Text10, { color: colors.textDim, children: [
|
|
2203
|
+
"\u2193 ",
|
|
2204
|
+
lines.length - scrollOffset - actualContentLines,
|
|
2205
|
+
" more lines"
|
|
2206
|
+
] })
|
|
2207
|
+
]
|
|
2208
|
+
}
|
|
2209
|
+
),
|
|
2210
|
+
/* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "\u2191\u2193 scroll \xB7 space/pgdn page \xB7 esc back" }) })
|
|
2211
|
+
] });
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// src/commands/tui/views/ToolExplorer.tsx
|
|
2215
|
+
import { Box as Box10, Text as Text11, useInput as useInput5 } from "ink";
|
|
2216
|
+
import { useState as useState5, useMemo as useMemo3 } from "react";
|
|
2217
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
2218
|
+
import { join as join8 } from "path";
|
|
2219
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2220
|
+
function ToolExplorer({ tool, onViewSource, onClose }) {
|
|
2221
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
2222
|
+
const items = useMemo3(() => {
|
|
2223
|
+
const result = [];
|
|
2224
|
+
const toolDir = getBundledToolsDir();
|
|
2225
|
+
for (const skill of tool.includes.skills) {
|
|
2226
|
+
result.push({
|
|
2227
|
+
type: "skill",
|
|
2228
|
+
name: skill.name,
|
|
2229
|
+
path: join8(toolDir, tool.name, "skills", skill.name, "SKILL.md")
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
for (const cmd of tool.includes.commands) {
|
|
2233
|
+
result.push({
|
|
2234
|
+
type: "command",
|
|
2235
|
+
name: `/${cmd}`,
|
|
2236
|
+
path: join8(toolDir, tool.name, "commands", `${cmd}.md`)
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
for (const agent of tool.includes.agents) {
|
|
2240
|
+
result.push({
|
|
2241
|
+
type: "agent",
|
|
2242
|
+
name: agent,
|
|
2243
|
+
path: join8(toolDir, tool.name, "agents", agent, "AGENT.md")
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
return result;
|
|
2247
|
+
}, [tool]);
|
|
2248
|
+
useInput5((input, key) => {
|
|
2249
|
+
if (key.escape) {
|
|
2250
|
+
onClose();
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
if (key.leftArrow || key.upArrow) {
|
|
2254
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
2255
|
+
}
|
|
2256
|
+
if (key.rightArrow || key.downArrow) {
|
|
2257
|
+
setSelectedIndex((prev) => Math.min(items.length - 1, prev + 1));
|
|
2258
|
+
}
|
|
2259
|
+
if (key.return && items.length > 0) {
|
|
2260
|
+
const item = items[selectedIndex];
|
|
2261
|
+
if (existsSync6(item.path)) {
|
|
2262
|
+
const content = readFileSync7(item.path, "utf-8");
|
|
2263
|
+
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
2264
|
+
} else {
|
|
2265
|
+
const yamlPath = item.path.replace(".md", ".yaml");
|
|
2266
|
+
if (existsSync6(yamlPath)) {
|
|
2267
|
+
const content = readFileSync7(yamlPath, "utf-8");
|
|
2268
|
+
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
const skillItems = items.filter((i) => i.type === "skill");
|
|
2274
|
+
const commandItems = items.filter((i) => i.type === "command");
|
|
2275
|
+
const agentItems = items.filter((i) => i.type === "agent");
|
|
2276
|
+
const getItemIndex = (item) => items.indexOf(item);
|
|
2277
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
|
|
2278
|
+
/* @__PURE__ */ jsx11(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
2279
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: "[" }),
|
|
2280
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.primary, children: "\u25CF" }),
|
|
2281
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: " " }),
|
|
2282
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.primary, children: "\u25CF" }),
|
|
2283
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: "] " }),
|
|
2284
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.text, bold: true, children: tool.name })
|
|
2285
|
+
] }) }),
|
|
2286
|
+
/* @__PURE__ */ jsx11(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.textMuted, children: tool.description }) }),
|
|
2287
|
+
skillItems.length > 0 && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
2288
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.skill, bold: true, children: "Skills" }),
|
|
2289
|
+
/* @__PURE__ */ jsx11(Box10, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: skillItems.map((item) => {
|
|
2290
|
+
const idx = getItemIndex(item);
|
|
2291
|
+
const isSelected = selectedIndex === idx;
|
|
2292
|
+
return /* @__PURE__ */ jsx11(Box10, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx11(
|
|
2293
|
+
Text11,
|
|
2294
|
+
{
|
|
2295
|
+
backgroundColor: isSelected ? colors.skill : colors.bgSelected,
|
|
2296
|
+
color: isSelected ? "#000000" : colors.skill,
|
|
2297
|
+
bold: isSelected,
|
|
2298
|
+
children: ` ${item.name} `
|
|
2299
|
+
}
|
|
2300
|
+
) }, item.name);
|
|
2301
|
+
}) })
|
|
2302
|
+
] }),
|
|
2303
|
+
commandItems.length > 0 && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
2304
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.command, bold: true, children: "Commands" }),
|
|
2305
|
+
/* @__PURE__ */ jsx11(Box10, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: commandItems.map((item) => {
|
|
2306
|
+
const idx = getItemIndex(item);
|
|
2307
|
+
const isSelected = selectedIndex === idx;
|
|
2308
|
+
return /* @__PURE__ */ jsx11(Box10, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx11(
|
|
2309
|
+
Text11,
|
|
2310
|
+
{
|
|
2311
|
+
backgroundColor: isSelected ? colors.command : colors.bgSelected,
|
|
2312
|
+
color: isSelected ? "#000000" : colors.command,
|
|
2313
|
+
bold: isSelected,
|
|
2314
|
+
children: ` ${item.name} `
|
|
2315
|
+
}
|
|
2316
|
+
) }, item.name);
|
|
2317
|
+
}) })
|
|
2318
|
+
] }),
|
|
2319
|
+
agentItems.length > 0 && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
2320
|
+
/* @__PURE__ */ jsx11(Text11, { color: colors.agent, bold: true, children: "Agents" }),
|
|
2321
|
+
/* @__PURE__ */ jsx11(Box10, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: agentItems.map((item) => {
|
|
2322
|
+
const idx = getItemIndex(item);
|
|
2323
|
+
const isSelected = selectedIndex === idx;
|
|
2324
|
+
return /* @__PURE__ */ jsx11(Box10, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx11(
|
|
2325
|
+
Text11,
|
|
2326
|
+
{
|
|
2327
|
+
backgroundColor: isSelected ? colors.agent : colors.bgSelected,
|
|
2328
|
+
color: isSelected ? "#000000" : colors.agent,
|
|
2329
|
+
bold: isSelected,
|
|
2330
|
+
children: ` ${item.name} `
|
|
2331
|
+
}
|
|
2332
|
+
) }, item.name);
|
|
2333
|
+
}) })
|
|
2334
|
+
] }),
|
|
2335
|
+
/* @__PURE__ */ jsx11(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: "\u2190\u2192 navigate \xB7 enter view source \xB7 esc back" }) })
|
|
2336
|
+
] });
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// src/commands/tui/views/SkillConfigScreen.tsx
|
|
2340
|
+
import { Box as Box11, Text as Text12, useInput as useInput6 } from "ink";
|
|
2341
|
+
import TextInput2 from "ink-text-input";
|
|
2342
|
+
import { useState as useState6, useMemo as useMemo4 } from "react";
|
|
2343
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2344
|
+
function SkillConfigScreen({ skill, onComplete, onCancel }) {
|
|
2345
|
+
const configSchema = skill.config_schema || {};
|
|
2346
|
+
const configKeys = Object.keys(configSchema);
|
|
2347
|
+
const initialOverrides = useMemo4(() => loadSkillOverrides(skill.name), [skill.name]);
|
|
2348
|
+
const [values, setValues] = useState6(() => {
|
|
2349
|
+
const initial = {};
|
|
2350
|
+
for (const key of configKeys) {
|
|
2351
|
+
const option = configSchema[key];
|
|
2352
|
+
initial[key] = initialOverrides[key] ?? option.default ?? (option.type === "boolean" /* Boolean */ ? false : "");
|
|
2353
|
+
}
|
|
2354
|
+
return initial;
|
|
2355
|
+
});
|
|
2356
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
2357
|
+
const [scrollOffset, setScrollOffset] = useState6(0);
|
|
2358
|
+
const [editingField, setEditingField] = useState6(null);
|
|
2359
|
+
const [editValue, setEditValue] = useState6("");
|
|
2360
|
+
const [editingSelect, setEditingSelect] = useState6(null);
|
|
2361
|
+
const [selectOptionIndex, setSelectOptionIndex] = useState6(0);
|
|
2362
|
+
const totalItems = configKeys.length + 1;
|
|
2363
|
+
const handleSave = () => {
|
|
2364
|
+
saveSkillOverrides(skill.name, values);
|
|
2365
|
+
onComplete();
|
|
2366
|
+
};
|
|
2367
|
+
const handleSubmitEdit = () => {
|
|
2368
|
+
if (editingField) {
|
|
2369
|
+
setValues((prev) => ({ ...prev, [editingField]: editValue }));
|
|
2370
|
+
setEditingField(null);
|
|
2371
|
+
setEditValue("");
|
|
2372
|
+
}
|
|
2373
|
+
};
|
|
2374
|
+
useInput6((input, key) => {
|
|
2375
|
+
if (key.escape) {
|
|
2376
|
+
setEditingField(null);
|
|
2377
|
+
setEditValue("");
|
|
2378
|
+
}
|
|
2379
|
+
}, { isActive: editingField !== null });
|
|
2380
|
+
useInput6((input, key) => {
|
|
2381
|
+
if (!editingSelect) return;
|
|
2382
|
+
const option = configSchema[editingSelect];
|
|
2383
|
+
if (!option?.options) return;
|
|
2384
|
+
if (key.escape) {
|
|
2385
|
+
setEditingSelect(null);
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
if (key.leftArrow || key.upArrow) {
|
|
2389
|
+
setSelectOptionIndex((prev) => Math.max(0, prev - 1));
|
|
2390
|
+
}
|
|
2391
|
+
if (key.rightArrow || key.downArrow) {
|
|
2392
|
+
setSelectOptionIndex((prev) => Math.min(option.options.length - 1, prev + 1));
|
|
2393
|
+
}
|
|
2394
|
+
if (key.return) {
|
|
2395
|
+
setValues((prev) => ({ ...prev, [editingSelect]: option.options[selectOptionIndex] }));
|
|
2396
|
+
setEditingSelect(null);
|
|
2397
|
+
}
|
|
2398
|
+
}, { isActive: editingSelect !== null });
|
|
2399
|
+
useInput6((input, key) => {
|
|
2400
|
+
if (key.escape) {
|
|
2401
|
+
onCancel();
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
if (key.upArrow) {
|
|
2405
|
+
setSelectedIndex((prev) => {
|
|
2406
|
+
const newIndex = Math.max(0, prev - 1);
|
|
2407
|
+
if (newIndex < scrollOffset) {
|
|
2408
|
+
setScrollOffset(newIndex);
|
|
2409
|
+
}
|
|
2410
|
+
return newIndex;
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
if (key.downArrow) {
|
|
2414
|
+
setSelectedIndex((prev) => {
|
|
2415
|
+
const newIndex = Math.min(totalItems - 1, prev + 1);
|
|
2416
|
+
if (newIndex >= scrollOffset + MAX_VISIBLE_CONFIG_ITEMS) {
|
|
2417
|
+
setScrollOffset(newIndex - MAX_VISIBLE_CONFIG_ITEMS + 1);
|
|
2418
|
+
}
|
|
2419
|
+
return newIndex;
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
if (key.return) {
|
|
2423
|
+
if (selectedIndex === configKeys.length) {
|
|
2424
|
+
handleSave();
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
const key2 = configKeys[selectedIndex];
|
|
2428
|
+
const option = configSchema[key2];
|
|
2429
|
+
if (option.type === "boolean" /* Boolean */) {
|
|
2430
|
+
setValues((prev) => ({ ...prev, [key2]: !prev[key2] }));
|
|
2431
|
+
} else if (option.type === "select" /* Select */ && option.options) {
|
|
2432
|
+
const currentValue = String(values[key2] || option.options[0]);
|
|
2433
|
+
const currentIndex = option.options.indexOf(currentValue);
|
|
2434
|
+
setSelectOptionIndex(currentIndex >= 0 ? currentIndex : 0);
|
|
2435
|
+
setEditingSelect(key2);
|
|
2436
|
+
} else if (option.type === "string" /* String */) {
|
|
2437
|
+
setEditingField(key2);
|
|
2438
|
+
setEditValue(String(values[key2] || ""));
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
}, { isActive: editingField === null && editingSelect === null });
|
|
2442
|
+
if (configKeys.length === 0) {
|
|
2443
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
|
|
2444
|
+
/* @__PURE__ */ jsx12(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2445
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "[" }),
|
|
2446
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
|
|
2447
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " " }),
|
|
2448
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
|
|
2449
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "] " }),
|
|
2450
|
+
/* @__PURE__ */ jsxs11(Text12, { color: colors.text, bold: true, children: [
|
|
2451
|
+
"configure ",
|
|
2452
|
+
skill.name
|
|
2453
|
+
] })
|
|
2454
|
+
] }) }),
|
|
2455
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textMuted, children: "This skill has no configuration options." }),
|
|
2456
|
+
/* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "esc to go back" }) })
|
|
2457
|
+
] });
|
|
2458
|
+
}
|
|
2459
|
+
const visibleEndIndex = Math.min(scrollOffset + MAX_VISIBLE_CONFIG_ITEMS, totalItems);
|
|
2460
|
+
const visibleConfigKeys = configKeys.slice(scrollOffset, Math.min(scrollOffset + MAX_VISIBLE_CONFIG_ITEMS, configKeys.length));
|
|
2461
|
+
const showSaveButton = visibleEndIndex > configKeys.length || scrollOffset + MAX_VISIBLE_CONFIG_ITEMS > configKeys.length;
|
|
2462
|
+
const showTopIndicator = scrollOffset > 0;
|
|
2463
|
+
const showBottomIndicator = scrollOffset + MAX_VISIBLE_CONFIG_ITEMS < totalItems;
|
|
2464
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
|
|
2465
|
+
/* @__PURE__ */ jsx12(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2466
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "[" }),
|
|
2467
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
|
|
2468
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " " }),
|
|
2469
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
|
|
2470
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "] " }),
|
|
2471
|
+
/* @__PURE__ */ jsxs11(Text12, { color: colors.text, bold: true, children: [
|
|
2472
|
+
"configure ",
|
|
2473
|
+
skill.name
|
|
2474
|
+
] })
|
|
2475
|
+
] }) }),
|
|
2476
|
+
/* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
2477
|
+
showTopIndicator && /* @__PURE__ */ jsx12(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
|
|
2478
|
+
" \u2191 ",
|
|
2479
|
+
scrollOffset,
|
|
2480
|
+
" more"
|
|
2481
|
+
] }) }),
|
|
2482
|
+
visibleConfigKeys.map((configKey) => {
|
|
2483
|
+
const actualIndex = configKeys.indexOf(configKey);
|
|
2484
|
+
const option = configSchema[configKey];
|
|
2485
|
+
const isSelected = selectedIndex === actualIndex;
|
|
2486
|
+
const isEditing = editingField === configKey;
|
|
2487
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
|
|
2488
|
+
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2489
|
+
/* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
|
|
2490
|
+
isSelected ? ">" : " ",
|
|
2491
|
+
" "
|
|
2492
|
+
] }),
|
|
2493
|
+
/* @__PURE__ */ jsx12(Text12, { color: isSelected ? colors.text : colors.textMuted, children: configKey })
|
|
2494
|
+
] }),
|
|
2495
|
+
/* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
|
|
2496
|
+
" ",
|
|
2497
|
+
option.description
|
|
2498
|
+
] }),
|
|
2499
|
+
/* @__PURE__ */ jsxs11(Box11, { children: [
|
|
2500
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " " }),
|
|
2501
|
+
option.type === "boolean" /* Boolean */ ? /* @__PURE__ */ jsxs11(Text12, { color: colors.text, children: [
|
|
2502
|
+
"[",
|
|
2503
|
+
values[configKey] ? "x" : " ",
|
|
2504
|
+
"] ",
|
|
2505
|
+
values[configKey] ? "enabled" : "disabled"
|
|
2506
|
+
] }) : option.type === "select" /* Select */ && option.options ? /* @__PURE__ */ jsx12(Text12, { color: colors.text, children: option.options.map((opt, i) => {
|
|
2507
|
+
const isCurrentValue = String(values[configKey]) === opt;
|
|
2508
|
+
const isEditingThis = editingSelect === configKey;
|
|
2509
|
+
const isHighlighted = isEditingThis && selectOptionIndex === i;
|
|
2510
|
+
return /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2511
|
+
i > 0 && /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " \xB7 " }),
|
|
2512
|
+
/* @__PURE__ */ jsx12(
|
|
2513
|
+
Text12,
|
|
2514
|
+
{
|
|
2515
|
+
color: isHighlighted ? "#ffffff" : isCurrentValue ? colors.primary : colors.textMuted,
|
|
2516
|
+
backgroundColor: isHighlighted ? colors.primary : void 0,
|
|
2517
|
+
children: isCurrentValue && !isEditingThis ? `[${opt}]` : isHighlighted ? ` ${opt} ` : opt
|
|
2518
|
+
}
|
|
2519
|
+
)
|
|
2520
|
+
] }, opt);
|
|
2521
|
+
}) }) : isEditing ? /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
2522
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "> " }),
|
|
2523
|
+
/* @__PURE__ */ jsx12(
|
|
2524
|
+
TextInput2,
|
|
2525
|
+
{
|
|
2526
|
+
value: editValue,
|
|
2527
|
+
onChange: setEditValue,
|
|
2528
|
+
onSubmit: handleSubmitEdit
|
|
2529
|
+
}
|
|
2530
|
+
)
|
|
2531
|
+
] }) : /* @__PURE__ */ jsx12(Text12, { color: colors.text, children: String(values[configKey]) || "(not set)" })
|
|
2532
|
+
] })
|
|
2533
|
+
] }, configKey);
|
|
2534
|
+
}),
|
|
2535
|
+
showSaveButton && /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
2536
|
+
/* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
|
|
2537
|
+
selectedIndex === configKeys.length ? ">" : " ",
|
|
2538
|
+
" "
|
|
2539
|
+
] }),
|
|
2540
|
+
/* @__PURE__ */ jsxs11(
|
|
2541
|
+
Text12,
|
|
2542
|
+
{
|
|
2543
|
+
backgroundColor: selectedIndex === configKeys.length ? colors.primary : void 0,
|
|
2544
|
+
color: selectedIndex === configKeys.length ? "#ffffff" : colors.textMuted,
|
|
2545
|
+
bold: selectedIndex === configKeys.length,
|
|
2546
|
+
children: [
|
|
2547
|
+
" ",
|
|
2548
|
+
"Save",
|
|
2549
|
+
" "
|
|
2550
|
+
]
|
|
2551
|
+
}
|
|
2552
|
+
)
|
|
2553
|
+
] }) }),
|
|
2554
|
+
showBottomIndicator && /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
|
|
2555
|
+
" \u2193 ",
|
|
2556
|
+
totalItems - scrollOffset - MAX_VISIBLE_CONFIG_ITEMS,
|
|
2557
|
+
" more"
|
|
2558
|
+
] }) })
|
|
2559
|
+
] }),
|
|
2560
|
+
/* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: editingField ? "enter save \xB7 esc cancel" : editingSelect ? "\u2190\u2192 choose \xB7 enter select \xB7 esc cancel" : "\u2191\u2193 select \xB7 enter toggle/edit \xB7 esc back" }) })
|
|
2561
|
+
] });
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// src/commands/tui/hooks/useAppUpdate.ts
|
|
2565
|
+
import { useState as useState7, useMemo as useMemo5 } from "react";
|
|
2566
|
+
import { useApp } from "ink";
|
|
2567
|
+
function useAppUpdate({ onUpdateSuccess, onUpdateFailure }) {
|
|
2568
|
+
const { exit } = useApp();
|
|
2569
|
+
const [isUpdating, setIsUpdating] = useState7(false);
|
|
2570
|
+
const updateInfo = useMemo5(() => getUpdateInfo(), []);
|
|
2571
|
+
const handleUpdate = () => {
|
|
2572
|
+
setIsUpdating(true);
|
|
2573
|
+
setTimeout(() => {
|
|
2574
|
+
const result = runUpdate();
|
|
2575
|
+
if (result.success) {
|
|
2576
|
+
const blue = "\x1B[38;2;99;102;241m";
|
|
2577
|
+
const dim = "\x1B[38;2;106;106;106m";
|
|
2578
|
+
const reset = "\x1B[0m";
|
|
2579
|
+
const message = `
|
|
2580
|
+
${dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${reset}
|
|
2581
|
+
|
|
2582
|
+
${dim}\u2554\u2550\u2550\u2550\u2550\u2550\u2557${reset}
|
|
2583
|
+
${dim}\u2551${reset} ${blue}\u25CF${reset} ${blue}\u25CF${reset} ${dim}\u2551${reset} ${blue}"It's quite possible this system${reset}
|
|
2584
|
+
${dim}\u255A\u2550\u2566\u2550\u2566\u2550\u255D${reset} ${blue}is now fully operational."${reset}
|
|
2585
|
+
|
|
2586
|
+
Run ${blue}droid${reset} to start the new version.
|
|
2587
|
+
|
|
2588
|
+
${dim}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${reset}
|
|
2589
|
+
`;
|
|
2590
|
+
onUpdateSuccess(message);
|
|
2591
|
+
exit();
|
|
2592
|
+
} else {
|
|
2593
|
+
setIsUpdating(false);
|
|
2594
|
+
onUpdateFailure(result.message);
|
|
2595
|
+
}
|
|
2596
|
+
}, 100);
|
|
2597
|
+
};
|
|
2598
|
+
const handleAlwaysUpdate = () => {
|
|
2599
|
+
setAutoUpdateConfig({ app: true });
|
|
2600
|
+
handleUpdate();
|
|
2601
|
+
};
|
|
2602
|
+
return {
|
|
2603
|
+
updateInfo,
|
|
2604
|
+
isUpdating,
|
|
2605
|
+
handleUpdate,
|
|
2606
|
+
handleAlwaysUpdate
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
// src/commands/tui/hooks/useToolUpdates.ts
|
|
2611
|
+
import { useState as useState8, useCallback } from "react";
|
|
2612
|
+
function useToolUpdates({ onUpdateComplete }) {
|
|
2613
|
+
const [toolUpdates, setToolUpdates] = useState8([]);
|
|
2614
|
+
const [isUpdatingTools, setIsUpdatingTools] = useState8(false);
|
|
2615
|
+
const [autoUpdatedTools, setAutoUpdatedTools] = useState8([]);
|
|
2616
|
+
const tools = getBundledTools();
|
|
2617
|
+
const ensureSystemTools = useCallback(() => {
|
|
2618
|
+
const systemTools = tools.filter((t) => t.system === true);
|
|
2619
|
+
for (const systemTool of systemTools) {
|
|
2620
|
+
const installed = isToolInstalled(systemTool.name);
|
|
2621
|
+
const updateStatus = getToolUpdateStatus(systemTool.name);
|
|
2622
|
+
if (!installed || updateStatus.hasUpdate) {
|
|
2623
|
+
const primarySkill = systemTool.includes.skills.find((s) => s.required)?.name || systemTool.name;
|
|
2624
|
+
installSkill(primarySkill);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}, [tools]);
|
|
2628
|
+
const checkForUpdates = useCallback(() => {
|
|
2629
|
+
ensureSystemTools();
|
|
2630
|
+
const updates = getToolsWithUpdates();
|
|
2631
|
+
setToolUpdates(updates);
|
|
2632
|
+
const autoUpdateConfig = getAutoUpdateConfig();
|
|
2633
|
+
return {
|
|
2634
|
+
updates,
|
|
2635
|
+
shouldAutoUpdate: autoUpdateConfig.tools && updates.length > 0
|
|
2636
|
+
};
|
|
2637
|
+
}, [ensureSystemTools]);
|
|
2638
|
+
const updateAllTools = useCallback((updates = toolUpdates, silent = false) => {
|
|
2639
|
+
if (!silent) {
|
|
2640
|
+
setIsUpdatingTools(true);
|
|
2641
|
+
}
|
|
2642
|
+
setTimeout(() => {
|
|
2643
|
+
let successCount = 0;
|
|
2644
|
+
let failCount = 0;
|
|
2645
|
+
const updatedNames = [];
|
|
2646
|
+
for (const tool of updates) {
|
|
2647
|
+
const toolManifest = tools.find((t) => t.name === tool.name);
|
|
2648
|
+
if (toolManifest) {
|
|
2649
|
+
const primarySkill = toolManifest.includes.skills.find((s) => s.required)?.name || toolManifest.name;
|
|
2650
|
+
const result = updateSkill(primarySkill);
|
|
2651
|
+
if (result.success) {
|
|
2652
|
+
successCount++;
|
|
2653
|
+
updatedNames.push(tool.name);
|
|
2654
|
+
} else {
|
|
2655
|
+
failCount++;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
setIsUpdatingTools(false);
|
|
2660
|
+
if (silent && updatedNames.length > 0) {
|
|
2661
|
+
setAutoUpdatedTools(updatedNames);
|
|
2662
|
+
}
|
|
2663
|
+
onUpdateComplete({ successCount, failCount, updatedNames, silent });
|
|
2664
|
+
}, 100);
|
|
2665
|
+
}, [toolUpdates, tools, onUpdateComplete]);
|
|
2666
|
+
const enableAutoUpdateAndUpdate = useCallback(() => {
|
|
2667
|
+
setAutoUpdateConfig({ tools: true });
|
|
2668
|
+
updateAllTools();
|
|
2669
|
+
}, [updateAllTools]);
|
|
2670
|
+
return {
|
|
2671
|
+
toolUpdates,
|
|
2672
|
+
isUpdatingTools,
|
|
2673
|
+
autoUpdatedTools,
|
|
2674
|
+
checkForUpdates,
|
|
2675
|
+
updateAllTools,
|
|
2676
|
+
enableAutoUpdateAndUpdate
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// src/commands/tui.tsx
|
|
2681
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2682
|
+
var exitMessage = null;
|
|
2683
|
+
function App() {
|
|
2684
|
+
const { exit } = useApp2();
|
|
2685
|
+
const tabs = [
|
|
2686
|
+
{ id: "tools", label: "Tools" },
|
|
2687
|
+
{ id: "settings", label: "Settings" }
|
|
2688
|
+
];
|
|
2689
|
+
const [activeTab, setActiveTab] = useState9("tools");
|
|
2690
|
+
const [tabIndex, setTabIndex] = useState9(0);
|
|
2691
|
+
const [view, setView] = useState9("welcome");
|
|
2692
|
+
const [selectedIndex, setSelectedIndex] = useState9(0);
|
|
2693
|
+
const [selectedAction, setSelectedAction] = useState9(0);
|
|
2694
|
+
const [scrollOffset, setScrollOffset] = useState9(0);
|
|
2695
|
+
const [message, setMessage] = useState9(null);
|
|
2696
|
+
const [isEditingSettings, setIsEditingSettings] = useState9(false);
|
|
2697
|
+
const [readmeContent, setReadmeContent] = useState9(null);
|
|
2698
|
+
const [previousView, setPreviousView] = useState9("detail");
|
|
2699
|
+
const { updateInfo, isUpdating, handleUpdate, handleAlwaysUpdate } = useAppUpdate({
|
|
2700
|
+
onUpdateSuccess: (msg) => {
|
|
2701
|
+
exitMessage = msg;
|
|
2702
|
+
},
|
|
2703
|
+
onUpdateFailure: (error) => {
|
|
2704
|
+
setMessage({ text: error, type: "error" });
|
|
2705
|
+
if (!configExists()) {
|
|
2706
|
+
setView("setup");
|
|
2707
|
+
} else {
|
|
2708
|
+
setView("menu");
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
const proceedToNextView = () => {
|
|
2713
|
+
if (!configExists()) {
|
|
2714
|
+
setView("setup");
|
|
2715
|
+
} else {
|
|
2716
|
+
setView("menu");
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2719
|
+
const {
|
|
2720
|
+
toolUpdates,
|
|
2721
|
+
isUpdatingTools,
|
|
2722
|
+
autoUpdatedTools,
|
|
2723
|
+
checkForUpdates,
|
|
2724
|
+
updateAllTools,
|
|
2725
|
+
enableAutoUpdateAndUpdate
|
|
2726
|
+
} = useToolUpdates({
|
|
2727
|
+
onUpdateComplete: ({ successCount, failCount, silent }) => {
|
|
2728
|
+
if (successCount > 0) {
|
|
2729
|
+
setMessage({
|
|
2730
|
+
text: silent ? `\u2191 Auto-updated ${successCount} tool${successCount > 1 ? "s" : ""}` : `\u2713 Updated ${successCount} tool${successCount > 1 ? "s" : ""}${failCount > 0 ? `, ${failCount} failed` : ""}`,
|
|
2731
|
+
type: failCount > 0 ? "error" : "success"
|
|
2732
|
+
});
|
|
2733
|
+
}
|
|
2734
|
+
proceedToNextView();
|
|
2735
|
+
}
|
|
2736
|
+
});
|
|
2737
|
+
const checkToolUpdatesAndProceed = () => {
|
|
2738
|
+
const { updates, shouldAutoUpdate } = checkForUpdates();
|
|
2739
|
+
if (shouldAutoUpdate) {
|
|
2740
|
+
updateAllTools(updates, true);
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
if (updates.length > 0) {
|
|
2744
|
+
setView("tool-updates");
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
proceedToNextView();
|
|
2748
|
+
};
|
|
2749
|
+
const handleSkipToolUpdates = () => {
|
|
2750
|
+
proceedToNextView();
|
|
2751
|
+
};
|
|
2752
|
+
const tools = getBundledTools();
|
|
2753
|
+
const skills = getBundledSkills();
|
|
2754
|
+
useInput7((input, key) => {
|
|
2755
|
+
if (message) setMessage(null);
|
|
2756
|
+
if (input === "q") {
|
|
2757
|
+
exit();
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
if (view === "menu") {
|
|
2761
|
+
if (key.leftArrow) {
|
|
2762
|
+
const newIndex = Math.max(0, tabIndex - 1);
|
|
2763
|
+
setTabIndex(newIndex);
|
|
2764
|
+
setActiveTab(tabs[newIndex].id);
|
|
2765
|
+
setSelectedIndex(0);
|
|
2766
|
+
setScrollOffset(0);
|
|
2767
|
+
}
|
|
2768
|
+
if (key.rightArrow) {
|
|
2769
|
+
const newIndex = Math.min(tabs.length - 1, tabIndex + 1);
|
|
2770
|
+
setTabIndex(newIndex);
|
|
2771
|
+
setActiveTab(tabs[newIndex].id);
|
|
2772
|
+
setSelectedIndex(0);
|
|
2773
|
+
setScrollOffset(0);
|
|
2774
|
+
}
|
|
2775
|
+
if (key.upArrow) {
|
|
2776
|
+
setSelectedIndex((prev) => {
|
|
2777
|
+
const newIndex = Math.max(0, prev - 1);
|
|
2778
|
+
if (newIndex < scrollOffset) {
|
|
2779
|
+
setScrollOffset(newIndex);
|
|
2780
|
+
}
|
|
2781
|
+
return newIndex;
|
|
2782
|
+
});
|
|
2783
|
+
setSelectedAction(0);
|
|
2784
|
+
}
|
|
2785
|
+
if (key.downArrow) {
|
|
2786
|
+
const maxIndex = activeTab === "tools" ? tools.length - 1 : 0;
|
|
2787
|
+
setSelectedIndex((prev) => {
|
|
2788
|
+
const newIndex = Math.min(maxIndex, prev + 1);
|
|
2789
|
+
if (newIndex >= scrollOffset + MAX_VISIBLE_ITEMS) {
|
|
2790
|
+
setScrollOffset(newIndex - MAX_VISIBLE_ITEMS + 1);
|
|
2791
|
+
}
|
|
2792
|
+
return newIndex;
|
|
2793
|
+
});
|
|
2794
|
+
setSelectedAction(0);
|
|
2795
|
+
}
|
|
2796
|
+
if (key.return) {
|
|
2797
|
+
if (activeTab === "tools" && tools.length > 0) {
|
|
2798
|
+
setView("detail");
|
|
2799
|
+
} else if (activeTab === "settings") {
|
|
2800
|
+
setView("detail");
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
} else if (view === "detail") {
|
|
2804
|
+
if (key.escape || key.backspace) {
|
|
2805
|
+
setView("menu");
|
|
2806
|
+
setSelectedAction(0);
|
|
2807
|
+
}
|
|
2808
|
+
if (activeTab === "settings") {
|
|
2809
|
+
if (key.return) {
|
|
2810
|
+
setIsEditingSettings(true);
|
|
2811
|
+
setView("setup");
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
if (key.leftArrow && activeTab === "tools") {
|
|
2815
|
+
setSelectedAction((prev) => Math.max(0, prev - 1));
|
|
2816
|
+
}
|
|
2817
|
+
if (key.rightArrow && activeTab === "tools") {
|
|
2818
|
+
let maxActions = 0;
|
|
2819
|
+
const tool = tools[selectedIndex];
|
|
2820
|
+
const installed = tool ? isToolInstalled(tool.name) : false;
|
|
2821
|
+
const hasUpdate = tool ? getToolUpdateStatus(tool.name).hasUpdate : false;
|
|
2822
|
+
const isSystem = tool ? tool.system === true : false;
|
|
2823
|
+
maxActions = installed ? hasUpdate ? isSystem ? 2 : 3 : isSystem ? 1 : 2 : 1;
|
|
2824
|
+
setSelectedAction((prev) => Math.min(maxActions, prev + 1));
|
|
2825
|
+
}
|
|
2826
|
+
if (key.return && activeTab === "tools") {
|
|
2827
|
+
const tool = tools[selectedIndex];
|
|
2828
|
+
if (tool) {
|
|
2829
|
+
const installed = isToolInstalled(tool.name);
|
|
2830
|
+
const toolUpdateStatus = getToolUpdateStatus(tool.name);
|
|
2831
|
+
const isSystemTool = tool.system === true;
|
|
2832
|
+
const toolActions = installed ? [
|
|
2833
|
+
{ id: "explore" },
|
|
2834
|
+
...toolUpdateStatus.hasUpdate ? [{ id: "update" }] : [],
|
|
2835
|
+
{ id: "configure" },
|
|
2836
|
+
...!isSystemTool ? [{ id: "uninstall" }] : []
|
|
2837
|
+
] : [{ id: "explore" }, { id: "install" }];
|
|
2838
|
+
const actionId = toolActions[selectedAction]?.id;
|
|
2839
|
+
if (actionId === "explore") {
|
|
2840
|
+
setView("explorer");
|
|
2841
|
+
} else if (actionId === "update") {
|
|
2842
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || tool.name;
|
|
2843
|
+
const result = updateSkill(primarySkill);
|
|
2844
|
+
setMessage({
|
|
2845
|
+
text: result.success ? `\u2713 Updated ${tool.name}` : `\u2717 ${result.message}`,
|
|
2846
|
+
type: result.success ? "success" : "error"
|
|
2847
|
+
});
|
|
2848
|
+
if (result.success) {
|
|
2849
|
+
setSelectedAction(0);
|
|
2850
|
+
}
|
|
2851
|
+
} else if (actionId === "configure") {
|
|
2852
|
+
setView("configure");
|
|
2853
|
+
} else if (actionId === "uninstall") {
|
|
2854
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || tool.name;
|
|
2855
|
+
const result = uninstallSkill(primarySkill);
|
|
2856
|
+
setMessage({
|
|
2857
|
+
text: result.success ? `\u2713 Uninstalled ${tool.name}` : `\u2717 ${result.message}`,
|
|
2858
|
+
type: result.success ? "success" : "error"
|
|
2859
|
+
});
|
|
2860
|
+
if (result.success) {
|
|
2861
|
+
setView("menu");
|
|
2862
|
+
setSelectedAction(0);
|
|
2863
|
+
}
|
|
2864
|
+
} else if (actionId === "install") {
|
|
2865
|
+
const primarySkill = tool.includes.skills.find((s) => s.required)?.name || tool.name;
|
|
2866
|
+
const result = installSkill(primarySkill);
|
|
2867
|
+
setMessage({
|
|
2868
|
+
text: result.success ? `\u2713 Installed ${tool.name}` : `\u2717 ${result.message}`,
|
|
2869
|
+
type: result.success ? "success" : "error"
|
|
2870
|
+
});
|
|
2871
|
+
if (result.success) {
|
|
2872
|
+
const configSchema = tool.config_schema || {};
|
|
2873
|
+
const hasRequiredConfig = Object.values(configSchema).some(
|
|
2874
|
+
(option) => option.default === void 0
|
|
2875
|
+
);
|
|
2876
|
+
if (hasRequiredConfig) {
|
|
2877
|
+
setView("configure");
|
|
2878
|
+
} else {
|
|
2879
|
+
setView("menu");
|
|
2880
|
+
setSelectedAction(0);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
}, { isActive: view !== "welcome" && view !== "tool-updates" && view !== "setup" && view !== "configure" && view !== "explorer" });
|
|
2888
|
+
const selectedTool = activeTab === "tools" ? tools[selectedIndex] ?? null : null;
|
|
2889
|
+
const selectedSkillForConfig = selectedTool ? skills.find((s) => s.name === (selectedTool.includes.skills.find((sk) => sk.required)?.name || selectedTool.name)) : null;
|
|
2890
|
+
if (view === "welcome") {
|
|
2891
|
+
return /* @__PURE__ */ jsx13(
|
|
2892
|
+
WelcomeScreen,
|
|
2893
|
+
{
|
|
2894
|
+
updateInfo,
|
|
2895
|
+
isUpdating,
|
|
2896
|
+
onUpdate: handleUpdate,
|
|
2897
|
+
onAlways: handleAlwaysUpdate,
|
|
2898
|
+
onExit: exit,
|
|
2899
|
+
onContinue: checkToolUpdatesAndProceed
|
|
2900
|
+
}
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
if (view === "tool-updates" && toolUpdates.length > 0) {
|
|
2904
|
+
return /* @__PURE__ */ jsx13(
|
|
2905
|
+
ToolUpdatePrompt,
|
|
2906
|
+
{
|
|
2907
|
+
toolUpdates,
|
|
2908
|
+
onUpdateAll: () => updateAllTools(),
|
|
2909
|
+
onAlways: enableAutoUpdateAndUpdate,
|
|
2910
|
+
onSkip: handleSkipToolUpdates,
|
|
2911
|
+
isUpdating: isUpdatingTools
|
|
2912
|
+
}
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
if (view === "setup") {
|
|
2916
|
+
return /* @__PURE__ */ jsx13(
|
|
2917
|
+
SetupScreen,
|
|
2918
|
+
{
|
|
2919
|
+
onComplete: () => {
|
|
2920
|
+
setIsEditingSettings(false);
|
|
2921
|
+
setView("menu");
|
|
2922
|
+
},
|
|
2923
|
+
onSkip: () => {
|
|
2924
|
+
setIsEditingSettings(false);
|
|
2925
|
+
setView("menu");
|
|
2926
|
+
},
|
|
2927
|
+
initialConfig: isEditingSettings ? loadConfig() : void 0
|
|
2928
|
+
}
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
if (view === "readme" && readmeContent) {
|
|
2932
|
+
return /* @__PURE__ */ jsx13(
|
|
2933
|
+
ReadmeViewer,
|
|
2934
|
+
{
|
|
2935
|
+
title: readmeContent.title,
|
|
2936
|
+
content: readmeContent.content,
|
|
2937
|
+
onClose: () => {
|
|
2938
|
+
setReadmeContent(null);
|
|
2939
|
+
setView(previousView);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
2944
|
+
if (view === "explorer" && selectedTool) {
|
|
2945
|
+
return /* @__PURE__ */ jsx13(
|
|
2946
|
+
ToolExplorer,
|
|
2947
|
+
{
|
|
2948
|
+
tool: selectedTool,
|
|
2949
|
+
onViewSource: (title, content) => {
|
|
2950
|
+
setPreviousView("explorer");
|
|
2951
|
+
setReadmeContent({ title, content });
|
|
2952
|
+
setView("readme");
|
|
2953
|
+
},
|
|
2954
|
+
onClose: () => {
|
|
2955
|
+
setView("detail");
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
if (view === "configure" && selectedSkillForConfig) {
|
|
2961
|
+
return /* @__PURE__ */ jsx13(
|
|
2962
|
+
SkillConfigScreen,
|
|
2963
|
+
{
|
|
2964
|
+
skill: selectedSkillForConfig,
|
|
2965
|
+
onComplete: () => {
|
|
2966
|
+
setMessage({ text: `\u2713 Configuration saved for ${selectedTool?.name || selectedSkillForConfig.name}`, type: "success" });
|
|
2967
|
+
setView("detail");
|
|
2968
|
+
},
|
|
2969
|
+
onCancel: () => {
|
|
2970
|
+
setView("detail");
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
);
|
|
2974
|
+
}
|
|
2975
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", padding: 1, children: [
|
|
2976
|
+
/* @__PURE__ */ jsxs12(
|
|
2977
|
+
Box12,
|
|
2978
|
+
{
|
|
2979
|
+
flexDirection: "column",
|
|
2980
|
+
width: 44,
|
|
2981
|
+
borderStyle: "single",
|
|
2982
|
+
borderColor: colors.border,
|
|
2983
|
+
children: [
|
|
2984
|
+
/* @__PURE__ */ jsx13(Box12, { paddingX: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
|
|
2985
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "[" }),
|
|
2986
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
|
|
2987
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " " }),
|
|
2988
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
|
|
2989
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "] " }),
|
|
2990
|
+
/* @__PURE__ */ jsx13(Text13, { color: colors.textMuted, children: "droid" }),
|
|
2991
|
+
/* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
|
|
2992
|
+
" v",
|
|
2993
|
+
getVersion()
|
|
2994
|
+
] })
|
|
2995
|
+
] }) }),
|
|
2996
|
+
/* @__PURE__ */ jsx13(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: loadConfig().platform === "claude-code" /* ClaudeCode */ ? "Claude Code" : "OpenCode" }) }),
|
|
2997
|
+
/* @__PURE__ */ jsx13(Box12, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx13(TabBar, { tabs, activeTab }) }),
|
|
2998
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
2999
|
+
activeTab === "tools" && /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
3000
|
+
scrollOffset > 0 && /* @__PURE__ */ jsx13(Box12, { paddingX: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
|
|
3001
|
+
"\u2191 ",
|
|
3002
|
+
scrollOffset,
|
|
3003
|
+
" more"
|
|
3004
|
+
] }) }),
|
|
3005
|
+
tools.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS).map((tool, index) => /* @__PURE__ */ jsx13(
|
|
3006
|
+
ToolItem,
|
|
3007
|
+
{
|
|
3008
|
+
tool,
|
|
3009
|
+
isSelected: scrollOffset + index === selectedIndex,
|
|
3010
|
+
isActive: scrollOffset + index === selectedIndex && view === "detail",
|
|
3011
|
+
wasAutoUpdated: autoUpdatedTools.includes(tool.name)
|
|
3012
|
+
},
|
|
3013
|
+
tool.name
|
|
3014
|
+
)),
|
|
3015
|
+
scrollOffset + MAX_VISIBLE_ITEMS < tools.length && /* @__PURE__ */ jsx13(Box12, { paddingX: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
|
|
3016
|
+
"\u2193 ",
|
|
3017
|
+
tools.length - scrollOffset - MAX_VISIBLE_ITEMS,
|
|
3018
|
+
" more"
|
|
3019
|
+
] }) }),
|
|
3020
|
+
tools.length > MAX_VISIBLE_ITEMS && /* @__PURE__ */ jsx13(Box12, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
|
|
3021
|
+
tools.length,
|
|
3022
|
+
" tools total"
|
|
3023
|
+
] }) })
|
|
3024
|
+
] }),
|
|
3025
|
+
activeTab === "settings" && /* @__PURE__ */ jsx13(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "View and edit config" }) })
|
|
3026
|
+
] }),
|
|
3027
|
+
message && /* @__PURE__ */ jsx13(Box12, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: message.type === "success" ? colors.success : colors.error, children: message.text }) }),
|
|
3028
|
+
/* @__PURE__ */ jsx13(Box12, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: view === "menu" ? "\u2190\u2192 \u2191\u2193 enter q" : "\u2190\u2192 enter esc q" }) })
|
|
3029
|
+
]
|
|
3030
|
+
}
|
|
3031
|
+
),
|
|
3032
|
+
activeTab === "tools" && /* @__PURE__ */ jsx13(ToolDetails, { tool: selectedTool, isFocused: view === "detail", selectedAction }),
|
|
3033
|
+
activeTab === "settings" && /* @__PURE__ */ jsx13(
|
|
3034
|
+
SettingsDetails,
|
|
3035
|
+
{
|
|
3036
|
+
isFocused: view === "detail"
|
|
3037
|
+
}
|
|
3038
|
+
)
|
|
3039
|
+
] });
|
|
3040
|
+
}
|
|
3041
|
+
async function tuiCommand() {
|
|
3042
|
+
process.stdout.write("\x1B[?1049h");
|
|
3043
|
+
process.stdout.write("\x1B[H");
|
|
3044
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx13(App, {}));
|
|
3045
|
+
await waitUntilExit();
|
|
3046
|
+
process.stdout.write("\x1B[?1049l");
|
|
3047
|
+
if (exitMessage) {
|
|
3048
|
+
console.log(exitMessage);
|
|
3049
|
+
exitMessage = null;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
// src/bin/droid.ts
|
|
3054
|
+
var version = getVersion();
|
|
3055
|
+
program.name("droid").description("Droid, teaching your AI new tricks").version(version);
|
|
3056
|
+
program.command("setup").description("Interactive wizard for global configuration").action(setupCommand);
|
|
3057
|
+
program.command("config").description("View or edit configuration").option("-e, --edit", "Open config in editor").option("-g, --get <key>", "Get a specific config value").option("-s, --set <key=value>", "Set a config value").action(configCommand);
|
|
3058
|
+
program.command("tools").description("Browse and manage available tools").action(skillsCommand);
|
|
3059
|
+
program.command("install <tool>").description("Install a tool and run its setup wizard").action(installCommand);
|
|
3060
|
+
program.command("uninstall <tool>").description("Uninstall a tool").action(uninstallCommand);
|
|
3061
|
+
program.command("update").description("Update droid and installed tools").option("--tools", "Only update tools").option("--cli", "Only update the CLI").argument("[tool]", "Update a specific tool").action(updateCommand);
|
|
3062
|
+
program.command("tui").description("Launch interactive TUI dashboard").action(tuiCommand);
|
|
3063
|
+
if (process.argv.length === 2) {
|
|
3064
|
+
tuiCommand();
|
|
3065
|
+
} else {
|
|
3066
|
+
program.parse();
|
|
56
3067
|
}
|
|
57
|
-
//# sourceMappingURL=droid.js.map
|