rrce-workflow 0.2.51 → 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 +132 -71
  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
  {
@@ -3907,7 +3968,7 @@ async function runWizard() {
3907
3968
  Workspace: ${pc15.bold(workspaceName)}`,
3908
3969
  "Context"
3909
3970
  );
3910
- const detectedProjects = scanForProjects({
3971
+ const detectedProjects = projectService.scan({
3911
3972
  excludeWorkspace: workspaceName,
3912
3973
  workspacePath
3913
3974
  });
@@ -4003,7 +4064,7 @@ var init_wizard = __esm({
4003
4064
  "use strict";
4004
4065
  init_git();
4005
4066
  init_paths();
4006
- init_detection();
4067
+ init_detection_service();
4007
4068
  init_setup_flow();
4008
4069
  init_link_flow();
4009
4070
  init_sync_flow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.51",
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",