rrce-workflow 0.2.38 → 0.2.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -1
  2. package/dist/index.js +421 -259
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -140,6 +140,7 @@ When you run the wizard on an already-configured project, you'll see:
140
140
  | **Link other project knowledge** | Reference knowledge from other projects in global storage |
141
141
  | **Sync to global storage** | Copy workspace data to global (enables cross-project access) |
142
142
  | **Update from package** | Get latest prompts and templates |
143
+ | **Reconfigure project** | Change storage mode, selected tools, or linked projects without resetting |
143
144
 
144
145
  ---
145
146
 
@@ -291,10 +292,20 @@ Most MCP clients follow similar patterns:
291
292
  Configure which projects to expose:
292
293
 
293
294
  ```bash
294
- npx rrce-workflow mcp # Interactive TUI
295
+ npx rrce-workflow mcp # Interactive TUI Dashboard
295
296
  npx rrce-workflow mcp status # View current status
296
297
  ```
297
298
 
299
+ **Interactive Dashboard Controls:**
300
+ - `p`: Open **Project Configuration** modal (Toggle exposed projects)
301
+ - `i`: Open **IDE Installation** modal (Auto-configure VSCode/Claude)
302
+ - `r`: Reload configuration
303
+ - `c`: Clear logs
304
+ - `?`: Toggle Help & Shortcuts
305
+
306
+ **Unified Context:**
307
+ Projects linked via the main wizard (`Link other project knowledge`) are automatically detected and exposed by the MCP server alongside globally configured projects.
308
+
298
309
  Config stored at `~/.rrce-workflow/mcp.yaml`
299
310
 
300
311
  ---
package/dist/index.js CHANGED
@@ -2141,125 +2141,358 @@ var init_StatusBoard = __esm({
2141
2141
  }
2142
2142
  });
2143
2143
 
2144
- // src/mcp/ui/LogViewer.tsx
2144
+ // src/mcp/ui/Overview.tsx
2145
2145
  import "react";
2146
2146
  import { Box as Box3, Text as Text3 } from "ink";
2147
2147
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2148
- var LogViewer;
2149
- var init_LogViewer = __esm({
2150
- "src/mcp/ui/LogViewer.tsx"() {
2148
+ var Overview;
2149
+ var init_Overview = __esm({
2150
+ "src/mcp/ui/Overview.tsx"() {
2151
2151
  "use strict";
2152
- LogViewer = ({ logs, height }) => {
2153
- const visibleLogs = logs.slice(-height);
2154
- const emptyLines = Math.max(0, height - visibleLogs.length);
2155
- const padding = Array(emptyLines).fill("");
2156
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "dim", paddingX: 1, height: height + 2, flexGrow: 1, children: [
2157
- padding.map((_, i) => /* @__PURE__ */ jsx3(Text3, { children: " " }, `empty-${i}`)),
2158
- visibleLogs.map((log, i) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate-end", children: log }, `log-${i}`))
2152
+ init_Header();
2153
+ init_StatusBoard();
2154
+ Overview = ({ serverStatus, stats }) => {
2155
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
2156
+ /* @__PURE__ */ jsx3(Header, {}),
2157
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx3(
2158
+ StatusBoard,
2159
+ {
2160
+ exposedLabel: `${stats.exposedProjects} / ${stats.totalProjects} projects`,
2161
+ port: serverStatus.port,
2162
+ pid: serverStatus.pid,
2163
+ running: serverStatus.running
2164
+ }
2165
+ ) }),
2166
+ /* @__PURE__ */ jsxs2(Box3, { borderStyle: "round", padding: 1, borderColor: "white", flexDirection: "column", children: [
2167
+ /* @__PURE__ */ jsx3(Text3, { bold: true, underline: true, children: "System Status" }),
2168
+ /* @__PURE__ */ jsxs2(Box3, { marginTop: 1, children: [
2169
+ /* @__PURE__ */ jsx3(Text3, { children: "Integrations Installed: " }),
2170
+ /* @__PURE__ */ jsx3(Text3, { color: stats.installedIntegrations > 0 ? "green" : "yellow", children: stats.installedIntegrations })
2171
+ ] }),
2172
+ /* @__PURE__ */ jsxs2(Box3, { children: [
2173
+ /* @__PURE__ */ jsx3(Text3, { children: "Server Port: " }),
2174
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: serverStatus.port })
2175
+ ] }),
2176
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "dim", children: "Press 'q' to stop server and exit." }) })
2177
+ ] })
2159
2178
  ] });
2160
2179
  };
2161
2180
  }
2162
2181
  });
2163
2182
 
