rrce-workflow 0.2.16 → 0.2.18

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 +147 -306
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/commands/wizard/index.ts
2
- import { intro, select as select2, spinner as spinner5, note as note5, outro as outro5, isCancel as isCancel6 } from "@clack/prompts";
3
- import pc6 from "picocolors";
4
- import * as fs11 from "fs";
2
+ import { intro, select as select2, spinner as spinner5, note as note5, outro as outro5, isCancel as isCancel5 } from "@clack/prompts";
3
+ import pc5 from "picocolors";
4
+ import * as fs10 from "fs";
5
5
 
6
6
  // src/lib/git.ts
7
7
  import { execSync } from "child_process";
@@ -247,47 +247,12 @@ function parseWorkspaceConfig(configPath) {
247
247
  return null;
248
248
  }
249
249
  }
250
- function getProjectDisplayLabel(project) {
251
- switch (project.source) {
252
- case "global":
253
- return `global: ~/.rrce-workflow/workspaces/${project.name}`;
254
- case "sibling":
255
- return `sibling: ${project.path}/.rrce-workflow`;
256
- default:
257
- return project.dataPath;
258
- }
259
- }
260
- function getProjectFolders(project) {
261
- const folders = [];
262
- if (project.knowledgePath) {
263
- folders.push({
264
- path: project.knowledgePath,
265
- type: "knowledge",
266
- displayName: `\u{1F4DA} ${project.name} (knowledge)`
267
- });
268
- }
269
- if (project.refsPath) {
270
- folders.push({
271
- path: project.refsPath,
272
- type: "refs",
273
- displayName: `\u{1F4CE} ${project.name} (refs)`
274
- });
275
- }
276
- if (project.tasksPath) {
277
- folders.push({
278
- path: project.tasksPath,
279
- type: "tasks",
280
- displayName: `\u{1F4CB} ${project.name} (tasks)`
281
- });
282
- }
283
- return folders;
284
- }
285
250
 
286
251
  // src/commands/wizard/setup-flow.ts
287
- import { group, select, multiselect, confirm, spinner, note, outro, cancel, isCancel as isCancel2 } from "@clack/prompts";
288
- import pc2 from "picocolors";
289
- import * as fs7 from "fs";
290
- import * as path7 from "path";
252
+ import { group, text, select, multiselect, confirm, spinner, note, outro, cancel, isCancel } from "@clack/prompts";
253
+ import pc from "picocolors";
254
+ import * as fs6 from "fs";
255
+ import * as path6 from "path";
291
256
 
292
257
  // src/lib/prompts.ts
293
258
  import * as fs3 from "fs";
