rrce-workflow 0.2.29 → 0.2.30

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 (2) hide show
  1. package/dist/index.js +295 -162
  2. package/package.json +7 -1
package/dist/index.js CHANGED
@@ -721,6 +721,17 @@ var init_types = __esm({
721
721
  });
722
722
 
723
723
  // src/mcp/config.ts
724
+ var config_exports = {};
725
+ __export(config_exports, {
726
+ ensureMCPGlobalPath: () => ensureMCPGlobalPath,
727
+ getMCPConfigPath: () => getMCPConfigPath,
728
+ getProjectPermissions: () => getProjectPermissions,
729
+ isProjectExposed: () => isProjectExposed,
730
+ loadMCPConfig: () => loadMCPConfig,
731
+ removeProjectConfig: () => removeProjectConfig,
732
+ saveMCPConfig: () => saveMCPConfig,
733
+ setProjectConfig: () => setProjectConfig
734
+ });
724
735
  import * as fs7 from "fs";
725
736
  import * as path8 from "path";
726
737
  function getMCPConfigPath() {
@@ -907,6 +918,10 @@ function setProjectConfig(config, name, expose, permissions) {
907
918
  }
908
919
  return config;
909
920
  }
921
+ function removeProjectConfig(config, name) {
922
+ config.projects = config.projects.filter((p) => p.name !== name);
923
+ return config;
924
+ }
910
925
  function isProjectExposed(config, name) {
911
926
  const project = config.projects.find((p) => p.name === name);
912
927
  if (project) {
@@ -1683,6 +1698,226 @@ var init_install = __esm({
1683
1698
  }
1684
1699
  });
1685
1700
 
1701
+ // src/mcp/ui/Header.tsx
1702
+ import "react";
1703
+ import { Box, Text } from "ink";
1704
+ import { jsx } from "react/jsx-runtime";
1705
+ var Header;
1706
+ var init_Header = __esm({
1707
+ "src/mcp/ui/Header.tsx"() {
1708
+ "use strict";
1709
+ Header = () => /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingBottom: 1, children: /* @__PURE__ */ jsx(Box, { borderStyle: "double", borderColor: "cyan", paddingX: 2, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: " RRCE MCP Hub " }) }) });
1710
+ }
1711
+ });
1712
+
1713
+ // src/mcp/ui/StatusBoard.tsx
1714
+ import "react";
1715
+ import { Box as Box2, Text as Text2 } from "ink";
1716
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1717
+ var StatusBoard;
1718
+ var init_StatusBoard = __esm({
1719
+ "src/mcp/ui/StatusBoard.tsx"() {
1720
+ "use strict";
1721
+ StatusBoard = ({ exposedLabel, port, pid }) => {
1722
+ return /* @__PURE__ */ jsx2(Box2, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs(Text2, { children: [
1723
+ "\u{1F4CB} ",
1724
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: exposedLabel }),
1725
+ " ",
1726
+ "\u2502",
1727
+ " Port: ",
1728
+ /* @__PURE__ */ jsx2(Text2, { color: "green", children: port }),
1729
+ " ",
1730
+ "\u2502",
1731
+ " PID: ",
1732
+ /* @__PURE__ */ jsx2(Text2, { color: "green", children: pid })
1733
+ ] }) });
1734
+ };
1735
+ }
1736
+ });
1737
+
1738
+ // src/mcp/ui/LogViewer.tsx
1739
+ import "react";
1740
+ import { Box as Box3, Text as Text3 } from "ink";
1741
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1742
+ var LogViewer;
1743
+ var init_LogViewer = __esm({
1744
+ "src/mcp/ui/LogViewer.tsx"() {
1745
+ "use strict";
1746
+ LogViewer = ({ logs, height }) => {
1747
+ const visibleLogs = logs.slice(-height);
1748
+ const emptyLines = Math.max(0, height - visibleLogs.length);
1749
+ const padding = Array(emptyLines).fill("");
1750
+ return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "dim", paddingX: 1, height: height + 2, flexGrow: 1, children: [
1751
+ padding.map((_, i) => /* @__PURE__ */ jsx3(Text3, { children: " " }, `empty-${i}`)),
1752
+ visibleLogs.map((log, i) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate-end", children: log }, `log-${i}`))
1753
+ ] });
1754
+ };
1755
+ }
1756
+ });
1757
+
1758
+ // src/mcp/ui/CommandBar.tsx
1759
+ import "react";
1760
+ import { Box as Box4, Text as Text4 } from "ink";
1761
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1762
+ var CommandBar;
1763
+ var init_CommandBar = __esm({
1764
+ "src/mcp/ui/CommandBar.tsx"() {
1765
+ "use strict";
1766
+ CommandBar = () => {
1767
+ return /* @__PURE__ */ jsx4(Box4, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: /* @__PURE__ */ jsxs3(Text4, { children: [
1768
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "q" }),
1769
+ ":Quit ",
1770
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "p" }),
1771
+ ":Projects ",
1772
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "i" }),
1773
+ ":Install ",
1774
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "r" }),
1775
+ ":Reload ",
1776
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "c" }),
1777
+ ":Clear ",
1778
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "?" }),
1779
+ ":Help"
1780
+ ] }) });
1781
+ };
1782
+ }
1783
+ });
1784
+
1785
+ // src/mcp/ui/Dashboard.tsx
1786
+ import "react";
1787
+ import { Box as Box5 } from "ink";
1788
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1789
+ var Dashboard;
1790
+ var init_Dashboard = __esm({
1791
+ "src/mcp/ui/Dashboard.tsx"() {
1792
+ "use strict";
1793
+ init_Header();
1794
+ init_StatusBoard();
1795
+ init_LogViewer();
1796
+ init_CommandBar();
1797
+ Dashboard = ({ logs, exposedLabel, port, pid, logHeight }) => {
1798
+ return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", padding: 0, children: [
1799
+ /* @__PURE__ */ jsx5(Header, {}),
1800
+ /* @__PURE__ */ jsx5(LogViewer, { logs, height: logHeight }),
1801
+ /* @__PURE__ */ jsx5(StatusBoard, { exposedLabel, port, pid }),
1802
+ /* @__PURE__ */ jsx5(CommandBar, {})
1803
+ ] });
1804
+ };
1805
+ }
1806
+ });
1807
+
1808
+ // src/mcp/ui/App.tsx
1809
+ var App_exports = {};
1810
+ __export(App_exports, {
1811
+ App: () => App
1812
+ });
1813
+ import { useState, useEffect as useEffect2 } from "react";
1814
+ import { useInput, useApp } from "ink";
1815
+ import fs11 from "fs";
1816
+ import { jsx as jsx6 } from "react/jsx-runtime";
1817
+ var App;
1818
+ var init_App = __esm({
1819
+ "src/mcp/ui/App.tsx"() {
1820
+ "use strict";
1821
+ init_Dashboard();
1822
+ init_config();
1823
+ init_detection();
1824
+ init_logger();
1825
+ init_server();
1826
+ App = ({ onExit, onConfigure, onInstall, initialPort }) => {
1827
+ const { exit } = useApp();
1828
+ const [logs, setLogs] = useState([]);
1829
+ const [serverInfo, setServerInfo] = useState({
1830
+ port: initialPort,
1831
+ pid: process.pid,
1832
+ running: false
1833
+ });
1834
+ const config = loadMCPConfig();
1835
+ const projects = scanForProjects();
1836
+ const exposedProjects = projects.filter((p) => {
1837
+ const cfg = config.projects.find((c) => c.name === p.name);
1838
+ return cfg?.expose ?? config.defaults.includeNew;
1839
+ });
1840
+ const exposedNames = exposedProjects.map((p) => p.name).slice(0, 5);
1841
+ const exposedLabel = exposedNames.length > 0 ? exposedNames.join(", ") + (exposedProjects.length > 5 ? ` (+${exposedProjects.length - 5} more)` : "") : "(none)";
1842
+ useEffect2(() => {
1843
+ const start = async () => {
1844
+ const status = getMCPServerStatus();
1845
+ if (!status.running) {
1846
+ try {
1847
+ const res = await startMCPServer();
1848
+ setServerInfo((prev) => ({ ...prev, running: true, port: res.port, pid: res.pid }));
1849
+ } catch (e) {
1850
+ setLogs((prev) => [...prev, `Error starting server: ${e}`]);
1851
+ }
1852
+ } else {
1853
+ setServerInfo((prev) => ({ ...prev, running: true, port: status.port || initialPort, pid: status.pid || process.pid }));
1854
+ }
1855
+ };
1856
+ start();
1857
+ return () => {
1858
+ };
1859
+ }, []);
1860
+ useEffect2(() => {
1861
+ const logPath = getLogFilePath();
1862
+ let lastSize = 0;
1863
+ if (fs11.existsSync(logPath)) {
1864
+ const stats = fs11.statSync(logPath);
1865
+ lastSize = stats.size;
1866
+ }
1867
+ const interval = setInterval(() => {
1868
+ if (fs11.existsSync(logPath)) {
1869
+ const stats = fs11.statSync(logPath);
1870
+ if (stats.size > lastSize) {
1871
+ const buffer = Buffer.alloc(stats.size - lastSize);
1872
+ const fd = fs11.openSync(logPath, "r");
1873
+ fs11.readSync(fd, buffer, 0, buffer.length, lastSize);
1874
+ fs11.closeSync(fd);
1875
+ const newContent = buffer.toString("utf-8");
1876
+ const newLines = newContent.split("\n").filter((l) => l.trim());
1877
+ setLogs((prev) => {
1878
+ const next = [...prev, ...newLines];
1879
+ return next.slice(-50);
1880
+ });
1881
+ lastSize = stats.size;
1882
+ }
1883
+ }
1884
+ }, 500);
1885
+ return () => clearInterval(interval);
1886
+ }, []);
1887
+ useInput((input, key) => {
1888
+ if (input === "q" || key.ctrl && input === "c") {
1889
+ stopMCPServer();
1890
+ onExit();
1891
+ }
1892
+ if (input === "p") {
1893
+ onConfigure();
1894
+ }
1895
+ if (input === "i") {
1896
+ onInstall();
1897
+ }
1898
+ if (input === "c") {
1899
+ setLogs([]);
1900
+ }
1901
+ if (input === "r") {
1902
+ setLogs((prev) => [...prev, "[INFO] Config reload requested..."]);
1903
+ }
1904
+ });
1905
+ const termHeight = process.stdout.rows || 24;
1906
+ const logHeight = Math.max(5, termHeight - 12);
1907
+ return /* @__PURE__ */ jsx6(
1908
+ Dashboard,
1909
+ {
1910
+ logs,
1911
+ exposedLabel,
1912
+ port: serverInfo.port,
1913
+ pid: serverInfo.pid,
1914
+ logHeight
1915
+ }
1916
+ );
1917
+ };
1918
+ }
1919
+ });
1920
+
1686
1921
  // src/mcp/index.ts
