rrce-workflow 0.2.50 → 0.2.52

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 +148 -81
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -273,6 +273,11 @@ function parseWorkspaceConfig(configPath) {
273
273
  return null;
274
274
  }
275
275
  }
276
+ function findClosestProject(projects, cwd = process.cwd()) {
277
+ const matches = projects.filter((p) => cwd.startsWith(p.path));
278
+ matches.sort((a, b) => b.path.length - a.path.length);
279
+ return matches[0];
280
+ }
276
281
  var SKIP_DIRECTORIES;
277
282
  var init_detection = __esm({
278
283
  "src/lib/detection.ts"() {
@@ -301,6 +306,68 @@ var init_detection = __esm({
301
306
  }
302
307
  });
303
308
 
309
+ // src/lib/detection-service.ts
310
+ var ProjectDetectionService, projectService;
311
+ var init_detection_service = __esm({
312
+ "src/lib/detection-service.ts"() {
313
+ "use strict";
314
+ init_detection();
315
+ ProjectDetectionService = class {
316
+ cache = null;
317
+ cacheTime = 0;
318
+ TTL = 3e4;
319
+ // 30 seconds cache TTL (scanning is expensive)
320
+ lastOptions = null;
321
+ /**
322
+ * Scan for projects with caching
323
+ * Returns cached version if still valid and options match
324
+ */
325
+ scan(options) {
326
+ const now = Date.now();
327
+ const optionsMatch = JSON.stringify(options) === JSON.stringify(this.lastOptions);
328
+ if (this.cache && optionsMatch && now - this.cacheTime < this.TTL) {
329
+ return this.cache;
330
+ }
331
+ this.cache = scanForProjects(options);
332
+ this.cacheTime = now;
333
+ this.lastOptions = options || null;
334
+ return this.cache;
335
+ }
336
+ /**
337
+ * Force a fresh scan, bypassing cache
338
+ */
339
+ refresh(options) {
340
+ this.invalidate();
341
+ return this.scan(options);
342
+ }
343
+ /**
344
+ * Invalidate cache, forcing next scan to read from disk
345
+ */
346
+ invalidate() {
347
+ this.cache = null;
348
+ this.lastOptions = null;
349
+ }
350
+ /**
351
+ * Check if cache is currently valid
352
+ */
353
+ isCacheValid() {
354
+ return this.cache !== null && Date.now() - this.cacheTime < this.TTL;
355
+ }
356
+ /**
357
+ * Get cached projects without triggering a scan
358
+ * Returns null if cache is invalid
359
+ */
360
+ getCached() {
361
+ if (this.isCacheValid()) {
362
+ return this.cache;
363
+ }
364
+ return null;
365
+ }
366
+ };
367
+ projectService = new ProjectDetectionService();
368
+ }
369
+ });
370
+
304
371
  // src/lib/autocomplete-prompt.ts
305
372
  import * as fs3 from "fs";
306
373
  import * as path3 from "path";
@@ -1303,33 +1370,46 @@ var init_rag = __esm({
1303
1370
  init_logger();
1304
1371
  INDEX_VERSION = "1.0.0";
1305
1372
  DEFAULT_MODEL = "Xenova/all-MiniLM-L6-v2";
1306
- RAGService = class {
1307
- pipe = null;
1373
+ RAGService = class _RAGService {
1374
+ // Static cache for the pipeline to prevent reloading model for every instance
1375
+ static pipelineInstance = null;
1376
+ static activeModelName = null;
1377
+ static loadPromise = null;
1308
1378
  modelName;
1309
1379
  indexPath;
1310
1380
  index = null;
1311
- lastAccess = 0;
1312
1381
  constructor(indexPath, modelName = DEFAULT_MODEL) {
1313
1382
  this.indexPath = indexPath;
1314
1383
  this.modelName = modelName;
1315
1384
  }
1316
1385
  /**
1317
- * Lazy load the model
1386
+ * Lazy load the model (Singleton pattern)
1318
1387
  */
1319
1388
  async getPipeline() {
1320
- if (!this.pipe) {
1321
- logger.info(`RAG: Initializing model ${this.modelName}...`);
1389
+ if (_RAGService.activeModelName === this.modelName && _RAGService.pipelineInstance) {
1390
+ return _RAGService.pipelineInstance;
1391
+ }
1392
+ if (_RAGService.activeModelName === this.modelName && _RAGService.loadPromise) {
1393
+ return _RAGService.loadPromise;
1394
+ }
1395
+ logger.info(`RAG: Initializing model ${this.modelName}...`);
1396
+ _RAGService.activeModelName = this.modelName;
1397
+ _RAGService.loadPromise = (async () => {
1322
1398
  try {
1323
1399
  const { pipeline } = await import("@xenova/transformers");
1324
- this.pipe = await pipeline("feature-extraction", this.modelName);
1400
+ const pipe = await pipeline("feature-extraction", this.modelName);
1401
+ _RAGService.pipelineInstance = pipe;
1325
1402
  logger.info(`RAG: Model ${this.modelName} initialized successfully.`);
1403
+ return pipe;
1326
1404
  } catch (error) {
1327
1405
  logger.error(`RAG: Failed to initialize model ${this.modelName}`, error);
1406
+ _RAGService.pipelineInstance = null;
1407
+ _RAGService.activeModelName = null;
1408
+ _RAGService.loadPromise = null;
1328
1409
  throw error;
1329
1410
  }
1330
- }
1331
- this.lastAccess = Date.now();
1332
- return this.pipe;
1411
+ })();
1412
+ return _RAGService.loadPromise;
1333
1413
  }
1334
1414
  /**
1335
1415
  * Load index from disk
@@ -1488,7 +1568,7 @@ import * as fs13 from "fs";
1488
1568
  import * as path14 from "path";
1489
1569
  function getExposedProjects() {
1490
1570
  const config = loadMCPConfig();
1491
- const allProjects = scanForProjects();
1571
+ const allProjects = projectService.scan();
1492
1572
  const globalProjects = allProjects.filter((project) => isProjectExposed(config, project.name, project.dataPath));
1493
1573
  const activeProject = detectActiveProject(globalProjects);
1494
1574
  let linkedProjects = [];
@@ -1536,17 +1616,14 @@ function detectActiveProject(knownProjects) {
1536
1616
  let scanList = knownProjects;
1537
1617
  if (!scanList) {
1538
1618
  const config = loadMCPConfig();
1539
- const all = scanForProjects();
1619
+ const all = projectService.scan();
1540
1620
  scanList = all.filter((project) => isProjectExposed(config, project.name, project.dataPath));
1541
1621
  }
1542
- const cwd = process.cwd();
1543
- const matches = scanList.filter((p) => cwd.startsWith(p.path));
1544
- matches.sort((a, b) => b.path.length - a.path.length);
1545
- return matches[0];
1622
+ return findClosestProject(scanList);
1546
1623
  }
1547
1624
  function getProjectContext(projectName) {
1548
1625
  const config = loadMCPConfig();
1549
- const projects = scanForProjects();
1626
+ const projects = projectService.scan();
1550
1627
  const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.dataPath));
1551
1628
  if (!project) {
1552
1629
  return null;
@@ -1566,7 +1643,7 @@ function getProjectContext(projectName) {
1566
1643
  }
1567
1644
  function getProjectTasks(projectName) {
1568
1645
  const config = loadMCPConfig();
1569
- const projects = scanForProjects();
1646
+ const projects = projectService.scan();
1570
1647
  const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.dataPath));
1571
1648
  if (!project) {
1572
1649
  return [];
@@ -1685,11 +1762,44 @@ async function indexKnowledge(projectName, force = false) {
1685
1762
  return { success: false, message: `Indexing failed: ${error}`, filesIndexed: 0 };
1686
1763
  }
1687
1764
  }
1765
+ function getContextPreamble() {
1766
+ const projects = getExposedProjects();
1767
+ const activeProject = detectActiveProject();
1768
+ const projectList = projects.map((p) => {
1769
+ const isActive = activeProject && p.dataPath === activeProject.dataPath;
1770
+ return `- ${p.name} (${p.source}) ${isActive ? "**[ACTIVE]**" : ""}`;
1771
+ }).join("\n");
1772
+ let contextPreamble = `
1773
+ Context - Available Projects (MCP Hub):
1774
+ ${projectList}
1775
+ `;
1776
+ if (projects.length === 0) {
1777
+ contextPreamble += `
1778
+ WARNING: No projects are currently exposed to the MCP server.
1779
+ The user needs to run 'npx rrce-workflow mcp configure' in their terminal to select projects to expose.
1780
+ Please advise the user to do this if they expect to see project context.
1781
+ `;
1782
+ }
1783
+ if (activeProject) {
1784
+ contextPreamble += `
1785
+ Current Active Workspace: ${activeProject.name} (${activeProject.path})
1786
+ `;
1787
+ contextPreamble += `IMPORTANT: Treat '${activeProject.path}' as the {{WORKSPACE_ROOT}}. All relative path operations (file reads/writes) MUST be performed relative to this directory.
1788
+ `;
1789
+ }
1790
+ contextPreamble += `
1791
+ Note: If the user's request refers to a project not listed here, ask them to expose it via 'rrce-workflow mcp configure'.
1792
+
1793
+ ---
1794
+ `;
1795
+ return contextPreamble;
1796
+ }
1688
1797
  var init_resources = __esm({
1689
1798
  "src/mcp/resources.ts"() {
1690
1799
  "use strict";
1691
1800
  init_config();
1692
1801
  init_detection();
1802
+ init_detection_service();
1693
1803
  init_rag();
1694
1804
  }
1695
1805
  });
@@ -1951,28 +2061,7 @@ function registerToolHandlers(server) {
1951
2061
  stringArgs[key] = String(val);
1952
2062
  }
1953
2063
  const content = renderPrompt(promptDef.content, stringArgs);
1954
- const projects = getExposedProjects();
1955
- const activeProject = detectActiveProject();
1956
- const projectList = projects.map((p) => {
1957
- const isActive = activeProject && p.dataPath === activeProject.dataPath;
1958
- return `- ${p.name} (${p.source}) ${isActive ? "**[ACTIVE]**" : ""}`;
1959
- }).join("\n");
1960
- let contextPreamble = `
1961
- Context - Available Projects (MCP Hub):
1962
- ${projectList}
1963
- `;
1964
- if (activeProject) {
1965
- contextPreamble += `
1966
- Current Active Workspace: ${activeProject.name} (${activeProject.path})
1967
- `;
1968
- contextPreamble += `IMPORTANT: Treat '${activeProject.path}' as the {{WORKSPACE_ROOT}}. All relative path operations (file reads/writes) MUST be performed relative to this directory.
1969
- `;
1970
- }
1971
- contextPreamble += `
1972
- Note: If the user's request refers to a project not listed here, ask them to expose it via 'rrce-workflow mcp configure'.
1973
-
1974
- ---
1975
- `;
2064
+ const contextPreamble = getContextPreamble();
1976
2065
  return { content: [{ type: "text", text: contextPreamble + content }] };
1977
2066
  }
1978
2067
  case "help_setup": {
@@ -2046,35 +2135,7 @@ function registerPromptHandlers(server) {
2046
2135
  renderArgs[key] = String(val);
2047
2136
  }
2048
2137
  const content = renderPrompt(promptDef.content, renderArgs);
2049
- const projects = getExposedProjects();
2050
- const activeProject = detectActiveProject();
2051
- const projectList = projects.map((p) => {
2052
- const isActive = activeProject && p.dataPath === activeProject.dataPath;
2053
- return `- ${p.name} (${p.source}) ${isActive ? "**[ACTIVE]**" : ""}`;
2054
- }).join("\n");
2055
- let contextPreamble = `
2056
- Context - Available Projects (MCP Hub):
2057
- ${projectList}
2058
- `;
2059
- if (projects.length === 0) {
2060
- contextPreamble += `
2061
- WARNING: No projects are currently exposed to the MCP server.
2062
- The user needs to run 'npx rrce-workflow mcp configure' in their terminal to select projects to expose.
2063
- Please advise the user to do this if they expect to see project context.
2064
- `;
2065
- }
2066
- if (activeProject) {
2067
- contextPreamble += `
2068
- Current Active Workspace: ${activeProject.name} (${activeProject.path})
2069
- `;
2070
- contextPreamble += `IMPORTANT: Treat '${activeProject.path}' as the {{WORKSPACE_ROOT}}. All relative path operations (file reads/writes) MUST be performed relative to this directory.
2071
- `;
2072
- }
2073
- contextPreamble += `
2074
- Note: If the user's request refers to a project not listed here, ask them to expose it via 'rrce-workflow mcp configure'.
2075
-
2076
- ---
2077
- `;
2138
+ const contextPreamble = getContextPreamble();
2078
2139
  return {
2079
2140
  messages: [
2080
2141
  {
@@ -2944,7 +3005,7 @@ var App_exports = {};
2944
3005
  __export(App_exports, {
2945
3006
  App: () => App
2946
3007
  });
2947
- import { useState as useState4, useEffect as useEffect3, useCallback } from "react";
3008
+ import { useState as useState4, useEffect as useEffect3, useMemo, useCallback } from "react";
2948
3009
  import { Box as Box10, useInput as useInput3, useApp } from "ink";
2949
3010
  import fs15 from "fs";
2950
3011
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -2980,15 +3041,21 @@ var init_App = __esm({
2980
3041
  pid: process.pid,
2981
3042
  running: false
2982
3043
  });
2983
- const [configVersion, setConfigVersion] = useState4(0);
2984
- const config = loadMCPConfig();
2985
- const projects = scanForProjects();
2986
- const exposedProjects = projects.filter((p) => {
2987
- const cfg = config.projects.find(
2988
- (c) => c.path && c.path === p.path || !c.path && c.name === p.name
2989
- );
2990
- return cfg?.expose ?? config.defaults.includeNew;
2991
- });
3044
+ const [config, setConfig] = useState4(() => loadMCPConfig());
3045
+ const [projects, setProjects] = useState4(() => scanForProjects());
3046
+ const refreshData = useCallback(() => {
3047
+ setConfig(loadMCPConfig());
3048
+ setProjects(scanForProjects());
3049
+ }, []);
3050
+ const exposedProjects = useMemo(
3051
+ () => projects.filter((p) => {
3052
+ const cfg = config.projects.find(
3053
+ (c) => c.path && c.path === p.path || !c.path && c.name === p.name
3054
+ );
3055
+ return cfg?.expose ?? config.defaults.includeNew;
3056
+ }),
3057
+ [projects, config]
3058
+ );
2992
3059
  const workspacePath = detectWorkspaceRoot();
2993
3060
  const installStatus = checkInstallStatus(workspacePath);
2994
3061
  const installedCount = [
@@ -3901,7 +3968,7 @@ async function runWizard() {
3901
3968
  Workspace: ${pc15.bold(workspaceName)}`,
3902
3969
  "Context"
3903
3970
  );
3904
- const detectedProjects = scanForProjects({
3971
+ const detectedProjects = projectService.scan({
3905
3972
  excludeWorkspace: workspaceName,
3906
3973
  workspacePath
3907
3974
  });
@@ -3997,7 +4064,7 @@ var init_wizard = __esm({
3997
4064
  "use strict";
3998
4065
  init_git();
3999
4066
  init_paths();
4000
- init_detection();
4067
+ init_detection_service();
4001
4068
  init_setup_flow();
4002
4069
  init_link_flow();
4003
4070
  init_sync_flow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.50",
3
+ "version": "0.2.52",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",