@@ -388,7 +353,6 @@ function copyDirRecursive(src, dest) {
388
353
  // src/commands/wizard/vscode.ts
389
354
  import * as fs5 from "fs";
390
355
  import * as path5 from "path";
391
- var REFERENCE_GROUP_PREFIX = "\u{1F4C1} References";
392
356
  function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
393
357
  const workspaceFilePath = path5.join(workspacePath, `${workspaceName}.code-workspace`);
394
358
  let workspace;
@@ -405,60 +369,47 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
405
369
  if (!workspace.settings) {
406
370
  workspace.settings = {};
407
371
  }
408
- const existingNonReferencesFolders = workspace.folders.filter(
409
- (f) => f.path === "." || !f.name?.includes(REFERENCE_GROUP_PREFIX) && !f.name?.startsWith("\u{1F4DA}") && !f.name?.startsWith("\u{1F4CE}") && !f.name?.startsWith("\u{1F4CB}")
372
+ workspace.folders = workspace.folders.filter(
373
+ (f) => f.path === "." || !f.name?.startsWith("\u{1F4C1}") && !f.name?.startsWith("\u{1F4DA}") && !f.name?.startsWith("\u{1F4CE}") && !f.name?.startsWith("\u{1F4CB}")
410
374
  );
411
- workspace.folders = [];
412
- const mainFolder = {
413
- path: ".",
414
- name: `\u{1F3E0} ${workspaceName} (workspace)`
415
- };
416
- workspace.folders.push(mainFolder);
417
- for (const folder of existingNonReferencesFolders) {
418
- if (folder.path !== ".") {
419
- workspace.folders.push(folder);
420
- }
375
+ const mainFolderIndex = workspace.folders.findIndex((f) => f.path === ".");
376
+ if (mainFolderIndex === -1) {
377
+ workspace.folders.unshift({
378
+ path: ".",
379
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
380
+ });
381
+ } else {
382
+ workspace.folders[mainFolderIndex] = {
383
+ path: ".",
384
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
385
+ };
421
386
  }
422
387
  const referenceFolderPaths = [];
423
388
  const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
424
389
  if (isDetectedProjects) {
425
390
  const projects = linkedProjects;
426
391
  for (const project of projects) {
427
- const folders = getProjectFolders(project);
428
392
  const sourceLabel = project.source === "global" ? "global" : "local";
429
- for (const folder of folders) {
430
- referenceFolderPaths.push(folder.path);
431
- const existingIndex = workspace.folders.findIndex((f) => f.path === folder.path);
432
- if (existingIndex === -1) {
433
- workspace.folders.push({
434
- path: folder.path,
435
- name: `${folder.displayName} [${sourceLabel}]`
436
- });
437
- }
393
+ const folderPath = project.dataPath;
394
+ if (fs5.existsSync(folderPath)) {
395
+ referenceFolderPaths.push(folderPath);
396
+ workspace.folders.push({
397
+ path: folderPath,
398
+ name: `\u{1F4C1} ${project.name} [${sourceLabel}]`
399
+ });
438
400
  }
439
401
  }
440
402
  } else {
441
403
  const projectNames = linkedProjects;
442
404
  const rrceHome = customGlobalPath || getRRCEHome();
443
405
  for (const projectName of projectNames) {
444
- const projectDataPath = path5.join(rrceHome, "workspaces", projectName);
445
- const folderTypes = [
446
- { subpath: "knowledge", icon: "\u{1F4DA}", type: "knowledge" },
447
- { subpath: "refs", icon: "\u{1F4CE}", type: "refs" },
448
- { subpath: "tasks", icon: "\u{1F4CB}", type: "tasks" }
449
- ];
450
- for (const { subpath, icon, type } of folderTypes) {
451
- const folderPath = path5.join(projectDataPath, subpath);
452
- if (fs5.existsSync(folderPath)) {
453
- referenceFolderPaths.push(folderPath);
454
- const existingIndex = workspace.folders.findIndex((f) => f.path === folderPath);
455
- if (existingIndex === -1) {
456
- workspace.folders.push({
457
- path: folderPath,
458
- name: `${icon} ${projectName} (${type}) [global]`
459
- });
460
- }
461
- }
406
+ const folderPath = path5.join(rrceHome, "workspaces", projectName);
407
+ if (fs5.existsSync(folderPath)) {
408
+ referenceFolderPaths.push(folderPath);
409
+ workspace.folders.push({
410
+ path: folderPath,
411
+ name: `\u{1F4C1} ${projectName} [global]`
412
+ });
462
413
  }
463
414
  }
464
415
  }
@@ -468,143 +419,20 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
468
419
  readonlyPatterns[`${folderPath}/**`] = true;
469
420
  }
470
421
  const existingReadonly = workspace.settings["files.readonlyInclude"] || {};
422
+ const cleanedReadonly = {};
423
+ for (const [pattern, value] of Object.entries(existingReadonly)) {
424
+ if (!pattern.includes(".rrce-workflow") && !pattern.includes("rrce-workflow/workspaces")) {
425
+ cleanedReadonly[pattern] = value;
426
+ }
427
+ }
471
428
  workspace.settings["files.readonlyInclude"] = {
472
- ...existingReadonly,
429
+ ...cleanedReadonly,
473
430
  ...readonlyPatterns
474
431
  };
475
432
  }
476
- workspace.settings["explorer.sortOrder"] = workspace.settings["explorer.sortOrder"] || "default";
477
433
  fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
478
434
  }
479
435
 
480
- // src/lib/autocomplete-prompt.ts
481
- import { TextPrompt, isCancel } from "@clack/core";
482
- import * as fs6 from "fs";
483
- import * as path6 from "path";
484
- import pc from "picocolors";
485
- async function directoryAutocomplete(opts) {
486
- let completions = [];
487
- let completionIndex = 0;
488
- let lastTabValue = "";
489
- const prompt = new TextPrompt({
490
- initialValue: opts.initialValue,
491
- validate: opts.validate,
492
- render() {
493
- const title = `${pc.cyan("\u25C6")} ${opts.message}`;
494
- const hintText = opts.hint ? pc.dim(` (${opts.hint})`) : "";
495
- let inputLine;
496
- if (this.state === "error") {
497
- inputLine = `${pc.yellow("\u25B2")} ${this.valueWithCursor}`;
498
- } else if (this.state === "submit") {
499
- inputLine = `${pc.green("\u2713")} ${pc.dim(String(this.value || ""))}`;
500
- } else {
501
- inputLine = `${pc.cyan("\u2502")} ${this.valueWithCursor || pc.dim(opts.placeholder || "")}`;
502
- }
503
- let result2 = `${title}${hintText}
504
- ${inputLine}`;
505
- if (this.state === "error" && this.error) {
506
- result2 += `
507
- ${pc.yellow("\u2502")} ${pc.yellow(this.error)}`;
508
- }
509
- if (completions.length > 1 && this.state === "active") {
510
- const remaining = completions.length - 1;
511
- result2 += `
512
- ${pc.dim("\u2502")} ${pc.dim(`+${remaining} more, press Tab again to cycle`)}`;
513
- }
514
- return result2;
515
- }
516
- });
517
- prompt.on("key", (key) => {
518
- if (key === " " || key === "tab") {
519
- handleTabCompletion(prompt);
520
- } else {
521
- completions = [];
522
- completionIndex = 0;
523
- lastTabValue = "";
524
- }
525
- });
526
- function handleTabCompletion(p) {
527
- const input = String(p.value || "");
528
- const expanded = input.startsWith("~") ? input.replace(/^~/, process.env.HOME || "") : input;
529
- if (lastTabValue === input && completions.length > 1) {
530
- completionIndex = (completionIndex + 1) % completions.length;
531
- const completion = completions[completionIndex] || "";
532
- setPromptValue(p, completion);
533
- return;
534
- }
535
- completions = getDirectoryCompletions(expanded);
536
- completionIndex = 0;
537
- lastTabValue = input;
538
- if (completions.length === 1) {
539
- const completion = completions[0] || "";
540
- setPromptValue(p, completion.endsWith("/") ? completion : completion + "/");
541
- completions = [];
542
- lastTabValue = "";
543
- } else if (completions.length > 1) {
544
- const commonPrefix = getCommonPrefix(completions);
545
- if (commonPrefix.length > expanded.length) {
546
- setPromptValue(p, commonPrefix);
547
- lastTabValue = formatForDisplay(commonPrefix);
548
- } else {
549
- setPromptValue(p, completions[0] || "");
550
- }
551
- }
552
- }
553
- function setPromptValue(p, value2) {
554
- const displayValue = formatForDisplay(value2);
555
- p.value = displayValue;
556
- }
557
- function formatForDisplay(value2) {
558
- const home = process.env.HOME || "";
559
- return value2.startsWith(home) ? value2.replace(home, "~") : value2;
560
- }
561
- function getDirectoryCompletions(inputPath) {
562
- try {
563
- let dirToScan;
564
- let prefix;
565
- if (inputPath === "" || inputPath === "/") {
566
- dirToScan = inputPath || "/";
567
- prefix = "";
568
- } else if (inputPath.endsWith("/")) {
569
- dirToScan = inputPath;
570
- prefix = "";
571
- } else {
572
- dirToScan = path6.dirname(inputPath);
573
- prefix = path6.basename(inputPath).toLowerCase();
574
- }
575
- if (!fs6.existsSync(dirToScan)) {
576
- return [];
577
- }
578
- const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
579
- if (!entry.isDirectory()) return false;
580
- if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
581
- return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
582
- }).map((entry) => path6.join(dirToScan, entry.name)).sort();
583
- return entries;
584
- } catch {
585
- return [];
586
- }
587
- }
588
- function getCommonPrefix(strings) {
589
- if (strings.length === 0) return "";
590
- if (strings.length === 1) return strings[0] || "";
591
- let prefix = strings[0] || "";
592
- for (let i = 1; i < strings.length; i++) {
593
- const str = strings[i] || "";
594
- while (prefix.length > 0 && !str.startsWith(prefix)) {
595
- prefix = prefix.slice(0, -1);
596
- }
597
- }
598
- return prefix;
599
- }
600
- const result = await prompt.prompt();
601
- if (isCancel(result)) {
602
- return result;
603
- }
604
- const value = String(result || "");
605
- return value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
606
- }
607
-
608
436
  // src/commands/wizard/setup-flow.ts