1687
1922
  var mcp_exports = {};
1688
1923
  __export(mcp_exports, {
@@ -1694,9 +1929,13 @@ async function runMCP(subcommand2) {
1694
1929
  if (subcommand2) {
1695
1930
  switch (subcommand2) {
1696
1931
  case "start":
1697
- await startMCPServer();
1698
- await new Promise(() => {
1699
- });
1932
+ if (process.stdout.isTTY) {
1933
+ await handleStartServer();
1934
+ } else {
1935
+ await startMCPServer();
1936
+ await new Promise(() => {
1937
+ });
1938
+ }
1700
1939
  return;
1701
1940
  case "stop":
1702
1941
  await handleStopServer();
@@ -1848,9 +2087,11 @@ async function handleInstallWizard(workspacePath) {
1848
2087
  }
1849
2088
  }
1850
2089
  async function handleStartServer() {
1851
- const fs16 = await import("fs");
1852
- const { getLogFilePath: getLogFilePath2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1853
- const config = loadMCPConfig();
2090
+ const React7 = await import("react");
2091
+ const { render } = await import("ink");
2092
+ const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
2093
+ const { loadMCPConfig: loadMCPConfig2, saveMCPConfig: saveMCPConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2094
+ const config = loadMCPConfig2();
1854
2095
  const projects = scanForProjects();
1855
2096
  const exposedProjects = projects.filter((p) => {
1856
2097
  const cfg = config.projects.find((c) => c.name === p.name);
@@ -1867,7 +2108,7 @@ async function handleStartServer() {
1867
2108
  }
1868
2109
  }
1869
2110
  const status = getMCPServerStatus();
1870
- let newPort = config.server.port;
2111
+ let initialPort = config.server.port;
1871
2112
  if (!status.running) {
1872
2113
  const portInput = await text({
1873
2114
  message: "Select port for MCP Server",
@@ -1878,153 +2119,45 @@ async function handleStartServer() {
1878
2119
  }
1879
2120
  });
1880
2121
  if (isCancel2(portInput)) return;
1881
- newPort = parseInt(portInput, 10);
2122
+ const newPort = parseInt(portInput, 10);
1882
2123
  if (newPort !== config.server.port) {
1883
2124
  config.server.port = newPort;
1884
- saveMCPConfig(config);
2125
+ saveMCPConfig2(config);
2126
+ initialPort = newPort;
1885
2127
  }
1886
- } else {
1887
- newPort = status.port || newPort;
1888
- note2(`Server is already running on port ${newPort}`, "Info");
1889
2128
  }
1890
2129
  console.clear();
1891
- const logPath = getLogFilePath2();
1892
- const exposedNames = exposedProjects.map((p) => p.name).slice(0, 5);
1893
- const exposedLabel = exposedNames.length > 0 ? exposedNames.join(", ") + (exposedProjects.length > 5 ? ` (+${exposedProjects.length - 5} more)` : "") : pc3.dim("(none)");
1894
- const render = (logs = []) => {
1895
- console.clear();
1896
- console.log(pc3.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1897
- console.log(pc3.cyan("\u2551") + pc3.bold(pc3.white(" RRCE MCP Hub Running ")) + pc3.cyan("\u2551"));
1898
- console.log(pc3.cyan("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1899
- const logLines = logs.slice(-10);
1900
- const emptyLines = 10 - logLines.length;
1901
- for (let i = 0; i < emptyLines; i++) {
1902
- console.log(pc3.cyan("\u2551") + " ".repeat(63) + pc3.cyan("\u2551"));
1903
- }
1904
- for (const line of logLines) {
1905
- const cleanLine = line.replace(/\u001b\[\d+m/g, "");
1906
- const truncated = cleanLine.substring(0, 61).padEnd(61);
1907
- console.log(pc3.cyan("\u2551") + " " + pc3.dim(truncated) + " " + pc3.cyan("\u2551"));
1908
- }
1909
- console.log(pc3.cyan("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1910
- const infoLine = ` \u{1F4CB} ${exposedLabel} \u2502 Port: ${newPort} \u2502 PID: ${process.pid || "?"}`.substring(0, 61).padEnd(61);
1911
- console.log(pc3.cyan("\u2551") + pc3.yellow(infoLine) + " " + pc3.cyan("\u2551"));
1912
- console.log(pc3.cyan("\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1913
- const cmdLine = ` q:Quit p:Projects i:Install r:Reload c:Clear ?:Help`;
1914
- console.log(pc3.cyan("\u2551") + pc3.dim(cmdLine.padEnd(63)) + pc3.cyan("\u2551"));
1915
- console.log(pc3.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1916
- };
1917
- let logBuffer = [];
1918
- render(logBuffer);
1919
- try {
1920
- if (!status.running) {
1921
- await startMCPServer();
1922
- }
1923
- let lastSize = 0;
1924
- if (fs16.existsSync(logPath)) {
1925
- const stats = fs16.statSync(logPath);
1926
- lastSize = stats.size;
1927
- }
1928
- let isRunning = true;
1929
- let interval;
1930
- if (process.stdin.setRawMode) {
1931
- process.stdin.setRawMode(true);
1932
- process.stdin.resume();
1933
- process.stdin.setEncoding("utf8");
1934
- }
1935
- return new Promise((resolve) => {
1936
- const cleanup = (shouldStopServer) => {
1937
- isRunning = false;
1938
- clearInterval(interval);
1939
- if (process.stdin.setRawMode) {
1940
- process.stdin.setRawMode(false);
1941
- }
1942
- process.stdin.removeListener("data", onKey);
1943
- process.stdin.pause();
1944
- if (shouldStopServer) {
1945
- stopMCPServer();
1946
- }
1947
- console.log("");
1948
- };
1949
- const onKey = async (key) => {
1950
- if (key === "" || key.toLowerCase() === "q") {
1951
- cleanup(true);
1952
- resolve();
1953
- return;
1954
- }
1955
- if (key.toLowerCase() === "p") {
1956
- cleanup(false);
1957
- console.clear();
1958
- await handleConfigure();
1959
- await handleStartServer();
1960
- resolve();
1961
- return;
1962
- }
1963
- if (key.toLowerCase() === "i") {
1964
- cleanup(false);
1965
- console.clear();
1966
- await handleInstallWizard(detectWorkspaceRoot());
1967
- await handleStartServer();
1968
- resolve();
1969
- return;
1970
- }
1971
- if (key.toLowerCase() === "r") {
1972
- logBuffer.push("[INFO] Reloading configuration...");
1973
- render(logBuffer);
1974
- return;
1975
- }
1976
- if (key.toLowerCase() === "c") {
1977
- logBuffer = [];
1978
- render(logBuffer);
1979
- return;
1980
- }
1981
- if (key === "?") {
1982
- logBuffer.push("\u2500".repeat(40));
1983
- logBuffer.push("Commands:");
1984
- logBuffer.push(" q - Stop server and return to menu");
1985
- logBuffer.push(" p - Reconfigure exposed projects");
1986
- logBuffer.push(" i - Install to additional IDEs");
1987
- logBuffer.push(" r - Reload configuration");
1988
- logBuffer.push(" c - Clear log display");
1989
- logBuffer.push(" ? - Show this help");
1990
- logBuffer.push("\u2500".repeat(40));
1991
- render(logBuffer);
1992
- return;
1993
- }
1994
- };
1995
- process.stdin.on("data", onKey);
1996
- interval = setInterval(() => {
1997
- if (!isRunning) return;
1998
- if (fs16.existsSync(logPath)) {
1999
- const stats = fs16.statSync(logPath);
2000
- if (stats.size > lastSize) {
2001
- const buffer = Buffer.alloc(stats.size - lastSize);
2002
- const fd = fs16.openSync(logPath, "r");
2003
- fs16.readSync(fd, buffer, 0, buffer.length, lastSize);
2004
- fs16.closeSync(fd);
2005
- const newContent = buffer.toString("utf-8");
2006
- const newLines = newContent.split("\n").filter((l) => l.trim());
2007
- logBuffer.push(...newLines);
2008
- if (logBuffer.length > 100) {
2009
- logBuffer = logBuffer.slice(-100);
2010
- }
2011
- lastSize = stats.size;
2012
- render(logBuffer);
2013
- }
2014
- }
2015
- }, 500);
2016
- });
2017
- } catch (error) {
2018
- if (process.stdin.setRawMode) {
2019
- process.stdin.setRawMode(false);
2130
+ let keepRunning = true;
2131
+ while (keepRunning) {
2132
+ let nextAction = "exit";
2133
+ const app = render(React7.createElement(App2, {
2134
+ initialPort,
2135
+ onExit: () => {
2136
+ nextAction = "exit";
2137
+ },
2138
+ onConfigure: () => {
2139
+ nextAction = "configure";
2140
+ },
2141
+ onInstall: () => {
2142
+ nextAction = "install";
2143
+ }
2144
+ }));
2145
+ await app.waitUntilExit();
2146
+ if (nextAction === "exit") {
2147
+ keepRunning = false;
2148
+ } else if (nextAction === "configure") {
2149
+ console.clear();
2150
+ await handleConfigure();
2151
+ } else if (nextAction === "install") {
2152
+ console.clear();
2153
+ const workspacePath = detectWorkspaceRoot();
2154
+ await handleInstallWizard(workspacePath);
2020
2155
  }
2021
- console.error(pc3.red("\nFailed to start/monitor server:"));
2022
- console.error(error);
2023
2156
  }
2024
2157
  }
2025
2158
  async function handleConfigureGlobalPath() {
2026
2159
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
2027
- const fs16 = await import("fs");
2160
+ const fs17 = await import("fs");
2028
2161
  const path16 = await import("path");
2029
2162
  note2(
2030
2163
  `MCP Hub requires a ${pc3.bold("global storage path")} to store its configuration
@@ -2039,8 +2172,8 @@ locally in each project. MCP needs a central location.`,
2039
2172
  return false;
2040
2173
  }
2041
2174
  try {
2042
- if (!fs16.existsSync(resolvedPath)) {
2043
- fs16.mkdirSync(resolvedPath, { recursive: true });
2175
+ if (!fs17.existsSync(resolvedPath)) {
2176
+ fs17.mkdirSync(resolvedPath, { recursive: true });
2044
2177
  }
2045
2178
  const config = loadMCPConfig();
2046
2179
  saveMCPConfig(config);
@@ -2220,7 +2353,7 @@ var init_mcp = __esm({
2220
2353
  // src/commands/wizard/setup-flow.ts
2221
2354
  import { group, select as select3, multiselect as multiselect2, confirm as confirm2, spinner as spinner2, note as note3, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
2222
2355
  import pc4 from "picocolors";
2223
- import * as fs11 from "fs";
2356
+ import * as fs12 from "fs";
2224
2357
  import * as path12 from "path";
2225
2358
  async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
2226
2359
  const s = spinner2();
@@ -2395,7 +2528,7 @@ linked_projects:
2395
2528
  `;
2396
2529
  });
2397
2530
  }
2398
- fs11.writeFileSync(workspaceConfigPath, configContent);
2531
+ fs12.writeFileSync(workspaceConfigPath, configContent);
2399
2532
  if (config.addToGitignore) {
2400
2533
  updateGitignore(workspacePath, config.storageMode, config.tools);
2401
2534
  }
@@ -2435,8 +2568,8 @@ function updateGitignore(workspacePath, storageMode, tools) {
2435
2568
  }
2436
2569
  try {
2437
2570
  let content = "";
2438
- if (fs11.existsSync(gitignorePath)) {
2439
- content = fs11.readFileSync(gitignorePath, "utf-8");
2571
+ if (fs12.existsSync(gitignorePath)) {
2572
+ content = fs12.readFileSync(gitignorePath, "utf-8");
2440
2573
  }
2441
2574
  const lines = content.split("\n").map((line) => line.trim());
2442
2575
  const newEntries = [];
@@ -2461,7 +2594,7 @@ function updateGitignore(workspacePath, storageMode, tools) {
2461
2594
  newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
2462
2595
  }
2463
2596
  newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
2464
- fs11.writeFileSync(gitignorePath, newContent);
2597
+ fs12.writeFileSync(gitignorePath, newContent);
2465
2598
  return true;
2466
2599
  } catch {
2467
2600
  return false;
@@ -2482,7 +2615,7 @@ var init_setup_flow = __esm({
2482
2615
  // src/commands/wizard/link-flow.ts
2483
2616
  import { multiselect as multiselect3, spinner as spinner3, note as note4, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
2484
2617
  import pc5 from "picocolors";
2485
- import * as fs12 from "fs";
2618
+ import * as fs13 from "fs";
2486
2619
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
2487
2620
  const projects = scanForProjects({
2488
2621
  excludeWorkspace: workspaceName,
@@ -2520,7 +2653,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
2520
2653
  const s = spinner3();
2521
2654
  s.start("Linking projects");
2522
2655
  const configFilePath = getConfigPath(workspacePath);
2523
- let configContent = fs12.readFileSync(configFilePath, "utf-8");
2656
+ let configContent = fs13.readFileSync(configFilePath, "utf-8");
2524
2657
  if (configContent.includes("linked_projects:")) {
2525
2658
  const lines = configContent.split("\n");
2526
2659
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -2547,7 +2680,7 @@ linked_projects:
2547
2680
  `;
2548
2681
  });
2549
2682
  }
2550
- fs12.writeFileSync(configFilePath, configContent);
2683
+ fs13.writeFileSync(configFilePath, configContent);
2551
2684
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
2552
2685
  s.stop("Projects linked");
2553
2686
  const workspaceFile = `${workspaceName}.code-workspace`;
@@ -2572,7 +2705,7 @@ var init_link_flow = __esm({
2572
2705
  // src/commands/wizard/sync-flow.ts
2573
2706
  import { confirm as confirm3, spinner as spinner4, note as note5, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
2574
2707
  import pc6 from "picocolors";
2575
- import * as fs13 from "fs";
2708
+ import * as fs14 from "fs";
2576
2709
  import * as path13 from "path";
2577
2710
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
2578
2711
  const localPath = getLocalWorkspacePath(workspacePath);
@@ -2580,7 +2713,7 @@ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
2580
2713
  const globalPath = path13.join(customGlobalPath, "workspaces", workspaceName);
2581
2714
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
2582
2715
  const existingDirs = subdirs.filter(
2583
- (dir) => fs13.existsSync(path13.join(localPath, dir))
2716
+ (dir) => fs14.existsSync(path13.join(localPath, dir))
2584
2717
  );
2585
2718
  if (existingDirs.length === 0) {
2586
2719
  outro4(pc6.yellow("No data found in workspace storage to sync."));
@@ -2639,7 +2772,7 @@ var init_sync_flow = __esm({
2639
2772
  // src/commands/wizard/update-flow.ts
2640
2773
  import { confirm as confirm4, spinner as spinner5, note as note6, outro as outro5, cancel as cancel5, isCancel as isCancel6 } from "@clack/prompts";
2641
2774
  import pc7 from "picocolors";
2642
- import * as fs14 from "fs";
2775
+ import * as fs15 from "fs";
2643
2776
  import * as path14 from "path";
2644
2777
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
2645
2778
  const s = spinner5();
@@ -2673,7 +2806,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
2673
2806
  copyDirToAllStoragePaths(path14.join(agentCoreDir, "templates"), "templates", [dataPath]);
2674
2807
  }
2675
2808
  const configFilePath = getConfigPath(workspacePath);
2676
- const configContent = fs14.readFileSync(configFilePath, "utf-8");
2809
+ const configContent = fs15.readFileSync(configFilePath, "utf-8");
2677
2810
  if (configContent.includes("copilot: true")) {
2678
2811
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
2679
2812
  ensureDir(copilotPath);
@@ -2728,7 +2861,7 @@ __export(wizard_exports, {
2728
2861
  });
2729
2862
  import { intro as intro2, select as select4, spinner as spinner6, note as note7, outro as outro6, isCancel as isCancel7 } from "@clack/prompts";
2730
2863
  import pc8 from "picocolors";
2731
- import * as fs15 from "fs";
2864
+ import * as fs16 from "fs";
2732
2865
  async function runWizard() {
2733
2866
  intro2(pc8.cyan(pc8.inverse(" RRCE-Workflow Setup ")));
2734
2867
  const s = spinner6();
@@ -2748,18 +2881,18 @@ Workspace: ${pc8.bold(workspaceName)}`,
2748
2881
  workspacePath
2749
2882
  });
2750
2883
  const configFilePath = getConfigPath(workspacePath);
2751
- const isAlreadyConfigured = fs15.existsSync(configFilePath);
2884
+ const isAlreadyConfigured = fs16.existsSync(configFilePath);
2752
2885
  let currentStorageMode = null;
2753
2886
  if (isAlreadyConfigured) {
2754
2887
  try {
2755
- const configContent = fs15.readFileSync(configFilePath, "utf-8");
2888
+ const configContent = fs16.readFileSync(configFilePath, "utf-8");
2756
2889
  const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
2757
2890
  currentStorageMode = modeMatch?.[1] ?? null;
2758
2891
  } catch {
2759
2892
  }
2760
2893
  }
2761
2894
  const localDataPath = getLocalWorkspacePath(workspacePath);
2762
- const hasLocalData = fs15.existsSync(localDataPath);
2895
+ const hasLocalData = fs16.existsSync(localDataPath);
2763
2896
  if (isAlreadyConfigured) {
2764
2897
  const menuOptions = [];
2765
2898
  menuOptions.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",
@@ -50,11 +50,17 @@
50
50
  "@clack/prompts": "^0.11.0",
51
51
  "@modelcontextprotocol/sdk": "^1.25.1",
52
52
  "gray-matter": "^4.0.3",
53
+ "ink": "^6.6.0",
54
+ "ink-link": "^5.0.0",
55
+ "ink-spinner": "^5.0.0",
56
+ "ink-text-input": "^6.0.0",
53
57
  "picocolors": "^1.1.1",
58
+ "react": "^19.2.3",
54
59
  "zod": "^4.2.1"
55
60
  },
56
61
  "devDependencies": {
57
62
  "@types/node": "^25.0.3",
63
+ "@types/react": "^19.2.7",
58
64
  "esbuild": "^0.27.2",
59
65
  "tsx": "^4.21.0",
60
66
  "typescript": "^5.9.3"