@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.
@@ -179,11 +179,11 @@ var PrintTerminal = class {
179
179
  };
180
180
 
181
181
  // src/ui/render.tsx
182
- var import_ink12 = require("ink");
182
+ var import_ink14 = require("ink");
183
183
 
184
184
  // src/ui/App.tsx
185
- var import_react13 = require("react");
186
- var import_ink11 = require("ink");
185
+ var import_react16 = require("react");
186
+ var import_ink13 = require("ink");
187
187
  var import_agent_core3 = require("@robota-sdk/agent-core");
188
188
 
189
189
  // src/ui/hooks/useSession.ts
@@ -536,18 +536,9 @@ async function handlePluginCommand(args, addMessage, callbacks) {
536
536
  try {
537
537
  switch (subcommand) {
538
538
  case "":
539
- case void 0: {
540
- const plugins = await callbacks.listInstalled();
541
- if (plugins.length === 0) {
542
- addMessage({ role: "system", content: "No plugins installed." });
543
- } else {
544
- const lines = plugins.map(
545
- (p) => ` ${p.name} \u2014 ${p.description} [${p.enabled ? "enabled" : "disabled"}]`
546
- );
547
- addMessage({ role: "system", content: `Installed plugins:
548
- ${lines.join("\n")}` });
549
- }
550
- return { handled: true };
539
+ case void 0:
540
+ case "manage": {
541
+ return { handled: true, triggerPluginTUI: true };
551
542
  }
552
543
  case "install": {
553
544
  if (!subArgs) {
@@ -694,7 +685,7 @@ async function executeSlashCommand(cmd, args, session, addMessage, clearMessages
694
685
 
695
686
  // src/ui/hooks/useSlashCommands.ts
696
687
  var EXIT_DELAY_MS = 500;
697
- function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks) {
688
+ function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId, pluginCallbacks, setShowPluginTUI) {
698
689
  return (0, import_react3.useCallback)(
699
690
  async (input) => {
700
691
  const parts = input.slice(1).split(/\s+/);
@@ -714,6 +705,9 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
714
705
  pendingModelChangeRef.current = result.pendingModelId;
715
706
  setPendingModelId(result.pendingModelId);
716
707
  }
708
+ if (result.triggerPluginTUI) {
709
+ setShowPluginTUI?.(true);
710
+ }
717
711
  if (result.exitRequested) {
718
712
  setTimeout(() => exit(), EXIT_DELAY_MS);
719
713
  }
@@ -727,7 +721,8 @@ function useSlashCommands(session, addMessage, setMessages, exit, registry, pend
727
721
  registry,
728
722
  pendingModelChangeRef,
729
723
  setPendingModelId,
730
- pluginCallbacks
724
+ pluginCallbacks,
725
+ setShowPluginTUI
731
726
  ]
732
727
  );
733
728
  }
@@ -1111,22 +1106,7 @@ function createBuiltinCommands() {
1111
1106
  { name: "cost", description: "Show session info", source: "builtin" },
1112
1107
  { name: "context", description: "Context window info", source: "builtin" },
1113
1108
  { name: "permissions", description: "Permission rules", source: "builtin" },
1114
- {
1115
- name: "plugin",
1116
- description: "Manage plugins",
1117
- source: "builtin",
1118
- subcommands: [
1119
- { name: "install", description: "Install a plugin (name@marketplace)", source: "builtin" },
1120
- {
1121
- name: "uninstall",
1122
- description: "Uninstall a plugin (name@marketplace)",
1123
- source: "builtin"
1124
- },
1125
- { name: "enable", description: "Enable a plugin (name@marketplace)", source: "builtin" },
1126
- { name: "disable", description: "Disable a plugin (name@marketplace)", source: "builtin" },
1127
- { name: "marketplace", description: "Manage marketplace sources", source: "builtin" }
1128
- ]
1129
- },
1109
+ { name: "plugin", description: "Manage plugins", source: "builtin" },
1130
1110
  { name: "reload-plugins", description: "Reload all plugin resources", source: "builtin" },
1131
1111
  { name: "reset", description: "Delete settings and exit", source: "builtin" },
1132
1112
  { name: "exit", description: "Exit CLI", source: "builtin" }
@@ -1393,18 +1373,50 @@ function usePluginCallbacks(cwd) {
1393
1373
  return {
1394
1374
  listInstalled: async () => {
1395
1375
  const plugins = await loader.loadAll();
1396
- return plugins.map((p) => ({
1397
- name: p.manifest.name,
1398
- description: p.manifest.description,
1399
- enabled: true
1376
+ const enabledMap = settingsStore.getEnabledPlugins();
1377
+ return plugins.map((p) => {
1378
+ const parts = p.pluginDir.split("/");
1379
+ const cacheIdx = parts.indexOf("cache");
1380
+ const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
1381
+ const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
1382
+ return {
1383
+ name: fullId,
1384
+ description: p.manifest.description,
1385
+ enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
1386
+ };
1387
+ });
1388
+ },
1389
+ listAvailablePlugins: async (marketplaceName) => {
1390
+ let manifest;
1391
+ try {
1392
+ manifest = marketplace.fetchManifest(marketplaceName);
1393
+ } catch {
1394
+ return [];
1395
+ }
1396
+ const installed = installer.getInstalledPlugins();
1397
+ const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
1398
+ return manifest.plugins.map((p) => ({
1399
+ name: p.name,
1400
+ description: p.description,
1401
+ installed: installedNames.has(p.name)
1400
1402
  }));
1401
1403
  },
1402
- install: async (pluginId) => {
1404
+ install: async (pluginId, scope) => {
1403
1405
  const [name, marketplaceName] = pluginId.split("@");
1404
1406
  if (!name || !marketplaceName) {
1405
1407
  throw new Error("Plugin ID must be in format: name@marketplace");
1406
1408
  }
1407
- await installer.install(name, marketplaceName);
1409
+ if (scope === "project") {
1410
+ const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
1411
+ const projectInstaller = new import_agent_sdk4.BundlePluginInstaller({
1412
+ pluginsDir: projectPluginsDir,
1413
+ settingsStore,
1414
+ marketplaceClient: marketplace
1415
+ });
1416
+ await projectInstaller.install(name, marketplaceName);
1417
+ } else {
1418
+ await installer.install(name, marketplaceName);
1419
+ }
1408
1420
  },
1409
1421
  uninstall: async (pluginId) => {
1410
1422
  await installer.uninstall(pluginId);
@@ -2149,8 +2161,459 @@ function StreamingIndicator({ text, activeTools }) {
2149
2161
  ] });
2150
2162
  }
2151
2163
 
2152
- // src/ui/App.tsx
2164
+ // src/ui/PluginTUI.tsx
2165
+ var import_react15 = require("react");
2166
+
2167
+ // src/ui/MenuSelect.tsx
2168
+ var import_react13 = require("react");
2169
+ var import_ink11 = require("ink");
2153
2170
  var import_jsx_runtime11 = require("react/jsx-runtime");
2171
+ function MenuSelect({
2172
+ title,
2173
+ items,
2174
+ onSelect,
2175
+ onBack,
2176
+ loading,
2177
+ error
2178
+ }) {
2179
+ const [selected, setSelected] = (0, import_react13.useState)(0);
2180
+ const selectedRef = (0, import_react13.useRef)(0);
2181
+ const resolvedRef = (0, import_react13.useRef)(false);
2182
+ const doSelect = (0, import_react13.useCallback)(
2183
+ (index) => {
2184
+ if (resolvedRef.current || items.length === 0) return;
2185
+ resolvedRef.current = true;
2186
+ onSelect(items[index].value);
2187
+ },
2188
+ [items, onSelect]
2189
+ );
2190
+ (0, import_ink11.useInput)((input, key) => {
2191
+ if (resolvedRef.current) return;
2192
+ if (key.escape) {
2193
+ resolvedRef.current = true;
2194
+ onBack();
2195
+ return;
2196
+ }
2197
+ if (loading || error || items.length === 0) return;
2198
+ if (key.upArrow) {
2199
+ const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
2200
+ selectedRef.current = next;
2201
+ setSelected(next);
2202
+ } else if (key.downArrow) {
2203
+ const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
2204
+ selectedRef.current = next;
2205
+ setSelected(next);
2206
+ } else if (key.return) {
2207
+ doSelect(selectedRef.current);
2208
+ }
2209
+ });
2210
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2211
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
2212
+ 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..." }) }),
2213
+ error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
2214
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
2215
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
2216
+ ] }),
2217
+ !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: [
2218
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
2219
+ i === selected ? "> " : " ",
2220
+ item.label
2221
+ ] }),
2222
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2223
+ " ",
2224
+ item.hint
2225
+ ] })
2226
+ ] }, item.value)) }),
2227
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
2228
+ ] });
2229
+ }
2230
+
2231
+ // src/ui/TextPrompt.tsx
2232
+ var import_react14 = require("react");
2233
+ var import_ink12 = require("ink");
2234
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2235
+ function TextPrompt({
2236
+ title,
2237
+ placeholder,
2238
+ onSubmit,
2239
+ onCancel,
2240
+ validate
2241
+ }) {
2242
+ const [value, setValue] = (0, import_react14.useState)("");
2243
+ const [error, setError] = (0, import_react14.useState)();
2244
+ const resolvedRef = (0, import_react14.useRef)(false);
2245
+ const valueRef = (0, import_react14.useRef)("");
2246
+ const handleSubmit = (0, import_react14.useCallback)(() => {
2247
+ if (resolvedRef.current) return;
2248
+ const trimmed = valueRef.current.trim();
2249
+ if (!trimmed) return;
2250
+ if (validate) {
2251
+ const err = validate(trimmed);
2252
+ if (err) {
2253
+ setError(err);
2254
+ return;
2255
+ }
2256
+ }
2257
+ resolvedRef.current = true;
2258
+ onSubmit(trimmed);
2259
+ }, [validate, onSubmit]);
2260
+ (0, import_ink12.useInput)((input, key) => {
2261
+ if (resolvedRef.current) return;
2262
+ if (key.escape) {
2263
+ resolvedRef.current = true;
2264
+ onCancel();
2265
+ return;
2266
+ }
2267
+ if (key.return) {
2268
+ handleSubmit();
2269
+ return;
2270
+ }
2271
+ if (key.backspace || key.delete) {
2272
+ valueRef.current = valueRef.current.slice(0, -1);
2273
+ setValue(valueRef.current);
2274
+ setError(void 0);
2275
+ return;
2276
+ }
2277
+ if (input && !key.ctrl && !key.meta) {
2278
+ valueRef.current = valueRef.current + input;
2279
+ setValue(valueRef.current);
2280
+ setError(void 0);
2281
+ }
2282
+ });
2283
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2284
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
2285
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
2287
+ 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,
2288
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
2289
+ ] }),
2290
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
2291
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2292
+ ] });
2293
+ }
2294
+
2295
+ // src/ui/plugin-tui-handlers.ts
2296
+ function handleMainSelect(value, nav) {
2297
+ if (value === "marketplace") {
2298
+ nav.push({ screen: "marketplace-list" });
2299
+ } else if (value === "installed") {
2300
+ nav.push({ screen: "installed-list" });
2301
+ }
2302
+ }
2303
+ function handleMarketplaceListSelect(value, nav) {
2304
+ if (value === "__add__") {
2305
+ nav.push({ screen: "marketplace-add" });
2306
+ } else {
2307
+ nav.push({ screen: "marketplace-action", context: { marketplace: value } });
2308
+ }
2309
+ }
2310
+ function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
2311
+ if (value === "browse") {
2312
+ nav.push({ screen: "marketplace-browse", context: { marketplace } });
2313
+ } else if (value === "update") {
2314
+ callbacks.marketplaceUpdate(marketplace).then(() => {
2315
+ nav.notify(`Updated marketplace "${marketplace}".`);
2316
+ nav.pop();
2317
+ }).catch((err) => {
2318
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2319
+ });
2320
+ } else if (value === "remove") {
2321
+ nav.setConfirm({
2322
+ message: `Remove marketplace "${marketplace}" and all its plugins?`,
2323
+ onConfirm: () => {
2324
+ nav.setConfirm(void 0);
2325
+ callbacks.marketplaceRemove(marketplace).then(() => {
2326
+ nav.notify(`Removed marketplace "${marketplace}".`);
2327
+ nav.popN(2);
2328
+ }).catch((err) => {
2329
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2330
+ });
2331
+ },
2332
+ onCancel: () => nav.setConfirm(void 0)
2333
+ });
2334
+ }
2335
+ }
2336
+ function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
2337
+ const fullId = `${value}@${marketplace}`;
2338
+ const item = items.find((i) => i.value === value);
2339
+ if (item?.hint === "installed") {
2340
+ nav.push({ screen: "installed-action", context: { pluginId: fullId } });
2341
+ } else {
2342
+ nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
2343
+ }
2344
+ }
2345
+ function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
2346
+ const scope = value;
2347
+ callbacks.install(pluginId, scope).then(() => {
2348
+ nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
2349
+ nav.popN(2);
2350
+ }).catch((err) => {
2351
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2352
+ });
2353
+ }
2354
+ function handleInstalledListSelect(value, callbacks, nav) {
2355
+ nav.setConfirm({
2356
+ message: `Uninstall plugin "${value}"?`,
2357
+ onConfirm: () => {
2358
+ nav.setConfirm(void 0);
2359
+ callbacks.uninstall(value).then(() => {
2360
+ nav.notify(`Uninstalled plugin "${value}".`);
2361
+ nav.refresh();
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
+ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
2370
+ if (value === "uninstall") {
2371
+ nav.setConfirm({
2372
+ message: `Uninstall plugin "${pluginId}"?`,
2373
+ onConfirm: () => {
2374
+ nav.setConfirm(void 0);
2375
+ callbacks.uninstall(pluginId).then(() => {
2376
+ nav.notify(`Uninstalled plugin "${pluginId}".`);
2377
+ nav.popN(2);
2378
+ }).catch((err) => {
2379
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2380
+ });
2381
+ },
2382
+ onCancel: () => nav.setConfirm(void 0)
2383
+ });
2384
+ }
2385
+ }
2386
+
2387
+ // src/ui/PluginTUI.tsx
2388
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2389
+ function PluginTUI({ callbacks, onClose, addMessage }) {
2390
+ const [stack, setStack] = (0, import_react15.useState)([{ screen: "main" }]);
2391
+ const [items, setItems] = (0, import_react15.useState)([]);
2392
+ const [loading, setLoading] = (0, import_react15.useState)(false);
2393
+ const [error, setError] = (0, import_react15.useState)();
2394
+ const [confirm, setConfirm] = (0, import_react15.useState)();
2395
+ const [refreshCounter, setRefreshCounter] = (0, import_react15.useState)(0);
2396
+ const current = stack[stack.length - 1] ?? { screen: "main" };
2397
+ const push = (0, import_react15.useCallback)((state) => {
2398
+ setStack((prev) => [...prev, state]);
2399
+ setItems([]);
2400
+ setError(void 0);
2401
+ }, []);
2402
+ const pop = (0, import_react15.useCallback)(() => {
2403
+ setStack((prev) => {
2404
+ if (prev.length <= 1) {
2405
+ onClose();
2406
+ return prev;
2407
+ }
2408
+ return prev.slice(0, -1);
2409
+ });
2410
+ setItems([]);
2411
+ setError(void 0);
2412
+ }, [onClose]);
2413
+ const popN = (0, import_react15.useCallback)(
2414
+ (n) => {
2415
+ setStack((prev) => {
2416
+ const next = prev.slice(0, Math.max(1, prev.length - n));
2417
+ if (next.length === 0) {
2418
+ onClose();
2419
+ return prev;
2420
+ }
2421
+ return next;
2422
+ });
2423
+ setItems([]);
2424
+ setError(void 0);
2425
+ },
2426
+ [onClose]
2427
+ );
2428
+ const notify = (0, import_react15.useCallback)(
2429
+ (content) => {
2430
+ addMessage?.({ role: "system", content });
2431
+ },
2432
+ [addMessage]
2433
+ );
2434
+ const refresh = (0, import_react15.useCallback)(() => {
2435
+ setItems([]);
2436
+ setRefreshCounter((c) => c + 1);
2437
+ }, []);
2438
+ const nav = { push, pop, popN, notify, setConfirm, refresh };
2439
+ (0, import_react15.useEffect)(() => {
2440
+ const screen2 = current.screen;
2441
+ if (screen2 === "marketplace-list") {
2442
+ setLoading(true);
2443
+ callbacks.marketplaceList().then((sources) => {
2444
+ const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
2445
+ const sourceItems = sources.map((s) => ({
2446
+ label: s.name,
2447
+ value: s.name,
2448
+ hint: s.type
2449
+ }));
2450
+ setItems([...baseItems, ...sourceItems]);
2451
+ setLoading(false);
2452
+ }).catch((err) => {
2453
+ setError(err instanceof Error ? err.message : String(err));
2454
+ setLoading(false);
2455
+ });
2456
+ } else if (screen2 === "marketplace-browse") {
2457
+ const marketplace = current.context?.marketplace ?? "";
2458
+ setLoading(true);
2459
+ callbacks.listAvailablePlugins(marketplace).then((plugins) => {
2460
+ setItems(
2461
+ plugins.map((p) => ({
2462
+ label: p.name,
2463
+ value: p.name,
2464
+ hint: p.installed ? "installed" : p.description
2465
+ }))
2466
+ );
2467
+ setLoading(false);
2468
+ }).catch((err) => {
2469
+ setError(err instanceof Error ? err.message : String(err));
2470
+ setLoading(false);
2471
+ });
2472
+ } else if (screen2 === "installed-list") {
2473
+ setLoading(true);
2474
+ callbacks.listInstalled().then((plugins) => {
2475
+ setItems(
2476
+ plugins.map((p) => ({
2477
+ label: p.name,
2478
+ value: p.name,
2479
+ hint: p.description
2480
+ }))
2481
+ );
2482
+ setLoading(false);
2483
+ }).catch((err) => {
2484
+ setError(err instanceof Error ? err.message : String(err));
2485
+ setLoading(false);
2486
+ });
2487
+ }
2488
+ }, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
2489
+ const handleSelect = (0, import_react15.useCallback)(
2490
+ (value) => {
2491
+ const screen2 = current.screen;
2492
+ const ctx = current.context;
2493
+ if (screen2 === "main") handleMainSelect(value, nav);
2494
+ else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
2495
+ else if (screen2 === "marketplace-action")
2496
+ handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
2497
+ else if (screen2 === "marketplace-browse")
2498
+ handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
2499
+ else if (screen2 === "marketplace-install-scope")
2500
+ handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2501
+ else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
2502
+ else if (screen2 === "installed-action")
2503
+ handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
2504
+ },
2505
+ [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
2506
+ );
2507
+ const handleTextSubmit = (0, import_react15.useCallback)(
2508
+ (value) => {
2509
+ if (current.screen === "marketplace-add") {
2510
+ callbacks.marketplaceAdd(value).then((name) => {
2511
+ notify(`Added marketplace "${name}" from ${value}.`);
2512
+ pop();
2513
+ }).catch((err) => {
2514
+ notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2515
+ pop();
2516
+ });
2517
+ }
2518
+ },
2519
+ [current.screen, callbacks, notify, pop]
2520
+ );
2521
+ if (confirm) {
2522
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2523
+ ConfirmPrompt,
2524
+ {
2525
+ message: confirm.message,
2526
+ onSelect: (index) => {
2527
+ if (index === 0) confirm.onConfirm();
2528
+ else confirm.onCancel();
2529
+ }
2530
+ }
2531
+ );
2532
+ }
2533
+ const screen = current.screen;
2534
+ if (screen === "marketplace-add") {
2535
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2536
+ TextPrompt,
2537
+ {
2538
+ title: "Add Marketplace Source",
2539
+ placeholder: "owner/repo or git URL",
2540
+ onSubmit: handleTextSubmit,
2541
+ onCancel: pop,
2542
+ validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
2543
+ }
2544
+ );
2545
+ }
2546
+ if (screen === "marketplace-action") {
2547
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2548
+ MenuSelect,
2549
+ {
2550
+ title: `Marketplace: ${current.context?.marketplace ?? ""}`,
2551
+ items: [
2552
+ { label: "Browse plugins", value: "browse" },
2553
+ { label: "Update", value: "update" },
2554
+ { label: "Remove", value: "remove" }
2555
+ ],
2556
+ onSelect: handleSelect,
2557
+ onBack: pop
2558
+ },
2559
+ stack.length
2560
+ );
2561
+ }
2562
+ if (screen === "marketplace-install-scope") {
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2564
+ MenuSelect,
2565
+ {
2566
+ title: `Install scope for "${current.context?.pluginId ?? ""}"`,
2567
+ items: [
2568
+ { label: "User scope", value: "user" },
2569
+ { label: "Project scope", value: "project" }
2570
+ ],
2571
+ onSelect: handleSelect,
2572
+ onBack: pop
2573
+ },
2574
+ stack.length
2575
+ );
2576
+ }
2577
+ if (screen === "installed-action") {
2578
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2579
+ MenuSelect,
2580
+ {
2581
+ title: `Plugin: ${current.context?.pluginId ?? ""}`,
2582
+ items: [{ label: "Uninstall", value: "uninstall" }],
2583
+ onSelect: handleSelect,
2584
+ onBack: pop
2585
+ },
2586
+ stack.length
2587
+ );
2588
+ }
2589
+ const titleMap = {
2590
+ main: "Plugin Management",
2591
+ "marketplace-list": "Marketplace",
2592
+ "marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
2593
+ "installed-list": "Installed Plugins"
2594
+ };
2595
+ const staticItemsMap = {
2596
+ main: [
2597
+ { label: "Marketplace", value: "marketplace" },
2598
+ { label: "Installed Plugins", value: "installed" }
2599
+ ]
2600
+ };
2601
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2602
+ MenuSelect,
2603
+ {
2604
+ title: titleMap[screen] ?? "Plugin Management",
2605
+ items: staticItemsMap[screen] ?? items,
2606
+ onSelect: handleSelect,
2607
+ onBack: pop,
2608
+ loading,
2609
+ error
2610
+ },
2611
+ `${screen}-${stack.length}-${refreshCounter}`
2612
+ );
2613
+ }
2614
+
2615
+ // src/ui/App.tsx
2616
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2154
2617
  var EXIT_DELAY_MS2 = 500;
2155
2618
  function mergeHooksIntoConfig(configHooks, pluginHooks) {
2156
2619
  const pluginKeys = Object.keys(pluginHooks);
@@ -2169,7 +2632,7 @@ function mergeHooksIntoConfig(configHooks, pluginHooks) {
2169
2632
  return merged;
2170
2633
  }
2171
2634
  function App(props) {
2172
- const { exit } = (0, import_ink11.useApp)();
2635
+ const { exit } = (0, import_ink13.useApp)();
2173
2636
  const { registry, pluginHooks } = useCommandRegistry(props.cwd ?? process.cwd());
2174
2637
  const configWithPluginHooks = {
2175
2638
  ...props.config,
@@ -2182,15 +2645,16 @@ function App(props) {
2182
2645
  { ...props, config: configWithPluginHooks }
2183
2646
  );
2184
2647
  const { messages, setMessages, addMessage } = useMessages();
2185
- const [isThinking, setIsThinking] = (0, import_react13.useState)(false);
2648
+ const [isThinking, setIsThinking] = (0, import_react16.useState)(false);
2186
2649
  const initialCtx = session.getContextState();
2187
- const [contextState, setContextState] = (0, import_react13.useState)({
2650
+ const [contextState, setContextState] = (0, import_react16.useState)({
2188
2651
  percentage: initialCtx.usedPercentage,
2189
2652
  usedTokens: initialCtx.usedTokens,
2190
2653
  maxTokens: initialCtx.maxTokens
2191
2654
  });
2192
- const pendingModelChangeRef = (0, import_react13.useRef)(null);
2193
- const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
2655
+ const pendingModelChangeRef = (0, import_react16.useRef)(null);
2656
+ const [pendingModelId, setPendingModelId] = (0, import_react16.useState)(null);
2657
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react16.useState)(false);
2194
2658
  const pluginCallbacks = usePluginCallbacks(props.cwd ?? process.cwd());
2195
2659
  const handleSlashCommand = useSlashCommands(
2196
2660
  session,
@@ -2200,7 +2664,8 @@ function App(props) {
2200
2664
  registry,
2201
2665
  pendingModelChangeRef,
2202
2666
  setPendingModelId,
2203
- pluginCallbacks
2667
+ pluginCallbacks,
2668
+ setShowPluginTUI
2204
2669
  );
2205
2670
  const handleSubmit = useSubmitHandler(
2206
2671
  session,
@@ -2211,33 +2676,33 @@ function App(props) {
2211
2676
  setContextState,
2212
2677
  registry
2213
2678
  );
2214
- (0, import_ink11.useInput)(
2679
+ (0, import_ink13.useInput)(
2215
2680
  (_input, key) => {
2216
2681
  if (key.ctrl && _input === "c") exit();
2217
2682
  if (key.escape && isThinking) session.abort();
2218
2683
  },
2219
- { isActive: !permissionRequest }
2684
+ { isActive: !permissionRequest && !showPluginTUI }
2220
2685
  );
2221
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", children: [
2222
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2223
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: `
2686
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
2687
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2688
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { color: "cyan", bold: true, children: `
2224
2689
  ____ ___ ____ ___ _____ _
2225
2690
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
2226
2691
  | |_) | | | | _ \\| | | || | / _ \\
2227
2692
  | _ <| |_| | |_) | |_| || |/ ___ \\
2228
2693
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
2229
2694
  ` }),
2230
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
2695
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
2231
2696
  " v",
2232
2697
  props.version ?? "0.0.0"
2233
2698
  ] })
2234
2699
  ] }),
2235
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageList, { messages }),
2237
- 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 }) })
2700
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2701
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MessageList, { messages }),
2702
+ 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 }) })
2238
2703
  ] }),
2239
- permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PermissionPrompt, { request: permissionRequest }),
2240
- pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2704
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PermissionPrompt, { request: permissionRequest }),
2705
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2241
2706
  ConfirmPrompt,
2242
2707
  {
2243
2708
  message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
@@ -2265,7 +2730,15 @@ function App(props) {
2265
2730
  }
2266
2731
  }
2267
2732
  ),
2268
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2733
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2734
+ PluginTUI,
2735
+ {
2736
+ callbacks: pluginCallbacks,
2737
+ onClose: () => setShowPluginTUI(false),
2738
+ addMessage: (msg) => addMessage(msg)
2739
+ }
2740
+ ),
2741
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2269
2742
  StatusBar,
2270
2743
  {
2271
2744
  permissionMode: session.getPermissionMode(),
@@ -2278,20 +2751,20 @@ function App(props) {
2278
2751
  contextMaxTokens: contextState.maxTokens
2279
2752
  }
2280
2753
  ),
2281
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2754
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2282
2755
  InputArea,
2283
2756
  {
2284
2757
  onSubmit: handleSubmit,
2285
- isDisabled: isThinking || !!permissionRequest,
2758
+ isDisabled: isThinking || !!permissionRequest || showPluginTUI,
2286
2759
  registry
2287
2760
  }
2288
2761
  ),
2289
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { children: " " })
2762
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Text, { children: " " })
2290
2763
  ] });
2291
2764
  }
2292
2765
 
2293
2766
  // src/ui/render.tsx
2294
- var import_jsx_runtime12 = require("react/jsx-runtime");
2767
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2295
2768
  function renderApp(options) {
2296
2769
  process.on("unhandledRejection", (reason) => {
2297
2770
  process.stderr.write(`
@@ -2302,7 +2775,7 @@ function renderApp(options) {
2302
2775
  `);
2303
2776
  }
2304
2777
  });
2305
- const instance = (0, import_ink12.render)(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(App, { ...options }), {
2778
+ const instance = (0, import_ink14.render)(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(App, { ...options }), {
2306
2779
  exitOnCtrlC: true
2307
2780
  });
2308
2781
  instance.waitUntilExit().catch((err) => {