609
437
  async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
610
438
  const s = spinner();
@@ -634,9 +462,12 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
634
462
  return multiselect({
635
463
  message: "Link knowledge from other projects?",
636
464
  options: existingProjects.map((project) => ({
637
- value: project.name,
638
- label: project.name,
639
- hint: pc2.dim(getProjectDisplayLabel(project))
465
+ value: `${project.name}:${project.source}`,
466
+ // Unique key
467
+ label: `${project.name} ${pc.dim(`(${project.source})`)}`,
468
+ hint: pc.dim(
469
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
470
+ )
640
471
  })),
641
472
  required: false
642
473
  });
@@ -684,7 +515,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
684
515
  `Storage: ${config.storageMode === "both" ? "global + workspace" : config.storageMode}`
685
516
  ];
686
517
  if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
687
- summary.push(`Global path: ${pc2.cyan(customGlobalPath)}`);
518
+ summary.push(`Global path: ${pc.cyan(customGlobalPath)}`);
688
519
  }
689
520
  if (dataPaths.length > 0) {
690
521
  summary.push(`Data paths:`);
@@ -697,13 +528,13 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
697
528
  const linkedProjects = config.linkedProjects;
698
529
  if (linkedProjects.length > 0) {
699
530
  summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
700
- summary.push(`Workspace file: ${pc2.cyan(`${workspaceName}.code-workspace`)}`);
531
+ summary.push(`Workspace file: ${pc.cyan(`${workspaceName}.code-workspace`)}`);
701
532
  }
702
533
  note(summary.join("\n"), "Setup Summary");
703
534
  if (linkedProjects.length > 0) {
704
- outro(pc2.green(`\u2713 Setup complete! Open ${pc2.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
535
+ outro(pc.green(`\u2713 Setup complete! Open ${pc.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
705
536
  } else {
706
- outro(pc2.green(`\u2713 Setup complete! Your agents are ready to use.`));
537
+ outro(pc.green(`\u2713 Setup complete! Your agents are ready to use.`));
707
538
  }
708
539
  } catch (error) {
709
540
  s.stop("Error occurred");
@@ -718,7 +549,7 @@ async function resolveGlobalPath() {
718
549
  options.push({
719
550
  value: "default",
720
551
  label: `Default (${defaultPath})`,
721
- hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
552
+ hint: isDefaultWritable ? pc.green("\u2713 writable") : pc.red("\u2717 not writable")
722
553
  });
723
554
  options.push({
724
555
  value: "custom",
@@ -730,14 +561,14 @@ async function resolveGlobalPath() {
730
561
  options,
731
562
  initialValue: isDefaultWritable ? "default" : "custom"
732
563
  });
733
- if (isCancel2(choice)) {
564
+ if (isCancel(choice)) {
734
565
  return void 0;
735
566
  }
736
567
  if (choice === "default") {
737
568
  if (!isDefaultWritable) {
738
569
  note(
739
- `${pc2.yellow("\u26A0")} Cannot write to default path:
740
- ${pc2.dim(defaultPath)}
570
+ `${pc.yellow("\u26A0")} Cannot write to default path:
571
+ ${pc.dim(defaultPath)}
741
572
 
742
573
  This can happen when running via npx/bunx in restricted environments.
743
574
  Please choose a custom path instead.`,
@@ -747,18 +578,18 @@ Please choose a custom path instead.`,
747
578
  }
748
579
  return defaultPath;
749
580
  }
750
- const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
751
- const customPath = await directoryAutocomplete({
581
+ const suggestedPath = path6.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
582
+ const customPath = await text({
752
583
  message: "Enter custom global path:",
753
- initialValue: suggestedPath,
754
- hint: "Tab to autocomplete",
584
+ defaultValue: suggestedPath,
585
+ placeholder: suggestedPath,
755
586
  validate: (value) => {
756
587
  if (!value.trim()) {
757
588
  return "Path cannot be empty";
758
589
  }
759
- const expandedPath = value.startsWith("~") ? value.replace("~", process.env.HOME || "") : value;
760
- if (!checkWriteAccess(expandedPath)) {
761
- return `Cannot write to ${expandedPath}. Please choose a writable path.`;
590
+ const expandedPath2 = value.startsWith("~") ? value.replace("~", process.env.HOME || "") : value;
591
+ if (!checkWriteAccess(expandedPath2)) {
592
+ return `Cannot write to ${expandedPath2}. Please choose a writable path.`;
762
593
  }
763
594
  return void 0;
764
595
  }
@@ -766,20 +597,24 @@ Please choose a custom path instead.`,
766
597
  if (isCancel(customPath)) {
767
598
  return void 0;
768
599
  }
769
- return customPath;
600
+ let expandedPath = customPath.startsWith("~") ? customPath.replace("~", process.env.HOME || "") : customPath;
601
+ if (!expandedPath.endsWith(".rrce-workflow")) {
602
+ expandedPath = path6.join(expandedPath, ".rrce-workflow");
603
+ }
604
+ return expandedPath;
770
605
  }
771
606
  async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
772
607
  const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
773
608
  for (const dataPath of dataPaths) {
774
609
  ensureDir(dataPath);
775
- ensureDir(path7.join(dataPath, "knowledge"));
776
- ensureDir(path7.join(dataPath, "refs"));
777
- ensureDir(path7.join(dataPath, "tasks"));
778
- ensureDir(path7.join(dataPath, "templates"));
610
+ ensureDir(path6.join(dataPath, "knowledge"));
611
+ ensureDir(path6.join(dataPath, "refs"));
612
+ ensureDir(path6.join(dataPath, "tasks"));
613
+ ensureDir(path6.join(dataPath, "templates"));
779
614
  }
780
615
  const agentCoreDir = getAgentCoreDir();
781
616
  syncMetadataToAll(agentCoreDir, dataPaths);
782
- copyDirToAllStoragePaths(path7.join(agentCoreDir, "templates"), "templates", dataPaths);
617
+ copyDirToAllStoragePaths(path6.join(agentCoreDir, "templates"), "templates", dataPaths);
783
618
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
784
619
  if (config.tools.includes("copilot")) {
785
620
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
@@ -791,8 +626,8 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
791
626
  ensureDir(antigravityPath);
792
627
  copyPromptsToDir(prompts, antigravityPath, ".md");
793
628
  }
794
- const workspaceConfigPath = path7.join(workspacePath, ".rrce-workflow", "config.yaml");
795
- ensureDir(path7.dirname(workspaceConfigPath));
629
+ const workspaceConfigPath = path6.join(workspacePath, ".rrce-workflow", "config.yaml");
630
+ ensureDir(path6.dirname(workspaceConfigPath));
796
631
  let configContent = `# RRCE-Workflow Configuration
797
632
  version: 1
798
633
 
@@ -820,15 +655,17 @@ linked_projects:
820
655
  `;
821
656
  });
822
657
  }
823
- fs7.writeFileSync(workspaceConfigPath, configContent);
658
+ fs6.writeFileSync(workspaceConfigPath, configContent);
824
659
  if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
825
- const selectedProjects = allProjects.filter((p) => config.linkedProjects.includes(p.name));
660
+ const selectedProjects = allProjects.filter(
661
+ (p) => config.linkedProjects.includes(`${p.name}:${p.source}`)
662
+ );
826
663
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, config.globalPath);
827
664
  }
828
665
  }
829
666
  function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
830
- const globalPath = path7.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
831
- const workspacePath = path7.join(workspaceRoot, ".rrce-workflow");
667
+ const globalPath = path6.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
668
+ const workspacePath = path6.join(workspaceRoot, ".rrce-workflow");
832
669
  switch (mode) {
833
670
  case "global":
834
671
  return [globalPath];
@@ -842,9 +679,9 @@ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
842
679
  }
843
680
 
844
681
  // src/commands/wizard/link-flow.ts
845
- import { multiselect as multiselect2, spinner as spinner2, note as note2, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
846
- import pc3 from "picocolors";
847
- import * as fs8 from "fs";
682
+ import { multiselect as multiselect2, spinner as spinner2, note as note2, outro as outro2, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
683
+ import pc2 from "picocolors";
684
+ import * as fs7 from "fs";
848
685
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
849
686
  const projects = scanForProjects({
850
687
  excludeWorkspace: workspaceName,
@@ -852,33 +689,38 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
852
689
  scanSiblings: true
853
690
  });
854
691
  if (projects.length === 0) {
855
- outro2(pc3.yellow("No other projects found. Try setting up another project first."));
692
+ outro2(pc2.yellow("No other projects found. Try setting up another project first."));
856
693
  return;
857
694
  }
858
695
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
859
696
  const linkedProjects = await multiselect2({
860
697
  message: "Select projects to link:",
861
698
  options: projects.map((project) => ({
862
- value: project.name,
863
- label: project.name,
864
- hint: pc3.dim(getProjectDisplayLabel(project))
699
+ value: `${project.name}:${project.source}`,
700
+ // Unique key
701
+ label: `${project.name} ${pc2.dim(`(${project.source})`)}`,
702
+ hint: pc2.dim(
703
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
704
+ )
865
705
  })),
866
706
  required: true
867
707
  });
868
- if (isCancel3(linkedProjects)) {
708
+ if (isCancel2(linkedProjects)) {
869
709
  cancel2("Cancelled.");
870
710
  process.exit(0);
871
711
  }
872
- const selectedProjectNames = linkedProjects;
873
- if (selectedProjectNames.length === 0) {
712
+ const selectedKeys = linkedProjects;
713
+ if (selectedKeys.length === 0) {
874
714
  outro2("No projects selected.");
875
715
  return;
876
716
  }
877
- const selectedProjects = projects.filter((p) => selectedProjectNames.includes(p.name));
717
+ const selectedProjects = projects.filter(
718
+ (p) => selectedKeys.includes(`${p.name}:${p.source}`)
719
+ );
878
720
  const s = spinner2();
879
721
  s.start("Linking projects");
880
722
  const configFilePath = getConfigPath(workspacePath);
881
- let configContent = fs8.readFileSync(configFilePath, "utf-8");
723
+ let configContent = fs7.readFileSync(configFilePath, "utf-8");
882
724
  if (configContent.includes("linked_projects:")) {
883
725
  const lines = configContent.split("\n");
884
726
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -887,9 +729,10 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
887
729
  while (insertIndex < lines.length && lines[insertIndex]?.startsWith(" - ")) {
888
730
  insertIndex++;
889
731
  }
890
- for (const name of selectedProjectNames) {
891
- if (!configContent.includes(` - ${name}`)) {
892
- lines.splice(insertIndex, 0, ` - ${name}`);
732
+ for (const project of selectedProjects) {
733
+ const entry = ` - ${project.name}:${project.source}`;
734
+ if (!configContent.includes(entry)) {
735
+ lines.splice(insertIndex, 0, entry);
893
736
  insertIndex++;
894
737
  }
895
738
  }
@@ -899,56 +742,54 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
899
742
  configContent += `
900
743
  linked_projects:
901
744
  `;
902
- selectedProjectNames.forEach((name) => {
903
- configContent += ` - ${name}
745
+ selectedProjects.forEach((project) => {
746
+ configContent += ` - ${project.name}:${project.source}
904
747
  `;
905
748
  });
906
749
  }
907
- fs8.writeFileSync(configFilePath, configContent);
750
+ fs7.writeFileSync(configFilePath, configContent);
908
751
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
909
752
  s.stop("Projects linked");
910
753
  const workspaceFile = `${workspaceName}.code-workspace`;
911
754
  const summary = [
912
755
  `Linked projects:`,
913
- ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc3.dim(`(${p.source})`)}`),
914
- ``,
915
- `Workspace file: ${pc3.cyan(workspaceFile)}`,
756
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc2.dim(`(${p.source})`)}`),
916
757
  ``,
917
- pc3.dim("Includes: knowledge, refs, tasks folders")
758
+ `Workspace file: ${pc2.cyan(workspaceFile)}`
918
759
  ];
919
760
  note2(summary.join("\n"), "Link Summary");
920
- outro2(pc3.green(`\u2713 Projects linked! Open ${pc3.bold(workspaceFile)} in VSCode to access linked knowledge.`));
761
+ outro2(pc2.green(`\u2713 Projects linked! Open ${pc2.bold(workspaceFile)} in VSCode to access linked data.`));
921
762
  }
922
763
 
923
764
  // src/commands/wizard/sync-flow.ts
924
- import { confirm as confirm2, spinner as spinner3, note as note3, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
925
- import pc4 from "picocolors";
926
- import * as fs9 from "fs";
927
- import * as path8 from "path";
765
+ import { confirm as confirm2, spinner as spinner3, note as note3, outro as outro3, cancel as cancel3, isCancel as isCancel3 } from "@clack/prompts";
766
+ import pc3 from "picocolors";
767
+ import * as fs8 from "fs";
768
+ import * as path7 from "path";
928
769
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
929
770
  const localPath = getLocalWorkspacePath(workspacePath);
930
771
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
931
- const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
772
+ const globalPath = path7.join(customGlobalPath, "workspaces", workspaceName);
932
773
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
933
774
  const existingDirs = subdirs.filter(
934
- (dir) => fs9.existsSync(path8.join(localPath, dir))
775
+ (dir) => fs8.existsSync(path7.join(localPath, dir))
935
776
  );
936
777
  if (existingDirs.length === 0) {
937
- outro3(pc4.yellow("No data found in workspace storage to sync."));
778
+ outro3(pc3.yellow("No data found in workspace storage to sync."));
938
779
  return;
939
780
  }
940
781
  note3(
941
782
  `The following will be copied to global storage:
942
783
  ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
943
784
 
944
- Destination: ${pc4.cyan(globalPath)}`,
785
+ Destination: ${pc3.cyan(globalPath)}`,
945
786
  "Sync Preview"
946
787
  );
947
788
  const shouldSync = await confirm2({
948
789
  message: "Proceed with sync to global storage?",
949
790
  initialValue: true
950
791
  });
951
- if (isCancel4(shouldSync) || !shouldSync) {
792
+ if (isCancel3(shouldSync) || !shouldSync) {
952
793
  outro3("Sync cancelled.");
953
794
  return;
954
795
  }
@@ -957,27 +798,27 @@ Destination: ${pc4.cyan(globalPath)}`,
957
798
  try {
958
799
  ensureDir(globalPath);
959
800
  for (const dir of existingDirs) {
960
- const srcDir = path8.join(localPath, dir);
961
- const destDir = path8.join(globalPath, dir);
801
+ const srcDir = path7.join(localPath, dir);
802
+ const destDir = path7.join(globalPath, dir);
962
803
  ensureDir(destDir);
963
804
  copyDirRecursive(srcDir, destDir);
964
805
  }
965
806
  const configFilePath = getConfigPath(workspacePath);
966
- let configContent = fs9.readFileSync(configFilePath, "utf-8");
807
+ let configContent = fs8.readFileSync(configFilePath, "utf-8");
967
808
  configContent = configContent.replace(/mode:\s*workspace/, "mode: both");
968
- fs9.writeFileSync(configFilePath, configContent);
809
+ fs8.writeFileSync(configFilePath, configContent);
969
810
  s.stop("Sync complete");
970
811
  const summary = [
971
812
  `Synced directories:`,
972
813
  ...existingDirs.map((d) => ` \u2713 ${d}/`),
973
814
  ``,
974
- `Global path: ${pc4.cyan(globalPath)}`,
975
- `Storage mode updated to: ${pc4.bold("both")}`,
815
+ `Global path: ${pc3.cyan(globalPath)}`,
816
+ `Storage mode updated to: ${pc3.bold("both")}`,
976
817
  ``,
977
818
  `Other projects can now link this knowledge!`
978
819
  ];
979
820
  note3(summary.join("\n"), "Sync Summary");
980
- outro3(pc4.green("\u2713 Workspace knowledge synced to global storage!"));
821
+ outro3(pc3.green("\u2713 Workspace knowledge synced to global storage!"));
981
822
  } catch (error) {
982
823
  s.stop("Error occurred");
983
824
  cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
@@ -986,10 +827,10 @@ Destination: ${pc4.cyan(globalPath)}`,
986
827
  }
987
828
 
988
829
  // src/commands/wizard/update-flow.ts
989
- import { confirm as confirm3, spinner as spinner4, note as note4, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
990
- import pc5 from "picocolors";
991
- import * as fs10 from "fs";
992
- import * as path9 from "path";
830
+ import { confirm as confirm3, spinner as spinner4, note as note4, outro as outro4, cancel as cancel4, isCancel as isCancel4 } from "@clack/prompts";
831
+ import pc4 from "picocolors";
832
+ import * as fs9 from "fs";
833
+ import * as path8 from "path";
993
834
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
994
835
  const s = spinner4();
995
836
  s.start("Checking for updates");
@@ -1013,16 +854,16 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1013
854
  message: "Proceed with update?",
1014
855
  initialValue: true
1015
856
  });
1016
- if (isCancel5(shouldUpdate) || !shouldUpdate) {
857
+ if (isCancel4(shouldUpdate) || !shouldUpdate) {
1017
858
  outro4("Update cancelled.");
1018
859
  return;
1019
860
  }
1020
861
  s.start("Updating from package");
1021
862
  for (const dataPath of dataPaths) {
1022
- copyDirToAllStoragePaths(path9.join(agentCoreDir, "templates"), "templates", [dataPath]);
863
+ copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", [dataPath]);
1023
864
  }
1024
865
  const configFilePath = getConfigPath(workspacePath);
1025
- const configContent = fs10.readFileSync(configFilePath, "utf-8");
866
+ const configContent = fs9.readFileSync(configFilePath, "utf-8");
1026
867
  if (configContent.includes("copilot: true")) {
1027
868
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
1028
869
  ensureDir(copilotPath);
@@ -1042,7 +883,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1042
883
  `Your configuration and knowledge files were preserved.`
1043
884
  ];
1044
885
  note4(summary.join("\n"), "Update Summary");
1045
- outro4(pc5.green("\u2713 Successfully updated from package!"));
886
+ outro4(pc4.green("\u2713 Successfully updated from package!"));
1046
887
  } catch (error) {
1047
888
  s.stop("Error occurred");
1048
889
  cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
@@ -1050,8 +891,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1050
891
  }
1051
892
  }
1052
893
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
1053
- const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
1054
- const workspacePath = path9.join(workspaceRoot, ".rrce-workflow");
894
+ const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
895
+ const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
1055
896
  switch (mode) {
1056
897
  case "global":
1057
898
  return [globalPath];
@@ -1066,7 +907,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
1066
907
 
1067
908
  // src/commands/wizard/index.ts
1068
909
  async function runWizard() {
1069
- intro(pc6.cyan(pc6.inverse(" RRCE-Workflow Setup ")));
910
+ intro(pc5.cyan(pc5.inverse(" RRCE-Workflow Setup ")));
1070
911
  const s = spinner5();
1071
912
  s.start("Detecting environment");
1072
913
  const workspacePath = detectWorkspaceRoot();
@@ -1075,8 +916,8 @@ async function runWizard() {
1075
916
  await new Promise((r) => setTimeout(r, 800));
1076
917
  s.stop("Environment detected");
1077
918
  note5(
1078
- `Git User: ${pc6.bold(gitUser || "(not found)")}
1079
- Workspace: ${pc6.bold(workspaceName)}`,
919
+ `Git User: ${pc5.bold(gitUser || "(not found)")}
920
+ Workspace: ${pc5.bold(workspaceName)}`,
1080
921
  "Context"
1081
922
  );
1082
923
  const detectedProjects = scanForProjects({
@@ -1085,18 +926,18 @@ Workspace: ${pc6.bold(workspaceName)}`,
1085
926
  scanSiblings: true
1086
927
  });
1087
928
  const configFilePath = getConfigPath(workspacePath);
1088
- const isAlreadyConfigured = fs11.existsSync(configFilePath);
929
+ const isAlreadyConfigured = fs10.existsSync(configFilePath);
1089
930
  let currentStorageMode = null;
1090
931
  if (isAlreadyConfigured) {
1091
932
  try {
1092
- const configContent = fs11.readFileSync(configFilePath, "utf-8");
933
+ const configContent = fs10.readFileSync(configFilePath, "utf-8");
1093
934
  const modeMatch = configContent.match(/mode:\s*(global|workspace|both)/);
1094
935
  currentStorageMode = modeMatch?.[1] ?? null;
1095
936
  } catch {
1096
937
  }
1097
938
  }
1098
939
  const localDataPath = getLocalWorkspacePath(workspacePath);
1099
- const hasLocalData = fs11.existsSync(localDataPath);
940
+ const hasLocalData = fs10.existsSync(localDataPath);
1100
941
  if (isAlreadyConfigured) {
1101
942
  const menuOptions = [];
1102
943
  if (detectedProjects.length > 0) {
@@ -1119,7 +960,7 @@ Workspace: ${pc6.bold(workspaceName)}`,
1119
960
  message: "This workspace is already configured. What would you like to do?",
1120
961
  options: menuOptions
1121
962
  });
1122
- if (isCancel6(action) || action === "exit") {
963
+ if (isCancel5(action) || action === "exit") {
1123
964
  outro5("Exited.");
1124
965
  process.exit(0);
1125
966
  }
@@ -1140,12 +981,12 @@ Workspace: ${pc6.bold(workspaceName)}`,
1140
981
  }
1141
982
 
1142
983
  // src/commands/selector.ts
1143
- import { intro as intro2, select as select3, note as note6, cancel as cancel6, isCancel as isCancel7, outro as outro6 } from "@clack/prompts";
1144
- import pc7 from "picocolors";
1145
- import * as path10 from "path";
984
+ import { intro as intro2, select as select3, note as note6, cancel as cancel6, isCancel as isCancel6, outro as outro6 } from "@clack/prompts";
985
+ import pc6 from "picocolors";
986
+ import * as path9 from "path";
1146
987
  async function runSelector() {
1147
- const workspaceName = path10.basename(process.cwd());
1148
- intro2(pc7.cyan(pc7.inverse(` RRCE-Workflow | ${workspaceName} `)));
988
+ const workspaceName = path9.basename(process.cwd());
989
+ intro2(pc6.cyan(pc6.inverse(` RRCE-Workflow | ${workspaceName} `)));
1149
990
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1150
991
  if (prompts.length === 0) {
1151
992
  cancel6("No agents found. Run `rrce-workflow` to set up.");
@@ -1159,14 +1000,14 @@ async function runSelector() {
1159
1000
  hint: p.frontmatter.description
1160
1001
  }))
1161
1002
  });
1162
- if (isCancel7(selection)) {
1003
+ if (isCancel6(selection)) {
1163
1004
  cancel6("Selection cancelled.");
1164
1005
  process.exit(0);
1165
1006
  }
1166
1007
  const prompt = selection;
1167
1008
  note6(
1168
1009
  `Use this agent in your IDE by invoking:
1169
- ${pc7.bold(pc7.cyan(`@${prompt.frontmatter.name}`))}`,
1010
+ ${pc6.bold(pc6.cyan(`@${prompt.frontmatter.name}`))}`,
1170
1011
  "Agent Selected"
1171
1012
  );
1172
1013
  outro6("Done");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",