@robota-sdk/agent-cli 3.0.0-beta.34 → 3.0.0-beta.35

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/dist/node/bin.cjs CHANGED
@@ -163,11 +163,11 @@ var PrintTerminal = class {
163
163
  };
164
164
 
165
165
  // src/ui/render.tsx
166
- var import_ink12 = require("ink");
166
+ var import_ink14 = require("ink");
167
167
 
168
168
  // src/ui/App.tsx
169
- var import_react13 = require("react");
170
- var import_ink11 = require("ink");
169
+ var import_react16 = require("react");
170
+ var import_ink13 = require("ink");
171
171
  var import_agent_core3 = require("@robota-sdk/agent-core");
172
172
 
173
173
  // src/ui/hooks/useSession.ts
@@ -520,18 +520,9 @@ async function handlePluginCommand(args, addMessage, callbacks) {
520
520
  try {
521
521
  switch (subcommand) {
522
522
  case "":
523
- case void 0: {
524
- const plugins = await callbacks.listInstalled();
525
- if (plugins.length === 0) {
526
- addMessage({ role: "system", content: "No plugins installed." });
527
- } else {
528
- const lines = plugins.map(
529
- (p) => ` ${p.name} \u2014 ${p.description} [${p.enabled ? "enabled" : "disabled"}]`
530
- );
531
- addMessage({ role: "system", content: `Installed plugins:
532
- ${lines.join("\n")}` });
533
- }
534
- return { handled: true };
523
+ case void 0:
524
+ case "manage": {
525
+ return { handled: true, triggerPluginTUI: true };
535
526
  }
536
527
  case "install": {
537
528
  if (!subArgs) {
@@ -678,7 +669,7 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
678
669
 
679
670
  // src/ui/hooks/useSlashCommands.ts
680
671
  var EXIT_DELAY_MS = 500;
681
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks) {
672
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks, setShowPluginTUI) {
682
673
  return (0, import_react3.useCallback)(
683
674
  async (input) => {
684
675
  const parts = input.slice(1).split(/\s+/);
@@ -698,6 +689,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
698
689
  pendingModelChangeRef.current = result.pendingModelId;
699
690
  setPendingModelId(result.pendingModelId);
700
691
  }
692
+ if (result.triggerPluginTUI) {
693
+ setShowPluginTUI?.(true);
694
+ }
701
695
  if (result.exitRequested) {
702
696
  setTimeout(() => exit(), EXIT_DELAY_MS);
703
697
  }
@@ -711,7 +705,8 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
711
705
  registry,
712
706
  pendingModelChangeRef,
713
707
  setPendingModelId,
714
- pluginCallbacks
708
+ pluginCallbacks,
709
+ setShowPluginTUI
715
710
  ]
716
711
  );
717
712
  }
@@ -1095,22 +1090,7 @@ function createBuiltinCommands() {
1095
1090
  { name: "cost", description: "Show session info", source: "builtin" },
1096
1091
  { name: "context", description: "Context window info", source: "builtin" },
1097
1092
  { name: "permissions", description: "Permission rules", source: "builtin" },
1098
- {
1099
- name: "plugin",
1100
- description: "Manage plugins",
1101
- source: "builtin",
1102
- subcommands: [
1103
- { name: "install", description: "Install a plugin (name@marketplace)", source: "builtin" },
1104
- {
1105
- name: "uninstall",
1106
- description: "Uninstall a plugin (name@marketplace)",
1107
- source: "builtin"
1108
- },
1109
- { name: "enable", description: "Enable a plugin (name@marketplace)", source: "builtin" },
1110
- { name: "disable", description: "Disable a plugin (name@marketplace)", source: "builtin" },
1111
- { name: "marketplace", description: "Manage marketplace sources", source: "builtin" }
1112
- ]
1113
- },
1093
+ { name: "plugin", description: "Manage plugins", source: "builtin" },
1114
1094
  { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
1115
1095
  { name: "reset", description: "Delete settings and exit", source: "builtin" },
1116
1096
  { name: "exit", description: "Exit CLI", source: "builtin" }
@@ -1377,18 +1357,50 @@ function usePluginCallbacks(cwd) {
1377
1357
  return {
1378
1358
  listInstalled: async () => {
1379
1359
  const plugins = await loader.loadAll();
1380
- return plugins.map((p) => ({
1381
- name: p.manifest.name,
1382
- description: p.manifest.description,
1383
- enabled: true
1360
+ const enabledMap = settingsStore.getEnabledPlugins();
1361
+ return plugins.map((p) => {
1362
+ const parts = p.pluginDir.split("/");
1363
+ const cacheIdx = parts.indexOf("cache");
1364
+ const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
1365
+ const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
1366
+ return {
1367
+ name: fullId,
1368
+ description: p.manifest.description,
1369
+ enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
1370
+ };
1371
+ });
1372
+ },
1373
+ listAvailablePlugins: async (marketplaceName) => {
1374
+ let manifest;
1375
+ try {
1376
+ manifest = marketplace.fetchManifest(marketplaceName);
1377
+ } catch {
1378
+ return [];
1379
+ }
1380
+ const installed = installer.getInstalledPlugins();
1381
+ const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
1382
+ return manifest.plugins.map((p) => ({
1383
+ name: p.name,
1384
+ description: p.description,
1385
+ installed: installedNames.has(p.name)
1384
1386
  }));
1385
1387
  },
1386
- install: async (pluginId) => {
1388
+ install: async (pluginId, scope) => {
1387
1389
  const [name, marketplaceName] = pluginId.split("@");
1388
1390
  if (!name || !marketplaceName) {
1389
1391
  throw new Error("Plugin ID must be in format: name@marketplace");
1390
1392
  }
1391
- await installer.install(name, marketplaceName);
1393
+ if (scope === "project") {
1394
+ const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
1395
+ const projectInstaller = new import_agent_sdk4.BundlePluginInstaller({
1396
+ pluginsDir: projectPluginsDir,
1397
+ settingsStore,
1398
+ marketplaceClient: marketplace
1399
+ });
1400
+ await projectInstaller.install(name, marketplaceName);
1401
+ } else {
1402
+ await installer.install(name, marketplaceName);
1403
+ }
1392
1404
  },
1393
1405
  uninstall: async (pluginId) => {
1394
1406
  await installer.uninstall(pluginId);
@@ -2133,8 +2145,459 @@ function StreamingIndicator({ text, activeTools }) {
2133
2145
  ] });
2134
2146
  }
2135
2147
 
2136
- // src/ui/App.tsx
2148
+ // src/ui/PluginTUI.tsx
2149
+ var import_react15 = require("react");
2150
+
2151
+ // src/ui/MenuSelect.tsx
2152
+ var import_react13 = require("react");
2153
+ var import_ink11 = require("ink");
2137
2154
  var import_jsx_runtime11 = require("react/jsx-runtime");
2155
+ function MenuSelect({
2156
+ title,
2157
+ items,
2158
+ onSelect,
2159
+ onBack,
2160
+ loading,
2161
+ error
2162
+ }) {
2163
+ const [selected, setSelected] = (0, import_react13.useState)(0);
2164
+ const selectedRef = (0, import_react13.useRef)(0);
2165
+ const resolvedRef = (0, import_react13.useRef)(false);
2166
+ const doSelect = (0, import_react13.useCallback)(
2167
+ (index) => {
2168
+ if (resolvedRef.current || items.length === 0) return;
2169
+ resolvedRef.current = true;
2170
+ onSelect(items[index].value);
2171
+ },
2172
+ [items, onSelect]
2173
+ );
2174
+ (0, import_ink11.useInput)((input, key) => {
2175
+ if (resolvedRef.current) return;
2176
+ if (key.escape) {
2177
+ resolvedRef.current = true;
2178
+ onBack();
2179
+ return;
2180
+ }
2181
+ if (loading || error || items.length === 0) return;
2182
+ if (key.upArrow) {
2183
+ const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
2184
+ selectedRef.current = next;
2185
+ setSelected(next);
2186
+ } else if (key.downArrow) {
2187
+ const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
2188
+ selectedRef.current = next;
2189
+ setSelected(next);
2190
+ } else if (key.return) {
2191
+ doSelect(selectedRef.current);
2192
+ }
2193
+ });
2194
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
2196
+ loading && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Loading..." }) }),
2197
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
2198
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
2200
+ ] }),
2201
+ !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { children: [
2202
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
2203
+ i === selected ? "> " : " ",
2204
+ item.label
2205
+ ] }),
2206
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2207
+ " ",
2208
+ item.hint
2209
+ ] })
2210
+ ] }, item.value)) }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
2212
+ ] });
2213
+ }
2214
+
2215
+ // src/ui/TextPrompt.tsx
2216
+ var import_react14 = require("react");
2217
+ var import_ink12 = require("ink");
2218
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2219
+ function TextPrompt({
2220
+ title,
2221
+ placeholder,
2222
+ onSubmit,
2223
+ onCancel,
2224
+ validate
2225
+ }) {
2226
+ const [value, setValue] = (0, import_react14.useState)("");
2227
+ const [error, setError] = (0, import_react14.useState)();
2228
+ const resolvedRef = (0, import_react14.useRef)(false);
2229
+ const valueRef = (0, import_react14.useRef)("");
2230
+ const handleSubmit = (0, import_react14.useCallback)(() => {
2231
+ if (resolvedRef.current) return;
2232
+ const trimmed = valueRef.current.trim();
2233
+ if (!trimmed) return;
2234
+ if (validate) {
2235
+ const err = validate(trimmed);
2236
+ if (err) {
2237
+ setError(err);
2238
+ return;
2239
+ }
2240
+ }
2241
+ resolvedRef.current = true;
2242
+ onSubmit(trimmed);
2243
+ }, [validate, onSubmit]);
2244
+ (0, import_ink12.useInput)((input, key) => {
2245
+ if (resolvedRef.current) return;
2246
+ if (key.escape) {
2247
+ resolvedRef.current = true;
2248
+ onCancel();
2249
+ return;
2250
+ }
2251
+ if (key.return) {
2252
+ handleSubmit();
2253
+ return;
2254
+ }
2255
+ if (key.backspace || key.delete) {
2256
+ valueRef.current = valueRef.current.slice(0, -1);
2257
+ setValue(valueRef.current);
2258
+ setError(void 0);
2259
+ return;
2260
+ }
2261
+ if (input && !key.ctrl && !key.meta) {
2262
+ valueRef.current = valueRef.current + input;
2263
+ setValue(valueRef.current);
2264
+ setError(void 0);
2265
+ }
2266
+ });
2267
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2268
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
2269
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
2270
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
2271
+ value ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: placeholder }) : null,
2272
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
2273
+ ] }),
2274
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
2275
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2276
+ ] });
2277
+ }
2278
+
2279
+ // src/ui/plugin-tui-handlers.ts
2280
+ function handleMainSelect(value, nav) {
2281
+ if (value === "marketplace") {
2282
+ nav.push({ screen: "marketplace-list" });
2283
+ } else if (value === "installed") {
2284
+ nav.push({ screen: "installed-list" });
2285
+ }
2286
+ }
2287
+ function handleMarketplaceListSelect(value, nav) {
2288
+ if (value === "__add__") {
2289
+ nav.push({ screen: "marketplace-add" });
2290
+ } else {
2291
+ nav.push({ screen: "marketplace-action", context: { marketplace: value } });
2292
+ }
2293
+ }
2294
+ function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
2295
+ if (value === "browse") {
2296
+ nav.push({ screen: "marketplace-browse", context: { marketplace } });
2297
+ } else if (value === "update") {
2298
+ callbacks.marketplaceUpdate(marketplace).then(() => {
2299
+ nav.notify(`Updated marketplace "${marketplace}".`);
2300
+ nav.pop();
2301
+ }).catch((err) => {
2302
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2303
+ });
2304
+ } else if (value === "remove") {
2305
+ nav.setConfirm({
2306
+ message: `Remove marketplace "${marketplace}" and all its plugins?`,
2307
+ onConfirm: () => {
2308
+ nav.setConfirm(void 0);
2309
+ callbacks.marketplaceRemove(marketplace).then(() => {
2310
+ nav.notify(`Removed marketplace "${marketplace}".`);
2311
+ nav.popN(2);
2312
+ }).catch((err) => {
2313
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2314
+ });
2315
+ },
2316
+ onCancel: () => nav.setConfirm(void 0)
2317
+ });
2318
+ }
2319
+ }
2320
+ function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
2321
+ const fullId = `${value}@${marketplace}`;
2322
+ const item = items.find((i) => i.value === value);
2323
+ if (item?.hint === "installed") {
2324
+ nav.push({ screen: "installed-action", context: { pluginId: fullId } });
2325
+ } else {
2326
+ nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
2327
+ }
2328
+ }
2329
+ function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
2330
+ const scope = value;
2331
+ callbacks.install(pluginId, scope).then(() => {
2332
+ nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
2333
+ nav.popN(2);
2334
+ }).catch((err) => {
2335
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2336
+ });
2337
+ }
2338
+ function handleInstalledListSelect(value, callbacks, nav) {
2339
+ nav.setConfirm({
2340
+ message: `Uninstall plugin "${value}"?`,
2341
+ onConfirm: () => {
2342
+ nav.setConfirm(void 0);
2343
+ callbacks.uninstall(value).then(() => {
2344
+ nav.notify(`Uninstalled plugin "${value}".`);
2345
+ nav.refresh();
2346
+ }).catch((err) => {
2347
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2348
+ });
2349
+ },
2350
+ onCancel: () => nav.setConfirm(void 0)
2351
+ });
2352
+ }
2353
+ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
2354
+ if (value === "uninstall") {
2355
+ nav.setConfirm({
2356
+ message: `Uninstall plugin "${pluginId}"?`,
2357
+ onConfirm: () => {
2358
+ nav.setConfirm(void 0);
2359
+ callbacks.uninstall(pluginId).then(() => {
2360
+ nav.notify(`Uninstalled plugin "${pluginId}".`);
2361
+ nav.popN(2);
2362
+ }).catch((err) => {
2363
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2364
+ });
2365
+ },
2366
+ onCancel: () => nav.setConfirm(void 0)
2367
+ });
2368
+ }
2369
+ }
2370
+
2371
+ // src/ui/PluginTUI.tsx
2372
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2373
+ function PluginTUI({ callbacks, onClose, addMessage }) {
2374
+ const [stack, setStack] = (0, import_react15.useState)([{ screen: "main" }]);
2375
+ const [items, setItems] = (0, import_react15.useState)([]);
2376
+ const [loading, setLoading] = (0, import_react15.useState)(false);
2377
+ const [error, setError] = (0, import_react15.useState)();
2378
+ const [confirm, setConfirm] = (0, import_react15.useState)();
2379
+ const [refreshCounter, setRefreshCounter] = (0, import_react15.useState)(0);
2380
+ const current = stack[stack.length - 1] ?? { screen: "main" };
2381
+ const push = (0, import_react15.useCallback)((state) => {
2382
+ setStack((prev) => [...prev, state]);
2383
+ setItems([]);
2384
+ setError(void 0);
2385
+ }, []);
2386
+ const pop = (0, import_react15.useCallback)(() => {
2387
+ setStack((prev) => {
2388
+ if (prev.length <= 1) {
2389
+ onClose();
2390
+ return prev;
2391
+ }
2392
+ return prev.slice(0, -1);
2393
+ });
2394
+ setItems([]);
2395
+ setError(void 0);
2396
+ }, [onClose]);
2397
+ const popN = (0, import_react15.useCallback)(
2398
+ (n) => {
2399
+ setStack((prev) => {
2400
+ const next = prev.slice(0, Math.max(1, prev.length - n));
2401
+ if (next.length === 0) {
2402
+ onClose();
2403
+ return prev;
2404
+ }
2405
+ return next;
2406
+ });
2407
+ setItems([]);
2408
+ setError(void 0);
2409
+ },
2410
+ [onClose]
2411
+ );
2412
+ const notify = (0, import_react15.useCallback)(
2413
+ (content) => {
2414
+ addMessage?.({ role: "system", content });
2415
+ },
2416
+ [addMessage]
2417
+ );
2418
+ const refresh = (0, import_react15.useCallback)(() => {
2419
+ setItems([]);
2420
+ setRefreshCounter((c) => c + 1);
2421
+ }, []);
2422
+ const nav = { push, pop, popN, notify, setConfirm, refresh };
2423
+ (0, import_react15.useEffect)(() => {
2424
+ const screen2 = current.screen;
2425
+ if (screen2 === "marketplace-list") {
2426
+ setLoading(true);
2427
+ callbacks.marketplaceList().then((sources) => {
2428
+ const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
2429
+ const sourceItems = sources.map((s) => ({
2430
+ label: s.name,
2431
+ value: s.name,
2432
+ hint: s.type
2433
+ }));
2434
+ setItems([...baseItems, ...sourceItems]);
2435
+ setLoading(false);
2436
+ }).catch((err) => {
2437
+ setError(err instanceof Error ? err.message : String(err));
2438
+ setLoading(false);
2439
+ });
2440
+ } else if (screen2 === "marketplace-browse") {
2441
+ const marketplace = current.context?.marketplace ?? "";
2442
+ setLoading(true);
2443
+ callbacks.listAvailablePlugins(marketplace).then((plugins) => {
2444
+ setItems(
2445
+ plugins.map((p) => ({
2446
+ label: p.name,
2447
+ value: p.name,
2448
+ hint: p.installed ? "installed" : p.description
2449
+ }))
2450
+ );
2451
+ setLoading(false);
2452
+ }).catch((err) => {
2453
+ setError(err instanceof Error ? err.message : String(err));
2454
+ setLoading(false);
2455
+ });
2456
+ } else if (screen2 === "installed-list") {
2457
+ setLoading(true);
2458
+ callbacks.listInstalled().then((plugins) => {
2459
+ setItems(
2460
+ plugins.map((p) => ({
2461
+ label: p.name,
2462
+ value: p.name,
2463
+ hint: p.description
2464
+ }))
2465
+ );
2466
+ setLoading(false);
2467
+ }).catch((err) => {
2468
+ setError(err instanceof Error ? err.message : String(err));
2469
+ setLoading(false);
2470
+ });
2471
+ }
2472
+ }, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
2473
+ const handleSelect = (0, import_react15.useCallback)(
2474
+ (value) => {
2475
+ const screen2 = current.screen;
2476
+ const ctx = current.context;
2477
+ if (screen2 === "main") handleMainSelect(value, nav);
2478
+ else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
2479
+ else if (screen2 === "marketplace-action")
2480
+ handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
2481
+ else if (screen2 === "marketplace-browse")
2482
+ handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
2483
+ else if (screen2 === "marketplace-install-scope")
2484
+ handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2485
+ else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
2486
+ else if (screen2 === "installed-action")
2487
+ handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2488
+ },
2489
+ [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
2490
+ );
2491
+ const handleTextSubmit = (0, import_react15.useCallback)(
2492
+ (value) => {
2493
+ if (current.screen === "marketplace-add") {
2494
+ callbacks.marketplaceAdd(value).then((name) => {
2495
+ notify(`Added marketplace "${name}" from ${value}.`);
2496
+ pop();
2497
+ }).catch((err) => {
2498
+ notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2499
+ pop();
2500
+ });
2501
+ }
2502
+ },
2503
+ [current.screen, callbacks, notify, pop]
2504
+ );
2505
+ if (confirm) {
2506
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2507
+ ConfirmPrompt,
2508
+ {
2509
+ message: confirm.message,
2510
+ onSelect: (index) => {
2511
+ if (index === 0) confirm.onConfirm();
2512
+ else confirm.onCancel();
2513
+ }
2514
+ }
2515
+ );
2516
+ }
2517
+ const screen = current.screen;
2518
+ if (screen === "marketplace-add") {
2519
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2520
+ TextPrompt,
2521
+ {
2522
+ title: "Add Marketplace Source",
2523
+ placeholder: "owner/repo or git URL",
2524
+ onSubmit: handleTextSubmit,
2525
+ onCancel: pop,
2526
+ validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
2527
+ }
2528
+ );
2529
+ }
2530
+ if (screen === "marketplace-action") {
2531
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2532
+ MenuSelect,
2533
+ {
2534
+ title: `Marketplace: ${current.context?.marketplace ?? ""}`,
2535
+ items: [
2536
+ { label: "Browse plugins", value: "browse" },
2537
+ { label: "Update", value: "update" },
2538
+ { label: "Remove", value: "remove" }
2539
+ ],
2540
+ onSelect: handleSelect,
2541
+ onBack: pop
2542
+ },
2543
+ stack.length
2544
+ );
2545
+ }
2546
+ if (screen === "marketplace-install-scope") {
2547
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2548
+ MenuSelect,
2549
+ {
2550
+ title: `Install scope for "${current.context?.pluginId ?? ""}"`,
2551
+ items: [
2552
+ { label: "User scope", value: "user" },
2553
+ { label: "Project scope", value: "project" }
2554
+ ],
2555
+ onSelect: handleSelect,
2556
+ onBack: pop
2557
+ },
2558
+ stack.length
2559
+ );
2560
+ }
2561
+ if (screen === "installed-action") {
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2563
+ MenuSelect,
2564
+ {
2565
+ title: `Plugin: ${current.context?.pluginId ?? ""}`,
2566
+ items: [{ label: "Uninstall", value: "uninstall" }],
2567
+ onSelect: handleSelect,
2568
+ onBack: pop
2569
+ },
2570
+ stack.length
2571
+ );
2572
+ }
2573
+ const titleMap = {
2574
+ main: "Plugin Management",
2575
+ "marketplace-list": "Marketplace",
2576
+ "marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
2577
+ "installed-list": "Installed Plugins"
2578
+ };
2579
+ const staticItemsMap = {
2580
+ main: [
2581
+ { label: "Marketplace", value: "marketplace" },
2582
+ { label: "Installed Plugins", value: "installed" }
2583
+ ]
2584
+ };
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2586
+ MenuSelect,
2587
+ {
2588
+ title: titleMap[screen] ?? "Plugin Management",
2589
+ items: staticItemsMap[screen] ?? items,
2590
+ onSelect: handleSelect,
2591
+ onBack: pop,
2592
+ loading,
2593
+ error
2594
+ },
2595
+ `${screen}-${stack.length}-${refreshCounter}`
2596
+ );
2597
+ }
2598
+
2599
+ // src/ui/App.tsx
2600
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2138
2601
  var EXIT_DELAY_MS2 = 500;
