rrce-workflow 0.2.62 → 0.2.64

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 +246 -168
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24,46 +24,86 @@ var init_git = __esm({
24
24
  }
25
25
  });
26
26
 
27
- // src/lib/paths.ts
27
+ // src/lib/preferences.ts
28
28
  import * as fs from "fs";
29
29
  import * as path from "path";
30
+ function getPreferencesPath() {
31
+ const home = process.env.HOME || "~";
32
+ return path.join(home, ".rrce-workflow", "preferences.json");
33
+ }
34
+ function loadUserPreferences() {
35
+ const prefPath = getPreferencesPath();
36
+ if (!fs.existsSync(prefPath)) {
37
+ return {};
38
+ }
39
+ try {
40
+ return JSON.parse(fs.readFileSync(prefPath, "utf-8"));
41
+ } catch {
42
+ return {};
43
+ }
44
+ }
45
+ function saveUserPreferences(prefs) {
46
+ const prefPath = getPreferencesPath();
47
+ ensureDir(path.dirname(prefPath));
48
+ const current = loadUserPreferences();
49
+ const refined = { ...current, ...prefs };
50
+ fs.writeFileSync(prefPath, JSON.stringify(refined, null, 2));
51
+ }
52
+ var init_preferences = __esm({
53
+ "src/lib/preferences.ts"() {
54
+ "use strict";
55
+ init_paths();
56
+ }
57
+ });
58
+
59
+ // src/lib/paths.ts
60
+ import * as fs2 from "fs";
61
+ import * as path2 from "path";
62
+ function getEffectiveGlobalPath() {
63
+ const prefs = loadUserPreferences();
64
+ if (prefs.useCustomGlobalPath && prefs.defaultGlobalPath) {
65
+ return prefs.defaultGlobalPath;
66
+ }
67
+ return RRCE_HOME;
68
+ }
30
69
  function detectWorkspaceRoot() {
31
70
  if (RRCE_WORKSPACE) {
32
71
  return RRCE_WORKSPACE;
33
72
  }
34
73
  let current = process.cwd();
35
74
  while (current !== "/") {
36
- if (fs.existsSync(path.join(current, ".git")) || fs.existsSync(path.join(current, ".rrce-workflow", "config.yaml")) || fs.existsSync(path.join(current, ".rrce-workflow.yaml"))) {
75
+ if (fs2.existsSync(path2.join(current, ".git")) || fs2.existsSync(path2.join(current, ".rrce-workflow", "config.yaml")) || fs2.existsSync(path2.join(current, ".rrce-workflow.yaml"))) {
37
76
  return current;
38
77
  }
39
- current = path.dirname(current);
78
+ current = path2.dirname(current);
40
79
  }
41
80
  return process.cwd();
42
81
  }
43
82
  function getConfigPath(workspaceRoot) {
44
- const newPath = path.join(workspaceRoot, ".rrce-workflow", "config.yaml");
45
- const legacyPath = path.join(workspaceRoot, ".rrce-workflow.yaml");
46
- if (fs.existsSync(newPath)) {
83
+ const newPath = path2.join(workspaceRoot, ".rrce-workflow", "config.yaml");
84
+ const legacyPath = path2.join(workspaceRoot, ".rrce-workflow.yaml");
85
+ if (fs2.existsSync(newPath)) {
47
86
  return newPath;
48
87
  }
49
- if (fs.existsSync(legacyPath)) {
88
+ if (fs2.existsSync(legacyPath)) {
50
89
  return legacyPath;
51
90
  }
52
91
  try {
53
- const rrceHome = getDefaultRRCEHome();
54
- const mcpConfigPath = path.join(rrceHome, "mcp.yaml");
55
- if (fs.existsSync(mcpConfigPath)) {
56
- const mcpContent = fs.readFileSync(mcpConfigPath, "utf-8");
92
+ const rrceHome = getDefaultRRCEHome2();
93
+ const mcpConfigPath = path2.join(rrceHome, "mcp.yaml");
94
+ if (fs2.existsSync(mcpConfigPath)) {
95
+ const mcpContent = fs2.readFileSync(mcpConfigPath, "utf-8");
57
96
  const lines = mcpContent.split("\n");
58
97
  let currentName = "";
59
98
  for (let i = 0; i < lines.length; i++) {
60
- const line = lines[i].trim();
99
+ const line = lines[i]?.trim();
100
+ if (!line) continue;
61
101
  if (line.startsWith("- name:")) {
62
102
  currentName = line.replace("- name:", "").trim();
63
103
  } else if (line.startsWith("path:")) {
64
104
  const p = line.replace("path:", "").trim();
65
105
  if (p === workspaceRoot || p === `"${workspaceRoot}"` || p === `'${workspaceRoot}'`) {
66
- return path.join(rrceHome, "workspaces", currentName, "config.yaml");
106
+ return path2.join(rrceHome, "workspaces", currentName, "config.yaml");
67
107
  }
68
108
  }
69
109
  }
@@ -73,42 +113,42 @@ function getConfigPath(workspaceRoot) {
73
113
  return newPath;
74
114
  }
75
115
  function getWorkspaceName(workspaceRoot) {
76
- return path.basename(workspaceRoot);
116
+ return path2.basename(workspaceRoot);
77
117
  }
78
118
  function getRRCEHome() {
79
119
  return RRCE_HOME;
80
120
  }
81
121
  function getLocalWorkspacePath(workspaceRoot) {
82
- return path.join(workspaceRoot, ".rrce-workflow");
122
+ return path2.join(workspaceRoot, ".rrce-workflow");
83
123
  }
84
124
  function ensureDir(dirPath) {
85
- if (!fs.existsSync(dirPath)) {
86
- fs.mkdirSync(dirPath, { recursive: true });
125
+ if (!fs2.existsSync(dirPath)) {
126
+ fs2.mkdirSync(dirPath, { recursive: true });
87
127
  }
88
128
  }
89
129
  function getAgentPromptPath(workspaceRoot, tool) {
90
130
  if (tool === "copilot") {
91
- return path.join(workspaceRoot, ".github", "agents");
131
+ return path2.join(workspaceRoot, ".github", "agents");
92
132
  } else {
93
- return path.join(workspaceRoot, ".agent", "workflows");
133
+ return path2.join(workspaceRoot, ".agent", "workflows");
94
134
  }
95
135
  }
96
136
  function copyToAllStoragePaths(sourceFile, relativePath, dataPaths) {
97
- const content = fs.readFileSync(sourceFile);
137
+ const content = fs2.readFileSync(sourceFile);
98
138
  for (const dataPath of dataPaths) {
99
- const targetPath = path.join(dataPath, relativePath);
100
- ensureDir(path.dirname(targetPath));
101
- fs.writeFileSync(targetPath, content);
139
+ const targetPath = path2.join(dataPath, relativePath);
140
+ ensureDir(path2.dirname(targetPath));
141
+ fs2.writeFileSync(targetPath, content);
102
142
  }
103
143
  }
104
144
  function copyDirToAllStoragePaths(sourceDir, relativeDir, dataPaths) {
105
- if (!fs.existsSync(sourceDir)) {
145
+ if (!fs2.existsSync(sourceDir)) {
106
146
  return;
107
147
  }
108
- const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
148
+ const entries = fs2.readdirSync(sourceDir, { withFileTypes: true });
109
149
  for (const entry of entries) {
110
- const sourcePath = path.join(sourceDir, entry.name);
111
- const relativePath = path.join(relativeDir, entry.name);
150
+ const sourcePath = path2.join(sourceDir, entry.name);
151
+ const relativePath = path2.join(relativeDir, entry.name);
112
152
  if (entry.isDirectory()) {
113
153
  copyDirToAllStoragePaths(sourcePath, relativePath, dataPaths);
114
154
  } else {
@@ -119,38 +159,38 @@ function copyDirToAllStoragePaths(sourceDir, relativeDir, dataPaths) {
119
159
  function syncMetadataToAll(agentCorePath, dataPaths) {
120
160
  const metadataDirs = ["knowledge", "refs", "tasks"];
121
161
  for (const dir of metadataDirs) {
122
- const sourceDir = path.join(agentCorePath, dir);
162
+ const sourceDir = path2.join(agentCorePath, dir);
123
163
  copyDirToAllStoragePaths(sourceDir, dir, dataPaths);
124
164
  }
125
165
  }
126
166
  function checkWriteAccess(dirPath) {
127
- const testFile = path.join(dirPath, ".rrce-write-test");
167
+ const testFile = path2.join(dirPath, ".rrce-write-test");
128
168
  try {
129
- if (!fs.existsSync(dirPath)) {
130
- fs.mkdirSync(dirPath, { recursive: true });
169
+ if (!fs2.existsSync(dirPath)) {
170
+ fs2.mkdirSync(dirPath, { recursive: true });
131
171
  }
132
- fs.writeFileSync(testFile, "write-test");
133
- fs.unlinkSync(testFile);
172
+ fs2.writeFileSync(testFile, "write-test");
173
+ fs2.unlinkSync(testFile);
134
174
  return true;
135
175
  } catch {
136
176
  try {
137
- if (fs.existsSync(testFile)) {
138
- fs.unlinkSync(testFile);
177
+ if (fs2.existsSync(testFile)) {
178
+ fs2.unlinkSync(testFile);
139
179
  }
140
180
  } catch {
141
181
  }
142
182
  return false;
143
183
  }
144
184
  }
145
- function getDefaultRRCEHome() {
146
- return process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
185
+ function getDefaultRRCEHome2() {
186
+ return process.env.RRCE_HOME || path2.join(process.env.HOME || "~", ".rrce-workflow");
147
187
  }
148
188
  function getEffectiveRRCEHome(workspaceRoot) {
149
189
  if (workspaceRoot) {
150
190
  const configPath = getConfigPath(workspaceRoot);
151
- if (fs.existsSync(configPath)) {
191
+ if (fs2.existsSync(configPath)) {
152
192
  try {
153
- const content = fs.readFileSync(configPath, "utf-8");
193
+ const content = fs2.readFileSync(configPath, "utf-8");
154
194
  const globalPathMatch = content.match(/globalPath:\s*["']?([^"'\n]+)["']?/);
155
195
  if (globalPathMatch?.[1]) {
156
196
  return globalPathMatch[1].trim();
@@ -159,24 +199,34 @@ function getEffectiveRRCEHome(workspaceRoot) {
159
199
  }
160
200
  }
161
201
  }
162
- return getDefaultRRCEHome();
202
+ return getDefaultRRCEHome2();
163
203
  }
164
204
  var RRCE_HOME, RRCE_WORKSPACE;
165
205
  var init_paths = __esm({
166
206
  "src/lib/paths.ts"() {
167
207
  "use strict";
168
- RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
208
+ init_preferences();
209
+ RRCE_HOME = process.env.RRCE_HOME || path2.join(process.env.HOME || "~", ".rrce-workflow");
169
210
  RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
170
211
  }
171
212
  });
172
213
 
173
214
  // src/lib/detection.ts
174
- import * as fs2 from "fs";
175
- import * as path2 from "path";
215
+ import * as fs3 from "fs";
216
+ import * as path3 from "path";
176
217
  function scanForProjects(options = {}) {
177
- const { excludeWorkspace, workspacePath } = options;
218
+ const { excludeWorkspace, workspacePath, knownPaths } = options;
178
219
  const projects = [];
179
220
  const seenPaths = /* @__PURE__ */ new Set();
221
+ if (knownPaths && knownPaths.length > 0) {
222
+ const explicitProjects = scanKnownPaths(knownPaths, excludeWorkspace);
223
+ for (const project of explicitProjects) {
224
+ if (!seenPaths.has(project.dataPath)) {
225
+ seenPaths.add(project.dataPath);
226
+ projects.push(project);
227
+ }
228
+ }
229
+ }
180
230
  const globalProjects = scanGlobalStorage(excludeWorkspace);
181
231
  for (const project of globalProjects) {
182
232
  if (!seenPaths.has(project.dataPath)) {
@@ -193,33 +243,67 @@ function scanForProjects(options = {}) {
193
243
  }
194
244
  return projects;
195
245
  }
246
+ function scanKnownPaths(paths, excludeWorkspace) {
247
+ const projects = [];
248
+ for (const p of paths) {
249
+ try {
250
+ if (!fs3.existsSync(p)) continue;
251
+ const localConfigPath = path3.join(p, ".rrce-workflow", "config.yaml");
252
+ if (fs3.existsSync(localConfigPath)) {
253
+ const config = parseWorkspaceConfig(localConfigPath);
254
+ if (config?.name === excludeWorkspace) continue;
255
+ const fullPath = path3.join(p, ".rrce-workflow");
256
+ const knowledgePath = path3.join(fullPath, "knowledge");
257
+ const refsPath = path3.join(fullPath, "refs");
258
+ const tasksPath = path3.join(fullPath, "tasks");
259
+ projects.push({
260
+ name: config?.name || path3.basename(p),
261
+ path: p,
262
+ dataPath: fullPath,
263
+ source: "local",
264
+ storageMode: config?.storageMode,
265
+ knowledgePath: fs3.existsSync(knowledgePath) ? knowledgePath : void 0,
266
+ refsPath: fs3.existsSync(refsPath) ? refsPath : void 0,
267
+ tasksPath: fs3.existsSync(tasksPath) ? tasksPath : void 0,
268
+ semanticSearchEnabled: config?.semanticSearchEnabled
269
+ });
270
+ continue;
271
+ }
272
+ } catch {
273
+ }
274
+ }
275
+ return projects;
276
+ }
196
277
  function scanGlobalStorage(excludeWorkspace) {
197
- const rrceHome = getDefaultRRCEHome();
198
- const workspacesDir = path2.join(rrceHome, "workspaces");
278
+ const rrceHome = getEffectiveGlobalPath();
279
+ const workspacesDir = path3.join(rrceHome, "workspaces");
199
280
  const projects = [];
200
- if (!fs2.existsSync(workspacesDir)) {
281
+ if (!fs3.existsSync(workspacesDir)) {
201
282
  return projects;
202
283
  }
203
284
  try {
204
- const entries = fs2.readdirSync(workspacesDir, { withFileTypes: true });
285
+ const entries = fs3.readdirSync(workspacesDir, { withFileTypes: true });
205
286
  for (const entry of entries) {
206
287
  if (!entry.isDirectory()) continue;
207
288
  if (entry.name === excludeWorkspace) continue;
208
- const projectDataPath = path2.join(workspacesDir, entry.name);
209
- const knowledgePath = path2.join(projectDataPath, "knowledge");
210
- const refsPath = path2.join(projectDataPath, "refs");
211
- const tasksPath = path2.join(projectDataPath, "tasks");
289
+ const projectDataPath = path3.join(workspacesDir, entry.name);
290
+ const knowledgePath = path3.join(projectDataPath, "knowledge");
291
+ const refsPath = path3.join(projectDataPath, "refs");
292
+ const tasksPath = path3.join(projectDataPath, "tasks");
293
+ const configPath = path3.join(projectDataPath, "config.yaml");
294
+ const config = parseWorkspaceConfig(configPath);
212
295
  projects.push({
213
- name: entry.name,
296
+ name: config?.name || entry.name,
214
297
  path: projectDataPath,
215
- // For global projects, path is the data path
298
+ // Still use dataPath as defaults, BUT...
299
+ sourcePath: config?.sourcePath,
300
+ // ...expose sourcePath if available
216
301
  dataPath: projectDataPath,
217
302
  source: "global",
218
- knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
219
- refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
220
- tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0,
221
- // Global projects store config at the root of their data path
222
- semanticSearchEnabled: parseWorkspaceConfig(path2.join(projectDataPath, "config.yaml"))?.semanticSearchEnabled
303
+ knowledgePath: fs3.existsSync(knowledgePath) ? knowledgePath : void 0,
304
+ refsPath: fs3.existsSync(refsPath) ? refsPath : void 0,
305
+ tasksPath: fs3.existsSync(tasksPath) ? tasksPath : void 0,
306
+ semanticSearchEnabled: config?.semanticSearchEnabled
223
307
  });
224
308
  }
225
309
  } catch {
@@ -234,29 +318,29 @@ function scanHomeDirectory(excludePath) {
234
318
  function scanDir(dirPath, depth) {
235
319
  if (depth > maxDepth) return;
236
320
  try {
237
- const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
321
+ const entries = fs3.readdirSync(dirPath, { withFileTypes: true });
238
322
  for (const entry of entries) {
239
323
  if (!entry.isDirectory()) continue;
240
- const fullPath = path2.join(dirPath, entry.name);
324
+ const fullPath = path3.join(dirPath, entry.name);
241
325
  if (excludePath && fullPath === excludePath) continue;
242
326
  if (entry.name === ".rrce-workflow") {
243
- const configPath = path2.join(fullPath, "config.yaml");
244
- if (fs2.existsSync(configPath)) {
327
+ const configPath = path3.join(fullPath, "config.yaml");
328
+ if (fs3.existsSync(configPath)) {
245
329
  const projectPath = dirPath;
246
- const projectName = path2.basename(projectPath);
330
+ const projectName = path3.basename(projectPath);
247
331
  const config = parseWorkspaceConfig(configPath);
248
- const knowledgePath = path2.join(fullPath, "knowledge");
249
- const refsPath = path2.join(fullPath, "refs");
250
- const tasksPath = path2.join(fullPath, "tasks");
332
+ const knowledgePath = path3.join(fullPath, "knowledge");
333
+ const refsPath = path3.join(fullPath, "refs");
334
+ const tasksPath = path3.join(fullPath, "tasks");
251
335
  projects.push({
252
336
  name: config?.name || projectName,
253
337
  path: projectPath,
254
338
  dataPath: fullPath,
255
339
  source: "local",
256
340
  storageMode: config?.storageMode,
257
- knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
258
- refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
259
- tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0,
341
+ knowledgePath: fs3.existsSync(knowledgePath) ? knowledgePath : void 0,
342
+ refsPath: fs3.existsSync(refsPath) ? refsPath : void 0,
343
+ tasksPath: fs3.existsSync(tasksPath) ? tasksPath : void 0,
260
344
  semanticSearchEnabled: config?.semanticSearchEnabled
261
345
  });
262
346
  }
@@ -274,8 +358,9 @@ function scanHomeDirectory(excludePath) {
274
358
  }
275
359
  function parseWorkspaceConfig(configPath) {
276
360
  try {
277
- const content = fs2.readFileSync(configPath, "utf-8");
361
+ const content = fs3.readFileSync(configPath, "utf-8");
278
362
  const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
363
+ const sourcePathMatch = content.match(/sourcePath:\s*["']?([^"'\n]+)["']?/);
279
364
  const modeMatch = content.match(/mode:\s*(global|workspace)/);
280
365
  const linkedProjects = [];
281
366
  const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
@@ -291,7 +376,8 @@ function parseWorkspaceConfig(configPath) {
291
376
  const semanticSearchMatch = content.match(/semantic_search:\s*\n\s*enabled:\s*(true|false)/);
292
377
  const semanticSearchEnabled = semanticSearchMatch ? semanticSearchMatch[1] === "true" : false;
293
378
  return {
294
- name: nameMatch?.[1]?.trim() || path2.basename(path2.dirname(path2.dirname(configPath))),
379
+ name: nameMatch?.[1]?.trim() || path3.basename(path3.dirname(path3.dirname(configPath))),
380
+ sourcePath: sourcePathMatch?.[1]?.trim(),
295
381
  storageMode: modeMatch?.[1] || "global",
296
382
  linkedProjects: linkedProjects.length > 0 ? linkedProjects : void 0,
297
383
  semanticSearchEnabled
@@ -396,8 +482,8 @@ var init_detection_service = __esm({
396
482
  });
397
483
 
398
484
  // src/lib/autocomplete-prompt.ts
399
- import * as fs3 from "fs";
400
- import * as path3 from "path";
485
+ import * as fs4 from "fs";
486
+ import * as path4 from "path";
401
487
  import * as readline from "readline";
402
488
  import pc from "picocolors";
403
489
  function directoryPrompt(opts) {
@@ -456,19 +542,19 @@ function completeDirectory(line) {
456
542
  prefix = "";
457
543
  basePath = expanded;
458
544
  } else {
459
- dirToScan = path3.dirname(expanded);
460
- prefix = path3.basename(expanded).toLowerCase();
545
+ dirToScan = path4.dirname(expanded);
546
+ prefix = path4.basename(expanded).toLowerCase();
461
547
  basePath = dirToScan === "/" ? "/" : dirToScan + "/";
462
548
  }
463
- if (!fs3.existsSync(dirToScan)) {
549
+ if (!fs4.existsSync(dirToScan)) {
464
550
  return [[], line];
465
551
  }
466
- const entries = fs3.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
552
+ const entries = fs4.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
467
553
  if (!entry.isDirectory()) return false;
468
554
  if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
469
555
  return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
470
556
  }).map((entry) => {
471
- const fullPath = path3.join(dirToScan, entry.name);
557
+ const fullPath = path4.join(dirToScan, entry.name);
472
558
  const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
473
559
  return displayPath + "/";
474
560
  }).sort();
@@ -507,38 +593,6 @@ var init_autocomplete_prompt = __esm({
507
593
  }
508
594
  });
509
595
 
510
- // src/lib/preferences.ts
511
- import * as fs4 from "fs";
512
- import * as path4 from "path";
513
- function getPreferencesPath() {
514
- const home = process.env.HOME || "~";
515
- return path4.join(home, ".rrce-workflow", "preferences.json");
516
- }
517
- function loadUserPreferences() {
518
- const prefPath = getPreferencesPath();
519
- if (!fs4.existsSync(prefPath)) {
520
- return {};
521
- }
522
- try {
523
- return JSON.parse(fs4.readFileSync(prefPath, "utf-8"));
524
- } catch {
525
- return {};
526
- }
527
- }
528
- function saveUserPreferences(prefs) {
529
- const prefPath = getPreferencesPath();
530
- ensureDir(path4.dirname(prefPath));
531
- const current = loadUserPreferences();
532
- const refined = { ...current, ...prefs };
533
- fs4.writeFileSync(prefPath, JSON.stringify(refined, null, 2));
534
- }
535
- var init_preferences = __esm({
536
- "src/lib/preferences.ts"() {
537
- "use strict";
538
- init_paths();
539
- }
540
- });
541
-
542
596
  // src/lib/tui-utils.ts
543
597
  var tui_utils_exports = {};
544
598
  __export(tui_utils_exports, {
@@ -549,7 +603,7 @@ import pc2 from "picocolors";
549
603
  import * as path5 from "path";
550
604
  async function resolveGlobalPath() {
551
605
  const prefs = loadUserPreferences();
552
- const defaultPath = prefs.defaultGlobalPath || getDefaultRRCEHome();
606
+ const defaultPath = prefs.defaultGlobalPath || getDefaultRRCEHome2();
553
607
  const isDefaultWritable = checkWriteAccess(defaultPath);
554
608
  const options = [
555
609
  {
@@ -585,7 +639,7 @@ Please choose a custom path instead.`,
585
639
  }
586
640
  return defaultPath;
587
641
  }
588
- const suggestedPath = path5.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
642
+ const suggestedPath = path5.join(process.env.HOME || "~", ".rrce-workflow");
589
643
  const customPath = await directoryPrompt({
590
644
  message: "Enter custom global path (Tab to autocomplete):",
591
645
  defaultValue: suggestedPath,
@@ -606,7 +660,7 @@ Please choose a custom path instead.`,
606
660
  if (!expandedPath.endsWith(".rrce-workflow")) {
607
661
  expandedPath = path5.join(expandedPath, ".rrce-workflow");
608
662
  }
609
- saveUserPreferences({ defaultGlobalPath: expandedPath });
663
+ saveUserPreferences({ defaultGlobalPath: expandedPath, useCustomGlobalPath: true });
610
664
  return expandedPath;
611
665
  }
612
666
  var init_tui_utils = __esm({
@@ -1129,7 +1183,7 @@ function getProjectPermissions(config, name, projectPath) {
1129
1183
  return project?.permissions ?? config.defaults.permissions;
1130
1184
  }
1131
1185
  function cleanStaleProjects(config) {
1132
- const rrceHome = getEffectiveRRCEHome();
1186
+ const rrceHome = getEffectiveGlobalPath();
1133
1187
  const globalWorkspacesDir = path9.join(rrceHome, "workspaces");
1134
1188
  const validProjects = [];
1135
1189
  const removed = [];
@@ -1197,7 +1251,7 @@ function installAgentPrompts(config, workspacePath, dataPaths) {
1197
1251
  function createWorkspaceConfig(config, workspacePath, workspaceName) {
1198
1252
  let configPath;
1199
1253
  if (config.storageMode === "global") {
1200
- const rrceHome = config.globalPath || getDefaultRRCEHome();
1254
+ const rrceHome = config.globalPath || getDefaultRRCEHome2();
1201
1255
  configPath = path10.join(rrceHome, "workspaces", workspaceName, "config.yaml");
1202
1256
  } else {
1203
1257
  configPath = path10.join(workspacePath, ".rrce-workflow", "config.yaml");
@@ -1208,7 +1262,7 @@ version: 1
1208
1262
 
1209
1263
  storage:
1210
1264
  mode: ${config.storageMode}`;
1211
- if (config.globalPath && config.globalPath !== getDefaultRRCEHome()) {
1265
+ if (config.globalPath && config.globalPath !== getDefaultRRCEHome2()) {
1212
1266
  content += `
1213
1267
  globalPath: "${config.globalPath}"`;
1214
1268
  }
@@ -1216,6 +1270,7 @@ storage:
1216
1270
 
1217
1271
  project:
1218
1272
  name: "${workspaceName}"
1273
+ sourcePath: "${workspacePath}"
1219
1274
 
1220
1275
  tools:
1221
1276
  copilot: ${config.storageMode === "workspace" && config.tools.includes("copilot")}
@@ -1243,25 +1298,14 @@ async function registerWithMCP(config, workspacePath, workspaceName) {
1243
1298
  try {
1244
1299
  const { loadMCPConfig: loadMCPConfig3, saveMCPConfig: saveMCPConfig2, setProjectConfig: setProjectConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1245
1300
  const mcpConfig = loadMCPConfig3();
1246
- if (config.storageMode === "workspace") {
1247
- setProjectConfig2(
1248
- mcpConfig,
1249
- workspaceName,
1250
- true,
1251
- void 0,
1252
- void 0,
1253
- config.enableRAG ? { enabled: true } : void 0
1254
- );
1255
- } else {
1256
- setProjectConfig2(
1257
- mcpConfig,
1258
- workspaceName,
1259
- true,
1260
- void 0,
1261
- workspacePath,
1262
- config.enableRAG ? { enabled: true } : void 0
1263
- );
1264
- }
1301
+ setProjectConfig2(
1302
+ mcpConfig,
1303
+ workspaceName,
1304
+ true,
1305
+ void 0,
1306
+ workspacePath,
1307
+ config.enableRAG ? { enabled: true } : void 0
1308
+ );
1265
1309
  saveMCPConfig2(mcpConfig);
1266
1310
  } catch (e) {
1267
1311
  note2(
@@ -1274,7 +1318,7 @@ You can configure MCP later: ${pc4.cyan("npx rrce-workflow mcp")}`,
1274
1318
  }
1275
1319
  }
1276
1320
  function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
1277
- const globalPath = path10.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
1321
+ const globalPath = path10.join(customGlobalPath || getDefaultRRCEHome2(), "workspaces", workspaceName);
1278
1322
  const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
1279
1323
  switch (mode) {
1280
1324
  case "global":
@@ -1658,7 +1702,8 @@ import * as fs13 from "fs";
1658
1702
  import * as path14 from "path";
1659
1703
  function getExposedProjects() {
1660
1704
  const config = loadMCPConfig();
1661
- const allProjects = projectService.scan();
1705
+ const knownPaths = config.projects.map((p) => p.path).filter((p) => !!p);
1706
+ const allProjects = projectService.scan({ knownPaths });
1662
1707
  const globalProjects = allProjects.filter((project) => isProjectExposed(config, project.name, project.dataPath));
1663
1708
  const activeProject = detectActiveProject(globalProjects);
1664
1709
  let linkedProjects = [];
@@ -1710,7 +1755,8 @@ function detectActiveProject(knownProjects) {
1710
1755
  let scanList = knownProjects;
1711
1756
  if (!scanList) {
1712
1757
  const config = loadMCPConfig();
1713
- const all = projectService.scan();
1758
+ const knownPaths = config.projects.map((p) => p.path).filter((p) => !!p);
1759
+ const all = projectService.scan({ knownPaths });
1714
1760
  scanList = all.filter((project) => isProjectExposed(config, project.name, project.dataPath));
1715
1761
  }
1716
1762
  return findClosestProject(scanList);
@@ -1833,11 +1879,12 @@ async function indexKnowledge(projectName, force = false) {
1833
1879
  }
1834
1880
  const projConfig = config.projects.find(
1835
1881
  (p) => p.path && p.path === project.dataPath || !p.path && p.name === project.name
1836
- );
1837
- if (!projConfig?.semanticSearch?.enabled) {
1882
+ ) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
1883
+ const isEnabled = projConfig?.semanticSearch?.enabled || project.semanticSearchEnabled;
1884
+ if (!isEnabled) {
1838
1885
  return { success: false, message: "Semantic Search is not enabled for this project", filesIndexed: 0, filesSkipped: 0 };
1839
1886
  }
1840
- const scanRoot = project.path || project.dataPath;
1887
+ const scanRoot = project.sourcePath || project.path || project.dataPath;
1841
1888
  if (!fs13.existsSync(scanRoot)) {
1842
1889
  return { success: false, message: "Project root not found", filesIndexed: 0, filesSkipped: 0 };
1843
1890
  }
@@ -1882,7 +1929,8 @@ async function indexKnowledge(projectName, force = false) {
1882
1929
  const SKIP_DIRS = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "venv", ".venv", "target", "vendor"];
1883
1930
  try {
1884
1931
  const indexPath = path14.join(project.knowledgePath || path14.join(scanRoot, ".rrce-workflow", "knowledge"), "embeddings.json");
1885
- const rag = new RAGService(indexPath, projConfig.semanticSearch.model);
1932
+ const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
1933
+ const rag = new RAGService(indexPath, model);
1886
1934
  let indexed = 0;
1887
1935
  let skipped = 0;
1888
1936
  const scanDir = async (dir) => {
@@ -2277,6 +2325,7 @@ import {
2277
2325
  ListPromptsRequestSchema,
2278
2326
  GetPromptRequestSchema
2279
2327
  } from "@modelcontextprotocol/sdk/types.js";
2328
+ import * as path15 from "path";
2280
2329
  function registerPromptHandlers(server) {
2281
2330
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
2282
2331
  logger.debug("Listing prompts");
@@ -2311,8 +2360,35 @@ function registerPromptHandlers(server) {
2311
2360
  for (const [key, val] of Object.entries(providedArgs)) {
2312
2361
  renderArgs[key] = String(val);
2313
2362
  }
2363
+ const activeProject = detectActiveProject();
2364
+ const DEFAULT_RRCE_HOME = getEffectiveGlobalPath();
2365
+ let resolvedRrceData = ".rrce-workflow/";
2366
+ let resolvedRrceHome = DEFAULT_RRCE_HOME;
2367
+ let resolvedWorkspaceRoot = process.cwd();
2368
+ let resolvedWorkspaceName = "current-project";
2369
+ if (activeProject) {
2370
+ resolvedRrceData = activeProject.dataPath + "/";
2371
+ resolvedWorkspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
2372
+ resolvedWorkspaceName = activeProject.name;
2373
+ if (activeProject.source === "global") {
2374
+ const workspacesDir = path15.dirname(activeProject.dataPath);
2375
+ resolvedRrceHome = path15.dirname(workspacesDir);
2376
+ }
2377
+ }
2378
+ if (!renderArgs["RRCE_DATA"]) renderArgs["RRCE_DATA"] = resolvedRrceData;
2379
+ if (!renderArgs["RRCE_HOME"]) renderArgs["RRCE_HOME"] = resolvedRrceHome;
2380
+ if (!renderArgs["WORKSPACE_ROOT"]) renderArgs["WORKSPACE_ROOT"] = resolvedWorkspaceRoot;
2381
+ if (!renderArgs["WORKSPACE_NAME"]) renderArgs["WORKSPACE_NAME"] = resolvedWorkspaceName;
2314
2382
  const content = renderPrompt(promptDef.content, renderArgs);
2315
- const contextPreamble = getContextPreamble();
2383
+ let contextPreamble = getContextPreamble();
2384
+ contextPreamble += `
2385
+ ### System Resolved Paths (OVERRIDE)
2386
+ The system has pre-resolved the configuration for this project. Use these values instead of manual resolution:
2387
+ - **RRCE_DATA**: \`${resolvedRrceData}\` (Stores knowledge, tasks, refs)
2388
+ - **WORKSPACE_ROOT**: \`${resolvedWorkspaceRoot}\` (Source code location)
2389
+ - **RRCE_HOME**: \`${resolvedRrceHome}\`
2390
+ - **Current Project**: ${resolvedWorkspaceName}
2391
+ `;
2316
2392
  return {
2317
2393
  messages: [
2318
2394
  {
@@ -2336,6 +2412,7 @@ var init_prompts3 = __esm({
2336
2412
  init_logger();
2337
2413
  init_resources();
2338
2414
  init_prompts2();
2415
+ init_paths();
2339
2416
  }
2340
2417
  });
2341
2418
 
@@ -2418,7 +2495,7 @@ var init_server = __esm({
2418
2495
 
2419
2496
  // src/mcp/install.ts
2420
2497
  import * as fs14 from "fs";
2421
- import * as path15 from "path";
2498
+ import * as path16 from "path";
2422
2499
  import * as os from "os";
2423
2500
  function checkInstallStatus(workspacePath) {
2424
2501
  return {
@@ -2456,7 +2533,7 @@ function checkVSCodeGlobalConfig() {
2456
2533
  }
2457
2534
  }
2458
2535
  function checkVSCodeWorkspaceConfig(workspacePath) {
2459
- const configPath = path15.join(workspacePath, ".vscode", "mcp.json");
2536
+ const configPath = path16.join(workspacePath, ".vscode", "mcp.json");
2460
2537
  if (!fs14.existsSync(configPath)) return false;
2461
2538
  try {
2462
2539
  const content = JSON.parse(fs14.readFileSync(configPath, "utf-8"));
@@ -2484,7 +2561,7 @@ function installToConfig(target, workspacePath) {
2484
2561
  }
2485
2562
  }
2486
2563
  function installToAntigravity() {
2487
- const dir = path15.dirname(ANTIGRAVITY_CONFIG);
2564
+ const dir = path16.dirname(ANTIGRAVITY_CONFIG);
2488
2565
  if (!fs14.existsSync(dir)) {
2489
2566
  fs14.mkdirSync(dir, { recursive: true });
2490
2567
  }
@@ -2508,7 +2585,7 @@ function installToAntigravity() {
2508
2585
  }
2509
2586
  }
2510
2587
  function installToClaude() {
2511
- const dir = path15.dirname(CLAUDE_CONFIG);
2588
+ const dir = path16.dirname(CLAUDE_CONFIG);
2512
2589
  if (!fs14.existsSync(dir)) {
2513
2590
  fs14.mkdirSync(dir, { recursive: true });
2514
2591
  }
@@ -2532,7 +2609,7 @@ function installToClaude() {
2532
2609
  }
2533
2610
  }
2534
2611
  function installToVSCodeGlobal() {
2535
- const dir = path15.dirname(VSCODE_GLOBAL_CONFIG);
2612
+ const dir = path16.dirname(VSCODE_GLOBAL_CONFIG);
2536
2613
  if (!fs14.existsSync(dir)) {
2537
2614
  fs14.mkdirSync(dir, { recursive: true });
2538
2615
  }
@@ -2556,8 +2633,8 @@ function installToVSCodeGlobal() {
2556
2633
  }
2557
2634
  }
2558
2635
  function installToVSCodeWorkspace(workspacePath) {
2559
- const vscodeDir = path15.join(workspacePath, ".vscode");
2560
- const configPath = path15.join(vscodeDir, "mcp.json");
2636
+ const vscodeDir = path16.join(workspacePath, ".vscode");
2637
+ const configPath = path16.join(vscodeDir, "mcp.json");
2561
2638
  if (!fs14.existsSync(vscodeDir)) {
2562
2639
  fs14.mkdirSync(vscodeDir, { recursive: true });
2563
2640
  }
@@ -2598,9 +2675,9 @@ var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG, VSCODE_GLOBAL_CONFIG;
2598
2675
  var init_install = __esm({
2599
2676
  "src/mcp/install.ts"() {
2600
2677
  "use strict";
2601
- ANTIGRAVITY_CONFIG = path15.join(os.homedir(), ".gemini/antigravity/mcp_config.json");
2602
- CLAUDE_CONFIG = path15.join(os.homedir(), ".config/claude/claude_desktop_config.json");
2603
- VSCODE_GLOBAL_CONFIG = path15.join(os.homedir(), ".config/Code/User/settings.json");
2678
+ ANTIGRAVITY_CONFIG = path16.join(os.homedir(), ".gemini/antigravity/mcp_config.json");
2679
+ CLAUDE_CONFIG = path16.join(os.homedir(), ".config/claude/claude_desktop_config.json");
2680
+ VSCODE_GLOBAL_CONFIG = path16.join(os.homedir(), ".config/Code/User/settings.json");
2604
2681
  }
2605
2682
  });
2606
2683
 
@@ -2611,7 +2688,8 @@ async function handleConfigure() {
2611
2688
  const s = spinner();
2612
2689
  s.start("Scanning for projects...");
2613
2690
  const config = loadMCPConfig();
2614
- const projects = scanForProjects();
2691
+ const knownPaths = config.projects.map((p) => p.path).filter((p) => !!p);
2692
+ const projects = scanForProjects({ knownPaths });
2615
2693
  logger.info("Configure: Loaded config", { projects: config.projects, defaultMode: config.defaults.includeNew });
2616
2694
  s.stop("Projects found");
2617
2695
  if (projects.length === 0) {
@@ -2686,7 +2764,7 @@ Hidden projects: ${projects.length - exposedCount}`,
2686
2764
  async function handleConfigureGlobalPath() {
2687
2765
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
2688
2766
  const fs21 = await import("fs");
2689
- const path19 = await import("path");
2767
+ const path20 = await import("path");
2690
2768
  note3(
2691
2769
  `MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
2692
2770
  and coordinate across projects.
@@ -2709,7 +2787,7 @@ locally in each project. MCP needs a central location.`,
2709
2787
  `${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
2710
2788
 
2711
2789
  MCP config will be stored at:
2712
- ${path19.join(resolvedPath, "mcp.yaml")}`,
2790
+ ${path20.join(resolvedPath, "mcp.yaml")}`,
2713
2791
  "Configuration Saved"
2714
2792
  );
2715
2793
  return true;
@@ -4065,14 +4143,14 @@ var init_link_flow = __esm({
4065
4143
  import { confirm as confirm7, spinner as spinner5, note as note10, outro as outro4, cancel as cancel5, isCancel as isCancel9 } from "@clack/prompts";
4066
4144
  import pc12 from "picocolors";
4067
4145
  import * as fs17 from "fs";
4068
- import * as path16 from "path";
4146
+ import * as path17 from "path";
4069
4147
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
4070
4148
  const localPath = getLocalWorkspacePath(workspacePath);
4071
4149
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
4072
- const globalPath = path16.join(customGlobalPath, "workspaces", workspaceName);
4150
+ const globalPath = path17.join(customGlobalPath, "workspaces", workspaceName);
4073
4151
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
4074
4152
  const existingDirs = subdirs.filter(
4075
- (dir) => fs17.existsSync(path16.join(localPath, dir))
4153
+ (dir) => fs17.existsSync(path17.join(localPath, dir))
4076
4154
  );
4077
4155
  if (existingDirs.length === 0) {
4078
4156
  outro4(pc12.yellow("No data found in workspace storage to sync."));
@@ -4098,8 +4176,8 @@ Destination: ${pc12.cyan(globalPath)}`,
4098
4176
  try {
4099
4177
  ensureDir(globalPath);
4100
4178
  for (const dir of existingDirs) {
4101
- const srcDir = path16.join(localPath, dir);
4102
- const destDir = path16.join(globalPath, dir);
4179
+ const srcDir = path17.join(localPath, dir);
4180
+ const destDir = path17.join(globalPath, dir);
4103
4181
  ensureDir(destDir);
4104
4182
  copyDirRecursive(srcDir, destDir);
4105
4183
  }
@@ -4132,7 +4210,7 @@ var init_sync_flow = __esm({
4132
4210
  import { confirm as confirm8, spinner as spinner6, note as note11, outro as outro5, cancel as cancel6, isCancel as isCancel10 } from "@clack/prompts";
4133
4211
  import pc13 from "picocolors";
4134
4212
  import * as fs18 from "fs";
4135
- import * as path17 from "path";
4213
+ import * as path18 from "path";
4136
4214
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
4137
4215
  const s = spinner6();
4138
4216
  s.start("Checking for updates");
@@ -4162,7 +4240,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
4162
4240
  }
4163
4241
  s.start("Updating from package");
4164
4242
  for (const dataPath of dataPaths) {
4165
- copyDirToAllStoragePaths(path17.join(agentCoreDir, "templates"), "templates", [dataPath]);
4243
+ copyDirToAllStoragePaths(path18.join(agentCoreDir, "templates"), "templates", [dataPath]);
4166
4244
  }
4167
4245
  const configFilePath = getConfigPath(workspacePath);
4168
4246
  if (fs18.existsSync(configFilePath)) {
@@ -4195,8 +4273,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
4195
4273
  }
4196
4274
  }
4197
4275
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
4198
- const globalPath = path17.join(customGlobalPath, "workspaces", workspaceName);
4199
- const workspacePath = path17.join(workspaceRoot, ".rrce-workflow");
4276
+ const globalPath = path18.join(customGlobalPath, "workspaces", workspaceName);
4277
+ const workspacePath = path18.join(workspaceRoot, ".rrce-workflow");
4200
4278
  switch (mode) {
4201
4279
  case "global":
4202
4280
  return [globalPath];
@@ -4428,9 +4506,9 @@ init_wizard();
4428
4506
  init_prompts();
4429
4507
  import { intro as intro3, select as select6, note as note14, cancel as cancel9, isCancel as isCancel13, outro as outro8 } from "@clack/prompts";
4430
4508
  import pc16 from "picocolors";
4431
- import * as path18 from "path";
4509
+ import * as path19 from "path";
4432
4510
  async function runSelector() {
4433
- const workspaceName = path18.basename(process.cwd());
4511
+ const workspaceName = path19.basename(process.cwd());
4434
4512
  intro3(pc16.cyan(pc16.inverse(` RRCE-Workflow | ${workspaceName} `)));
4435
4513
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
4436
4514
  if (prompts.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.62",
3
+ "version": "0.2.64",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",