2164
- // src/mcp/ui/CommandBar.tsx
2165
- import "react";
2166
- import { Box as Box4, Text as Text4 } from "ink";
2183
+ // src/mcp/ui/components/SimpleSelect.tsx
2184
+ import { useState } from "react";
2185
+ import { Box as Box4, Text as Text4, useInput } from "ink";
2167
2186
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2168
- var CommandBar;
2169
- var init_CommandBar = __esm({
2170
- "src/mcp/ui/CommandBar.tsx"() {
2187
+ function SimpleSelect({
2188
+ items,
2189
+ onSelect,
2190
+ isMulti = false,
2191
+ initialSelected = [],
2192
+ onSubmit,
2193
+ onCancel,
2194
+ message
2195
+ }) {
2196
+ const [selectedIndex, setSelectedIndex] = useState(0);
2197
+ const [selectedValues, setSelectedValues] = useState(new Set(initialSelected));
2198
+ useInput((input, key) => {
2199
+ if (key.upArrow) {
2200
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : items.length - 1);
2201
+ }
2202
+ if (key.downArrow) {
2203
+ setSelectedIndex((prev) => prev < items.length - 1 ? prev + 1 : 0);
2204
+ }
2205
+ if (input === " " && isMulti) {
2206
+ const item = items[selectedIndex];
2207
+ if (item) {
2208
+ const newSet = new Set(selectedValues);
2209
+ if (newSet.has(item.value)) {
2210
+ newSet.delete(item.value);
2211
+ } else {
2212
+ newSet.add(item.value);
2213
+ }
2214
+ setSelectedValues(newSet);
2215
+ }
2216
+ }
2217
+ if (key.return) {
2218
+ if (isMulti) {
2219
+ onSubmit?.(Array.from(selectedValues));
2220
+ } else {
2221
+ const item = items[selectedIndex];
2222
+ if (item) onSelect(item);
2223
+ }
2224
+ }
2225
+ if (key.escape) {
2226
+ onCancel?.();
2227
+ }
2228
+ });
2229
+ return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, children: [
2230
+ message && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { bold: true, children: message }) }),
2231
+ items.map((item, index) => {
2232
+ const isSelected = index === selectedIndex;
2233
+ const isChecked = isMulti && selectedValues.has(item.value);
2234
+ return /* @__PURE__ */ jsxs3(Box4, { children: [
2235
+ /* @__PURE__ */ jsx4(Text4, { color: isSelected ? "cyan" : "white", children: isSelected ? "> " : " " }),
2236
+ isMulti && /* @__PURE__ */ jsx4(Text4, { color: isChecked ? "green" : "gray", children: isChecked ? "[x] " : "[ ] " }),
2237
+ /* @__PURE__ */ jsx4(Text4, { color: isSelected ? "cyan" : "white", children: item.label })
2238
+ ] }, item.key || String(item.value));
2239
+ }),
2240
+ /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: isMulti ? "Space to toggle, Enter to confirm, Esc to cancel" : "Enter to select, Esc to cancel" }) })
2241
+ ] });
2242
+ }
2243
+ var init_SimpleSelect = __esm({
2244
+ "src/mcp/ui/components/SimpleSelect.tsx"() {
2171
2245
  "use strict";
2172
- CommandBar = () => {
2173
- return /* @__PURE__ */ jsx4(Box4, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs3(Text4, { children: [
2174
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "q" }),
2175
- ":Quit ",
2176
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "p" }),
2177
- ":Projects ",
2178
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "i" }),
2179
- ":Install ",
2180
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "r" }),
2181
- ":Reload ",
2182
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "c" }),
2183
- ":Clear ",
2184
- /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "?" }),
2185
- ":Help"
2186
- ] }) });
2187
- };
2188
2246
  }
2189
2247
  });
2190
2248
 
2191
- // src/mcp/ui/HelpModal.tsx
2192
- import "react";
2249
+ // src/mcp/ui/ProjectsView.tsx
2250
+ import { useState as useState2 } from "react";
2193
2251
  import { Box as Box5, Text as Text5 } from "ink";