2139
2602
  function mergeHooksIntoConfig(configHooks, pluginHooks) {
2140
2603
  const pluginKeys = Object.keys(pluginHooks);
@@ -2153,7 +2616,7 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
2153
2616
  return merged;
2154
2617
  }
2155
2618
  function App(props) {
2156
- const { exit } = (0, import_ink11.useApp)();
2619
+ const { exit } = (0, import_ink13.useApp)();
2157
2620
  const { registry, pluginHooks } = useCommandRegistry(props.cwd ?? process.cwd());
2158
2621
  const configWithPluginHooks = {
2159
2622
  ...props.config,
@@ -2166,15 +2629,16 @@ function App(props) {
2166
2629
  { ...props, config: configWithPluginHooks }
2167
2630
  );
2168
2631
  const { messages, setMessages, addMessage } = useMessages();
2169
- const [isThinking, setIsThinking] = (0, import_react13.useState)(false);
2632
+ const [isThinking, setIsThinking] = (0, import_react16.useState)(false);
2170
2633
  const initialCtx = session.getContextState();
2171
- const [contextState, setContextState] = (0, import_react13.useState)({
2634
+ const [contextState, setContextState] = (0, import_react16.useState)({
2172
2635
  percentage: initialCtx.usedPercentage,
2173
2636
  usedTokens: initialCtx.usedTokens,
2174
2637
  maxTokens: initialCtx.maxTokens
2175
2638
  });
2176
- const pendingModelChangeRef = (0, import_react13.useRef)(null);
2177
- const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
2639
+ const pendingModelChangeRef = (0, import_react16.useRef)(null);
2640
+ const [pendingModelId, setPendingModelId] = (0, import_react16.useState)(null);
2641
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react16.useState)(false);
2178
2642
  const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
2179
2643
  const handleSlashCommand = useSlashCommands(
2180
2644
  session,
@@ -2184,7 +2648,8 @@ function App(props) {
2184
2648
  registry,
2185
2649
  pendingModelChangeRef,
2186
2650
  setPendingModelId,
2187
- pluginCallbacks
2651
+ pluginCallbacks,
2652
+ setShowPluginTUI
2188
2653
  );
2189
2654
  const handleSubmit = useSubmitHandler(
2190
2655
  session,
@@ -2195,33 +2660,33 @@ function App(props) {
2195
2660
  setContextState,
2196
2661
  registry
2197
2662
  );
2198
- (0, import_ink11.useInput)(
2663
+ (0, import_ink13.useInput)(
2199
2664
  (_input, key) => {
2200
2665
  if (key.ctrl && _input === "c") exit();
2201
2666
  if (key.escape && isThinking) session.abort();
2202
2667
  },
2203
- { isActive: !permissionRequest }
2668
+ { isActive: !permissionRequest && !showPluginTUI }
2204
2669
  );
2205
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", children: [
2206
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2207
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: `
2670
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2671
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2672
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
2208
2673
  ____ ___ ____ ___ _____ _
2209
2674
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
2210
2675
  | |_) | | | | _ \\| | | || | / _ \\
2211
2676
  | _ <| |_| | |_) | |_| || |/ ___ \\
2212
2677
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
2213
2678
  ` }),
2214
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2679
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2215
2680
  " v",
2216
2681
  props.version ?? "0.0.0"
2217
2682
  ] })
2218
2683
  ] }),
2219
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2220
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageList, { messages }),
2221
- isThinking && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2684
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2685
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
2686
+ isThinking && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
2222
2687
  ] }),
2223
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PermissionPrompt, { request: permissionRequest }),
2224
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2688
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
2689
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2225
2690
  ConfirmPrompt,
2226
2691
  {
2227
2692
  message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
@@ -2249,7 +2714,15 @@ function App(props) {
2249
2714
  }
2250
2715
  }
2251
2716
  ),
2252
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2717
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2718
+ PluginTUI,
2719
+ {
2720
+ callbacks: pluginCallbacks,
2721
+ onClose: () => setShowPluginTUI(false),
2722
+ addMessage: (msg) => addMessage(msg)
2723
+ }
2724
+ ),
2725
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2253
2726
  StatusBar,
2254
2727
  {
2255
2728
  permissionMode: session.getPermissionMode(),
@@ -2262,20 +2735,20 @@ function App(props) {
2262
2735
  contextMaxTokens: contextState.maxTokens
2263
2736
  }
2264
2737
  ),
2265
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2738
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2266
2739
  InputArea,
2267
2740
  {
2268
2741
  onSubmit: handleSubmit,
2269
- isDisabled: isThinking || !!permissionRequest,
2742
+ isDisabled: isThinking || !!permissionRequest || showPluginTUI,
2270
2743
  registry
2271
2744
  }
2272
2745
  ),
2273
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: " " })
2746
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
2274
2747
  ] });
2275
2748
  }
2276
2749
 
2277
2750
  // src/ui/render.tsx
2278
- var import_jsx_runtime12 = require("react/jsx-runtime");
2751
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2279
2752
  function renderApp(options) {
2280
2753
  process.on("unhandledRejection", (reason) => {
2281
2754
  process.stderr.write(`
@@ -2286,7 +2759,7 @@ function renderApp(options) {
2286
2759
  `);
2287
2760
  }
2288
2761
  });
2289
- const instance = (0, import_ink12.render)(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(App, { ...options }), {
2762
+ const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
2290
2763
  exitOnCtrlC: true
2291
2764
  });
2292
2765
  instance.waitUntilExit().catch((err) => {