2194
2252
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2195
- var HelpModal;
2196
- var init_HelpModal = __esm({
2197
- "src/mcp/ui/HelpModal.tsx"() {
2253
+ var ProjectsView;
2254
+ var init_ProjectsView = __esm({
2255
+ "src/mcp/ui/ProjectsView.tsx"() {
2198
2256
  "use strict";
2199
- HelpModal = ({ onClose }) => {
2200
- return /* @__PURE__ */ jsxs4(Box5, { borderStyle: "double", borderColor: "yellow", padding: 1, flexDirection: "column", children: [
2201
- /* @__PURE__ */ jsx5(Text5, { bold: true, color: "yellow", children: "\u2328\uFE0F Keyboard Shortcuts" }),
2202
- /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
2203
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: "Configuration:" }) }),
2204
- /* @__PURE__ */ jsxs4(Text5, { children: [
2205
- " ",
2206
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "p" }),
2207
- " - Configure Projects (Opens Wizard)"
2208
- ] }),
2209
- /* @__PURE__ */ jsxs4(Text5, { children: [
2210
- " ",
2211
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "i" }),
2212
- " - Install to IDEs (Opens Wizard)"
2213
- ] }),
2214
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: "Server Control:" }) }),
2215
- /* @__PURE__ */ jsxs4(Text5, { children: [
2216
- " ",
2217
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "r" }),
2218
- " - Reload Configuration"
2219
- ] }),
2220
- /* @__PURE__ */ jsxs4(Text5, { children: [
2221
- " ",
2222
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "c" }),
2223
- " - Clear Logs"
2224
- ] }),
2225
- /* @__PURE__ */ jsxs4(Text5, { children: [
2226
- " ",
2227
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "q" }),
2228
- " - Stop Server & Exit"
2229
- ] }),
2230
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: "Other:" }) }),
2231
- /* @__PURE__ */ jsxs4(Text5, { children: [
2232
- " ",
2233
- /* @__PURE__ */ jsx5(Text5, { bold: true, children: "?" }),
2234
- " - Toggle this Help"
2235
- ] })
2236
- ] }),
2237
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "Press '?' again to close" }) })
2257
+ init_SimpleSelect();
2258
+ init_config();
2259
+ init_detection();
2260
+ ProjectsView = () => {
2261
+ const [config, setConfig] = useState2(loadMCPConfig());
2262
+ const allProjects = scanForProjects();
2263
+ const projectItems = allProjects.map((p) => {
2264
+ const projectConfig = config.projects.find(
2265
+ (c) => c.path && c.path === p.dataPath || !c.path && c.name === p.name
2266
+ );
2267
+ const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
2268
+ return {
2269
+ label: p.name + ` (${p.source})` + (p.path ? ` - ${p.path}` : ""),
2270
+ value: p.dataPath,
2271
+ // Unique ID
2272
+ key: p.dataPath,
2273
+ exposed: isExposed
2274
+ };
2275
+ });
2276
+ const initialSelected = projectItems.filter((p) => p.exposed).map((p) => p.value);
2277
+ const handleSubmit = (selectedIds) => {
2278
+ let newConfig = { ...config };
2279
+ projectItems.forEach((item) => {
2280
+ const isSelected = selectedIds.includes(item.value);
2281
+ const project = allProjects.find((p) => p.dataPath === item.value);
2282
+ if (project) {
2283
+ newConfig = setProjectConfig(
2284
+ newConfig,
2285
+ project.name,
2286
+ isSelected,
2287
+ void 0,
2288
+ project.path
2289
+ );
2290
+ }
2291
+ });
2292
+ saveMCPConfig(newConfig);
2293
+ setConfig(newConfig);
2294
+ };
2295
+ return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan", children: [
2296
+ /* @__PURE__ */ jsx5(Text5, { bold: true, color: "cyan", children: " Exposed Projects " }),
2297
+ /* @__PURE__ */ jsx5(Text5, { color: "dim", children: " Select projects to expose via the MCP server. Use Space to toggle, Enter to save." }),
2298
+ /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx5(
2299
+ SimpleSelect,
2300
+ {
2301
+ message: "",
2302
+ items: projectItems,
2303
+ isMulti: true,
2304
+ initialSelected,
2305
+ onSelect: () => {
2306
+ },
2307
+ onSubmit: handleSubmit,
2308
+ onCancel: () => {
2309
+ },
2310
+ itemLimit: 10
2311
+ },
2312
+ JSON.stringify(initialSelected)
2313
+ ) })
2238
2314
  ] });
2239
2315
  };
2240
2316
  }
2241
2317
  });
2242
2318
 
2243
- // src/mcp/ui/Dashboard.tsx
2244
- import "react";
2245
- import { Box as Box6 } from "ink";
2319
+ // src/mcp/ui/components/InstallWizard.tsx
2320
+ import { useState as useState3 } from "react";
2321
+ import { Box as Box6, Text as Text6 } from "ink";
2246
2322
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2247
- var Dashboard;
2248
- var init_Dashboard = __esm({
2249
- "src/mcp/ui/Dashboard.tsx"() {
2323
+ var InstallWizard;
2324
+ var init_InstallWizard = __esm({
2325
+ "src/mcp/ui/components/InstallWizard.tsx"() {
2250
2326
  "use strict";
2251
- init_Header();
2252
- init_StatusBoard();
2253
- init_LogViewer();
2254
- init_CommandBar();
2255
- init_HelpModal();
2256
- Dashboard = ({ logs, exposedLabel, port, pid, running, logHeight, showHelp: showHelp2 }) => {
2257
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 0, children: [
2258
- /* @__PURE__ */ jsx6(Header, {}),
2259
- showHelp2 ? /* @__PURE__ */ jsx6(HelpModal, { onClose: () => {
2260
- } }) : /* @__PURE__ */ jsx6(LogViewer, { logs, height: logHeight }),
2261
- /* @__PURE__ */ jsx6(StatusBoard, { exposedLabel, port, pid, running }),
2262
- /* @__PURE__ */ jsx6(CommandBar, {})
2327
+ init_SimpleSelect();
2328
+ init_install();
2329
+ InstallWizard = ({ workspacePath, onComplete, onCancel }) => {
2330
+ const [status, setStatus] = useState3(checkInstallStatus(workspacePath));
2331
+ const [message, setMessage] = useState3("");
2332
+ const options = [
2333
+ {
2334
+ value: "antigravity",
2335
+ label: "Antigravity IDE",
2336
+ hint: status.antigravity ? "INSTALLED" : "Not installed"
2337
+ },
2338
+ {
2339
+ value: "vscode-global",
2340
+ label: "VSCode (Global Settings)",
2341
+ hint: status.vscodeGlobal ? "INSTALLED" : "Not installed"
2342
+ },
2343
+ {
2344
+ value: "vscode-workspace",
2345
+ label: "VSCode (Workspace Config)",
2346
+ hint: status.vscodeWorkspace ? "INSTALLED" : "Not installed"
2347
+ },
2348
+ {
2349
+ value: "claude",
2350
+ label: "Claude Desktop",
2351
+ hint: status.claude ? "INSTALLED" : "Not installed"
2352
+ }
2353
+ ];
2354
+ const initialSelected = [
2355
+ ...status.antigravity ? ["antigravity"] : [],
2356
+ ...status.vscodeGlobal ? ["vscode-global"] : [],
2357
+ ...status.vscodeWorkspace ? ["vscode-workspace"] : [],
2358
+ ...status.claude ? ["claude"] : []
2359
+ ];
2360
+ const handleSubmit = (selectedIds) => {
2361
+ const targets = selectedIds;
2362
+ let results = [];
2363
+ targets.forEach((target) => {
2364
+ const success = installToConfig(target, workspacePath);
2365
+ const label = getTargetLabel(target);
2366
+ results.push(`${label}: ${success ? "Success" : "Failed"}`);
2367
+ });
2368
+ setStatus(checkInstallStatus(workspacePath));
2369
+ setMessage(`Installation updated: ${results.join(", ")}`);
2370
+ setTimeout(() => {
2371
+ setMessage("");
2372
+ onComplete();
2373
+ }, 2e3);
2374
+ };
2375
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
2376
+ message && /* @__PURE__ */ jsx6(Text6, { color: "green", children: message }),
2377
+ /* @__PURE__ */ jsx6(
2378
+ SimpleSelect,
2379
+ {
2380
+ message: "Select integrations to install:",
2381
+ items: options.map((o) => ({
2382
+ value: o.value,
2383
+ label: o.label + (o.hint === "INSTALLED" ? " (Installed)" : ""),
2384
+ key: o.value
2385
+ })),
2386
+ isMulti: true,
2387
+ initialSelected,
2388
+ onSelect: () => {
2389
+ },
2390
+ onSubmit: handleSubmit,
2391
+ onCancel
2392
+ }
2393
+ )
2394
+ ] });
2395
+ };
2396
+ }
2397
+ });
2398
+
2399
+ // src/mcp/ui/InstallView.tsx
2400
+ import "react";
2401
+ import { Box as Box7, Text as Text7 } from "ink";
2402
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2403
+ var InstallView;
2404
+ var init_InstallView = __esm({
2405
+ "src/mcp/ui/InstallView.tsx"() {
2406
+ "use strict";
2407
+ init_InstallWizard();
2408
+ init_paths();
2409
+ InstallView = () => {
2410
+ const workspacePath = detectWorkspaceRoot();
2411
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "magenta", children: [
2412
+ /* @__PURE__ */ jsx7(Text7, { bold: true, color: "magenta", children: " Installation & Configuration " }),
2413
+ /* @__PURE__ */ jsx7(Text7, { color: "dim", children: " Configure IDE integrations for VSCode, Claude, and Antigravity." }),
2414
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx7(
2415
+ InstallWizard,
2416
+ {
2417
+ workspacePath,
2418
+ onComplete: () => {
2419
+ },
2420
+ onCancel: () => {
2421
+ }
2422
+ }
2423
+ ) })
2424
+ ] });
2425
+ };
2426
+ }
2427
+ });
2428
+
2429
+ // src/mcp/ui/LogViewer.tsx
2430
+ import "react";
2431
+ import { Box as Box8, Text as Text8 } from "ink";
2432
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2433
+ var LogViewer;
2434
+ var init_LogViewer = __esm({
2435
+ "src/mcp/ui/LogViewer.tsx"() {
2436
+ "use strict";
2437
+ LogViewer = ({ logs, height }) => {
2438
+ const visibleLogs = logs.slice(-height);
2439
+ const emptyLines = Math.max(0, height - visibleLogs.length);
2440
+ const padding = Array(emptyLines).fill("");
2441
+ const formatLog = (log) => {
2442
+ if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx8(Text8, { color: "red", children: log });
2443
+ if (log.includes("[WARN]")) return /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: log });
2444
+ if (log.includes("[INFO]")) return /* @__PURE__ */ jsx8(Text8, { color: "green", children: log });
2445
+ if (log.includes("Success")) return /* @__PURE__ */ jsx8(Text8, { color: "green", children: log });
2446
+ return /* @__PURE__ */ jsx8(Text8, { children: log });
2447
+ };
2448
+ return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "dim", paddingX: 1, height: height + 2, flexGrow: 1, children: [
2449
+ padding.map((_, i) => /* @__PURE__ */ jsx8(Text8, { children: " " }, `empty-${i}`)),
2450
+ visibleLogs.map((log, i) => /* @__PURE__ */ jsx8(Box8, { children: formatLog(log) }, `log-${i}`))
2451
+ ] });
2452
+ };
2453
+ }
2454
+ });
2455
+
2456
+ // src/mcp/ui/components/TabBar.tsx
2457
+ import "react";
2458
+ import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
2459
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2460
+ var TabBar;
2461
+ var init_TabBar = __esm({
2462
+ "src/mcp/ui/components/TabBar.tsx"() {
2463
+ "use strict";
2464
+ TabBar = ({ tabs, activeTab, onChange }) => {
2465
+ useInput2((input, key) => {
2466
+ if (key.leftArrow) {
2467
+ const index = tabs.findIndex((t) => t.id === activeTab);
2468
+ const nextIndex = (index - 1 + tabs.length) % tabs.length;
2469
+ onChange(tabs[nextIndex].id);
2470
+ }
2471
+ if (key.rightArrow) {
2472
+ const index = tabs.findIndex((t) => t.id === activeTab);
2473
+ const nextIndex = (index + 1) % tabs.length;
2474
+ onChange(tabs[nextIndex].id);
2475
+ }
2476
+ const num = parseInt(input);
2477
+ if (!isNaN(num) && num > 0 && num <= tabs.length) {
2478
+ onChange(tabs[num - 1].id);
2479
+ }
2480
+ });
2481
+ return /* @__PURE__ */ jsxs8(Box9, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
2482
+ tabs.map((tab, index) => {
2483
+ const isActive = tab.id === activeTab;
2484
+ return /* @__PURE__ */ jsx9(Box9, { marginRight: 2, children: /* @__PURE__ */ jsx9(
2485
+ Text9,
2486
+ {
2487
+ color: isActive ? "cyan" : "gray",
2488
+ bold: isActive,
2489
+ backgroundColor: isActive ? "black" : void 0,
2490
+ children: isActive ? `[ ${tab.label} ]` : ` ${tab.label} `
2491
+ }
2492
+ ) }, tab.id);
2493
+ }),
2494
+ /* @__PURE__ */ jsx9(Box9, { flexGrow: 1 }),
2495
+ /* @__PURE__ */ jsx9(Text9, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
2263
2496
  ] });
2264
2497
  };
2265
2498
  }
@@ -2270,28 +2503,40 @@ var App_exports = {};
2270
2503
  __export(App_exports, {
2271
2504
  App: () => App
2272
2505
  });
2273
- import { useState, useEffect as useEffect2 } from "react";
2274
- import { useInput, useApp } from "ink";
2506
+ import { useState as useState4, useEffect as useEffect4 } from "react";
2507
+ import { Box as Box10, useInput as useInput3, useApp } from "ink";
2275
2508
  import fs11 from "fs";
2276
- import { jsx as jsx7 } from "react/jsx-runtime";
2277
- var App;
2509
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2510
+ var TABS, App;
2278
2511
  var init_App = __esm({
2279
2512
  "src/mcp/ui/App.tsx"() {
2280
2513
  "use strict";
2281
- init_Dashboard();
2514
+ init_Overview();
2515
+ init_ProjectsView();
2516
+ init_InstallView();
2517
+ init_LogViewer();
2518
+ init_TabBar();
2282
2519
  init_config();
2283
2520
  init_detection();
2284
2521
  init_logger();
2285
2522
  init_server();
2286
- App = ({ onExit, onConfigure, onInstall, initialPort }) => {
2523
+ init_install();
2524
+ init_paths();
2525
+ TABS = [
2526
+ { id: "overview", label: "Overview" },
2527
+ { id: "projects", label: "Projects" },
2528
+ { id: "install", label: "Install" },
2529
+ { id: "logs", label: "Logs" }
2530
+ ];
2531
+ App = ({ onExit, initialPort }) => {
2287
2532
  const { exit } = useApp();
2288
- const [logs, setLogs] = useState([]);
2289
- const [serverInfo, setServerInfo] = useState({
2533
+ const [activeTab, setActiveTab] = useState4("overview");
2534
+ const [logs, setLogs] = useState4([]);
2535
+ const [serverInfo, setServerInfo] = useState4({
2290
2536
  port: initialPort,
2291
2537
  pid: process.pid,
2292
2538
  running: false
2293
2539
  });
2294
- const [showHelp2, setShowHelp] = useState(false);
2295
2540
  const config = loadMCPConfig();
2296
2541
  const projects = scanForProjects();
2297
2542
  const exposedProjects = projects.filter((p) => {
@@ -2300,9 +2545,15 @@ var init_App = __esm({
2300
2545
  );
2301
2546
  return cfg?.expose ?? config.defaults.includeNew;
2302
2547
  });
2303
- const exposedNames = exposedProjects.map((p) => p.name).slice(0, 5);
2304
- const exposedLabel = exposedNames.length > 0 ? exposedNames.join(", ") + (exposedProjects.length > 5 ? ` (+${exposedProjects.length - 5} more)` : "") : "(none)";
2305
- useEffect2(() => {
2548
+ const workspacePath = detectWorkspaceRoot();
2549
+ const installStatus = checkInstallStatus(workspacePath);
2550
+ const installedCount = [
2551
+ installStatus.antigravity,
2552
+ installStatus.claude,
2553
+ installStatus.vscodeGlobal,
2554
+ installStatus.vscodeWorkspace
2555
+ ].filter(Boolean).length;
2556
+ useEffect4(() => {
2306
2557
  const start = async () => {
2307
2558
  const status = getMCPServerStatus();
2308
2559
  if (!status.running) {
@@ -2310,17 +2561,15 @@ var init_App = __esm({
2310
2561
  const res = await startMCPServer({ interactive: true });
2311
2562
  setServerInfo((prev) => ({ ...prev, running: true, port: res.port, pid: res.pid }));
2312
2563
  } catch (e) {
2313
- setLogs((prev) => [...prev, `Error starting server: ${e}`]);
2564
+ setLogs((prev) => [...prev, `[ERROR] Error starting server: ${e}`]);
2314
2565
  }
2315
2566
  } else {
2316
2567
  setServerInfo((prev) => ({ ...prev, running: true, port: status.port || initialPort, pid: status.pid || process.pid }));
2317
2568
  }
2318
2569
  };
2319
2570
  start();
2320
- return () => {
2321
- };
2322
2571
  }, []);
2323
- useEffect2(() => {
2572
+ useEffect4(() => {
2324
2573
  const logPath = getLogFilePath();
2325
2574
  let lastSize = 0;
2326
2575
  if (fs11.existsSync(logPath)) {
@@ -2339,7 +2588,7 @@ var init_App = __esm({
2339
2588
  const newLines = newContent.split("\n").filter((l) => l.trim());
2340
2589
  setLogs((prev) => {
2341
2590
  const next = [...prev, ...newLines];
2342
- return next.slice(-50);
2591
+ return next.slice(-100);
2343
2592
  });
2344
2593
  lastSize = stats.size;
2345
2594
  }
@@ -2347,46 +2596,34 @@ var init_App = __esm({
2347
2596
  }, 500);
2348
2597
  return () => clearInterval(interval);
2349
2598
  }, []);
2350
- useInput((input, key) => {
2599
+ useInput3((input, key) => {
2351
2600
  if (input === "q" || key.ctrl && input === "c") {
2352
2601
  stopMCPServer();
2353
2602
  onExit();
2354
2603
  exit();
2355
2604
  }
2356
- if (input === "p") {
2357
- setLogs((prev) => [...prev, "Switching to configuration wizard..."]);
2358
- onConfigure();
2359
- exit();
2360
- }
2361
- if (input === "i") {
2362
- setLogs((prev) => [...prev, "Switching to install wizard..."]);
2363
- onInstall();
2364
- exit();
2365
- }
2366
- if (input === "c") {
2367
- setLogs([]);
2368
- }
2369
- if (input === "r") {
2370
- setLogs((prev) => [...prev, "[INFO] Config reload requested..."]);
2371
- }
2372
- if (input === "?") {
2373
- setShowHelp((prev) => !prev);
2374
- }
2375
- }, { isActive: true });
2605
+ });
2376
2606
  const termHeight = process.stdout.rows || 24;
2377
- const logHeight = Math.max(5, termHeight - 12);
2378
- return /* @__PURE__ */ jsx7(
2379
- Dashboard,
2380
- {
2381
- logs,
2382
- exposedLabel,
2383
- port: serverInfo.port,
2384
- pid: serverInfo.pid,
2385
- running: serverInfo.running,
2386
- logHeight,
2387
- showHelp: showHelp2
2388
- }
2389
- );
2607
+ const contentHeight = termHeight - 8;
2608
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", padding: 0, height: termHeight, children: [
2609
+ /* @__PURE__ */ jsx10(TabBar, { tabs: TABS, activeTab, onChange: setActiveTab }),
2610
+ /* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexGrow: 1, children: [
2611
+ activeTab === "overview" && /* @__PURE__ */ jsx10(
2612
+ Overview,
2613
+ {
2614
+ serverStatus: serverInfo,
2615
+ stats: {
2616
+ exposedProjects: exposedProjects.length,
2617
+ totalProjects: projects.length,
2618
+ installedIntegrations: installedCount
2619
+ }
2620
+ }
2621
+ ),
2622
+ activeTab === "projects" && /* @__PURE__ */ jsx10(ProjectsView, {}),
2623
+ activeTab === "install" && /* @__PURE__ */ jsx10(InstallView, {}),
2624
+ activeTab === "logs" && /* @__PURE__ */ jsx10(LogViewer, { logs, height: contentHeight })
2625
+ ] })
2626
+ ] });
2390
2627
  };
2391
2628
  }
2392
2629
  });
@@ -2394,7 +2631,7 @@ var init_App = __esm({
2394
2631
  // src/mcp/commands/start.ts
2395
2632
  import { confirm, isCancel as isCancel4, text } from "@clack/prompts";
2396
2633
  async function handleStartServer() {
2397
- const React8 = await import("react");
2634
+ const React11 = await import("react");
2398
2635
  const { render } = await import("ink");
2399
2636
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
2400
2637
  const config = loadMCPConfig();
@@ -2434,38 +2671,16 @@ async function handleStartServer() {
2434
2671
  initialPort = newPort;
2435
2672
  }
2436
2673
  }
2437
- console.clear();
2438
- let keepRunning = true;
2439
- while (keepRunning) {
2440
- let nextAction = "exit";
2441
- process.stdin.resume();
2442
- const app = render(React8.createElement(App2, {
2443
- initialPort,
2444
- onExit: () => {
2445
- nextAction = "exit";
2446
- },
2447
- onConfigure: () => {
2448
- nextAction = "configure";
2449
- },
2450
- onInstall: () => {
2451
- nextAction = "install";
2452
- }
2453
- }), {
2454
- exitOnCtrlC: false
2455
- // We handle this in App
2456
- });
2457
- await app.waitUntilExit();
2458
- if (nextAction === "exit") {
2459
- keepRunning = false;
2460
- } else if (nextAction === "configure") {
2461
- console.clear();
2462
- await handleConfigure();
2463
- } else if (nextAction === "install") {
2464
- console.clear();
2465
- const workspacePath = detectWorkspaceRoot();
2466
- await runInstallWizard(workspacePath);
2674
+ process.stdin.resume();
2675
+ const app = render(React11.createElement(App2, {
2676
+ initialPort,
2677
+ onExit: () => {
2467
2678
  }
2468
- }
2679
+ }), {
2680
+ exitOnCtrlC: false
2681
+ });
2682
+ await app.waitUntilExit();
2683
+ console.clear();
2469
2684
  }
2470
2685
  var init_start = __esm({
2471
2686
  "src/mcp/commands/start.ts"() {
@@ -2589,7 +2804,7 @@ __export(mcp_exports, {
2589
2804
  handleStartServer: () => handleStartServer,
2590
2805
  runMCP: () => runMCP
2591
2806
  });
2592
- import { intro, outro, select as select2, confirm as confirm2, note as note6, cancel, isCancel as isCancel5 } from "@clack/prompts";
2807
+ import { intro, outro, confirm as confirm2, note as note6, isCancel as isCancel5 } from "@clack/prompts";
2593
2808
  import pc7 from "picocolors";
2594
2809
  async function runMCP(subcommand2) {
2595
2810
  if (subcommand2) {
@@ -2615,12 +2830,14 @@ async function runMCP(subcommand2) {
2615
2830
  case "configure":
2616
2831
  await handleConfigure();
2617
2832
  return;
2833
+ case "menu":
2834
+ break;
2618
2835
  }
2619
2836
  }
2620
- intro(pc7.bgCyan(pc7.black(" RRCE MCP Hub ")));
2621
2837
  const workspacePath = detectWorkspaceRoot();
2622
2838
  const globalPathCheck = await ensureMCPGlobalPath();
2623
2839
  if (!globalPathCheck.configured) {
2840
+ intro(pc7.bgCyan(pc7.black(" MCP Setup ")));
2624
2841
  const configured = await handleConfigureGlobalPath();
2625
2842
  if (!configured) {
2626
2843
  outro(pc7.yellow("MCP requires a global storage path. Setup cancelled."));
@@ -2629,101 +2846,46 @@ async function runMCP(subcommand2) {
2629
2846
  }
2630
2847
  const installed = isInstalledAnywhere(workspacePath);
2631
2848
  if (!installed) {
2849
+ intro(pc7.bgCyan(pc7.black(" Welcome to MCP Hub ")));
2632
2850
  note6(
2633
- `${pc7.bold("Welcome to RRCE MCP Hub!")}
2634
-
2635
- MCP (Model Context Protocol) allows AI assistants to access your
2636
- project knowledge in real-time. Let's get you set up.`,
2851
+ `${pc7.bold("Set up Model Context Protocol")}
2852
+ Allow AI assistants to access your project context.`,
2637
2853
  "Getting Started"
2638
2854
  );
2639
2855
  const shouldInstall = await confirm2({
2640
- message: "Install MCP server to your IDE(s)?",
2856
+ message: "Install MCP server integrations now?",
2641
2857
  initialValue: true
2642
2858
  });
2643
2859
  if (shouldInstall && !isCancel5(shouldInstall)) {
2644
2860
  await runInstallWizard(workspacePath);
2645
- const config2 = loadMCPConfig();
2646
- const exposedCount2 = config2.projects.filter((p) => p.expose).length;
2647
- if (exposedCount2 === 0) {
2648
- await handleConfigure();
2649
- }
2650
2861
  const shouldStart = await confirm2({
2651
- message: "Start the MCP server now?",
2862
+ message: "Start the MCP Dashboard?",
2652
2863
  initialValue: true
2653
2864
  });
2654
2865
  if (shouldStart && !isCancel5(shouldStart)) {
2655
2866
  await handleStartServer();
2656
2867
  }
2868
+ } else {
2869
+ outro(pc7.dim('Setup skipped. Run "npx rrce-workflow mcp" later to restart.'));
2657
2870
  }
2658
- outro(pc7.green("MCP Hub setup complete!"));
2659
2871
  return;
2660
2872
  }
2661
- const config = loadMCPConfig();
2662
- const exposedCount = config.projects.filter((p) => p.expose).length;
2663
- if (exposedCount === 0 && !config.defaults.includeNew) {
2664
- note6("MCP is installed but no projects are exposed. Let's configure that.", "Configuration Needed");
2665
- await handleConfigure();
2666
- }
2667
- let running = true;
2668
- while (running) {
2669
- const serverStatus = getMCPServerStatus();
2670
- const serverLabel = serverStatus.running ? pc7.green("\u25CF Running") : pc7.dim("\u25CB Stopped");
2671
- const currentStatus = checkInstallStatus(workspacePath);
2672
- const installedCount = [currentStatus.antigravity, currentStatus.claude, currentStatus.vscodeGlobal, currentStatus.vscodeWorkspace].filter(Boolean).length;
2673
- const action = await select2({
2674
- message: "What would you like to do?",
2675
- options: [
2676
- { value: "start", label: `\u25B6\uFE0F Start MCP server`, hint: serverLabel },
2677
- { value: "configure", label: "\u2699\uFE0F Configure projects", hint: "Choose which projects to expose" },
2678
- { value: "install", label: "\u{1F4E5} Install to IDE", hint: `${installedCount} IDE(s) configured` },
2679
- { value: "status", label: "\u{1F4CB} View status", hint: "See details" },
2680
- { value: "help", label: "\u2753 Help", hint: "Learn about MCP Hub" },
2681
- { value: "exit", label: "\u21A9 Exit", hint: "Return to shell" }
2682
- ]
2683
- });
2684
- if (isCancel5(action)) {
2685
- cancel("MCP Hub closed.");
2686
- return;
2687
- }
2688
- switch (action) {
2689
- case "start":
2690
- await handleStartServer();
2691
- break;
2692
- case "configure":
2693
- await handleConfigure();
2694
- break;
2695
- case "install":
2696
- await runInstallWizard(workspacePath);
2697
- break;
2698
- case "status":
2699
- await handleShowStatus();
2700
- break;
2701
- case "help":
2702
- showHelp();
2703
- break;
2704
- case "exit":
2705
- running = false;
2706
- break;
2707
- }
2873
+ try {
2874
+ await handleStartServer();
2875
+ } catch (err) {
2876
+ console.error(err);
2877
+ outro(pc7.red("Failed to launch MCP Dashboard"));
2708
2878
  }
2709
- outro(pc7.green("MCP Hub closed."));
2710
2879
  }
2711
2880
  async function handleStopServer() {
2712
2881
  const { stopMCPServer: stopMCPServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2713
2882
  const status = getMCPServerStatus();
2714
2883
  if (!status.running) {
2715
- note6("MCP server is not running.", "Status");
2716
- return;
2717
- }
2718
- const confirmed = await confirm2({
2719
- message: "Stop the MCP server?",
2720
- initialValue: true
2721
- });
2722
- if (isCancel5(confirmed) || !confirmed) {
2884
+ console.log(pc7.dim("MCP server is already stopped."));
2723
2885
  return;
2724
2886
  }
2725
2887
  stopMCPServer2();
2726
- note6(pc7.green("MCP server stopped."), "Server Stopped");
2888
+ console.log(pc7.green("MCP server stopped."));
2727
2889
  }
2728
2890
  var init_mcp = __esm({
2729
2891
  "src/mcp/index.ts"() {
@@ -2741,7 +2903,7 @@ var init_mcp = __esm({
2741
2903
  });
2742
2904
 
2743
2905
  // src/commands/wizard/setup-flow.ts
2744
- import { group, select as select3, multiselect as multiselect3, confirm as confirm3, spinner as spinner3, note as note7, outro as outro2, cancel as cancel2, isCancel as isCancel6 } from "@clack/prompts";
2906
+ import { group, select as select2, multiselect as multiselect3, confirm as confirm3, spinner as spinner3, note as note7, outro as outro2, cancel as cancel2, isCancel as isCancel6 } from "@clack/prompts";
2745
2907
  import pc8 from "picocolors";
2746
2908
  import * as fs12 from "fs";
2747
2909
  import * as path12 from "path";
@@ -2749,7 +2911,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
2749
2911
  const s = spinner3();
2750
2912
  const config = await group(
2751
2913
  {
2752
- storageMode: () => select3({
2914
+ storageMode: () => select2({
2753
2915
  message: "Where should workflow data be stored?",
2754
2916
  options: [
2755
2917
  { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
@@ -2944,9 +3106,9 @@ linked_projects:
2944
3106
  }
2945
3107
  if (config.exposeToMCP) {
2946
3108
  try {
2947
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3109
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2948
3110
  const { getWorkspaceName: getWorkspaceName2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
2949
- const mcpConfig = loadMCPConfig2();
3111
+ const mcpConfig = loadMCPConfig3();
2950
3112
  const currentProjectName = workspaceName;
2951
3113
  if (config.storageMode === "workspace") {
2952
3114
  setProjectConfig2(mcpConfig, currentProjectName, true);
@@ -3119,8 +3281,8 @@ linked_projects:
3119
3281
  });
3120
3282
  if (shouldExpose && !isCancel7(shouldExpose)) {
3121
3283
  try {
3122
- const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3123
- const mcpConfig = loadMCPConfig2();
3284
+ const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3285
+ const mcpConfig = loadMCPConfig3();
3124
3286
  for (const project of selectedProjects) {
3125
3287
  setProjectConfig2(mcpConfig, project.name, true, void 0, project.dataPath);
3126
3288
  }
@@ -3297,7 +3459,7 @@ var wizard_exports = {};
3297
3459
  __export(wizard_exports, {
3298
3460
  runWizard: () => runWizard
3299
3461
  });
3300
- import { intro as intro2, select as select4, spinner as spinner7, note as note11, outro as outro6, isCancel as isCancel10 } from "@clack/prompts";
3462
+ import { intro as intro2, select as select3, spinner as spinner7, note as note11, outro as outro6, isCancel as isCancel10 } from "@clack/prompts";
3301
3463
  import pc12 from "picocolors";
3302
3464
  import * as fs16 from "fs";
3303
3465
  async function runWizard() {
@@ -3355,7 +3517,7 @@ Workspace: ${pc12.bold(workspaceName)}`,
3355
3517
  menuOptions.push({ value: "update", label: "\u{1F4E6} Update from package", hint: "Get latest prompts & templates" });
3356
3518
  menuOptions.push({ value: "reconfigure", label: "\u{1F527} Reconfigure project", hint: "Change storage mode, tools, etc." });
3357
3519
  menuOptions.push({ value: "exit", label: "\u21A9 Exit" });
3358
- const action = await select4({
3520
+ const action = await select3({
3359
3521
  message: "This workspace is already configured. What would you like to do?",
3360
3522
  options: menuOptions
3361
3523
  });
@@ -3403,7 +3565,7 @@ init_wizard();
3403
3565
 
3404
3566
  // src/commands/selector.ts
3405
3567
  init_prompts();
3406
- import { intro as intro3, select as select5, note as note12, cancel as cancel7, isCancel as isCancel11, outro as outro7 } from "@clack/prompts";
3568
+ import { intro as intro3, select as select4, note as note12, cancel as cancel7, isCancel as isCancel11, outro as outro7 } from "@clack/prompts";
3407
3569
  import pc13 from "picocolors";
3408
3570
  import * as path15 from "path";
3409
3571
  async function runSelector() {
@@ -3414,7 +3576,7 @@ async function runSelector() {
3414
3576
  cancel7("No agents found. Run `rrce-workflow` to set up.");
3415
3577
  process.exit(0);
3416
3578
  }
3417
- const selection = await select5({
3579
+ const selection = await select4({
3418
3580
  message: "Select an agent:",
3419
3581
  options: [
3420
3582
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",