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.
- package/dist/index.js +147 -306
- 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
|
|
3
|
-
import
|
|
4
|
-
import * as
|
|
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
|
|
288
|
-
import
|
|
289
|
-
import * as
|
|
290
|
-
import * as
|
|
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
|
-
|
|
409
|
-
(f) => f.path === "." || !f.name?.
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
{
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
639
|
-
|
|
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: ${
|
|
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: ${
|
|
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(
|
|
535
|
+
outro(pc.green(`\u2713 Setup complete! Open ${pc.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
|
|
705
536
|
} else {
|
|
706
|
-
outro(
|
|
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 ?
|
|
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 (
|
|
564
|
+
if (isCancel(choice)) {
|
|
734
565
|
return void 0;
|
|
735
566
|
}
|
|
736
567
|
if (choice === "default") {
|
|
737
568
|
if (!isDefaultWritable) {
|
|
738
569
|
note(
|
|
739
|
-
`${
|
|
740
|
-
${
|
|
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 =
|
|
751
|
-
const customPath = await
|
|
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
|
-
|
|
754
|
-
|
|
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
|
|
760
|
-
if (!checkWriteAccess(
|
|
761
|
-
return `Cannot write to ${
|
|
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
|
-
|
|
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(
|
|
776
|
-
ensureDir(
|
|
777
|
-
ensureDir(
|
|
778
|
-
ensureDir(
|
|
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(
|
|
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 =
|
|
795
|
-
ensureDir(
|
|
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
|
-
|
|
658
|
+
fs6.writeFileSync(workspaceConfigPath, configContent);
|
|
824
659
|
if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
|
|
825
|
-
const selectedProjects = allProjects.filter(
|
|
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 =
|
|
831
|
-
const workspacePath =
|
|
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
|
|
846
|
-
import
|
|
847
|
-
import * as
|
|
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(
|
|
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
|
-
|
|
864
|
-
|
|
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 (
|
|
708
|
+
if (isCancel2(linkedProjects)) {
|
|
869
709
|
cancel2("Cancelled.");
|
|
870
710
|
process.exit(0);
|
|
871
711
|
}
|
|
872
|
-
const
|
|
873
|
-
if (
|
|
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(
|
|
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 =
|
|
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
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
|
|
903
|
-
configContent += ` - ${name}
|
|
745
|
+
selectedProjects.forEach((project) => {
|
|
746
|
+
configContent += ` - ${project.name}:${project.source}
|
|
904
747
|
`;
|
|
905
748
|
});
|
|
906
749
|
}
|
|
907
|
-
|
|
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} ${
|
|
914
|
-
``,
|
|
915
|
-
`Workspace file: ${pc3.cyan(workspaceFile)}`,
|
|
756
|
+
...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc2.dim(`(${p.source})`)}`),
|
|
916
757
|
``,
|
|
917
|
-
|
|
758
|
+
`Workspace file: ${pc2.cyan(workspaceFile)}`
|
|
918
759
|
];
|
|
919
760
|
note2(summary.join("\n"), "Link Summary");
|
|
920
|
-
outro2(
|
|
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
|
|
925
|
-
import
|
|
926
|
-
import * as
|
|
927
|
-
import * as
|
|
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 =
|
|
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) =>
|
|
775
|
+
(dir) => fs8.existsSync(path7.join(localPath, dir))
|
|
935
776
|
);
|
|
936
777
|
if (existingDirs.length === 0) {
|
|
937
|
-
outro3(
|
|
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: ${
|
|
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 (
|
|
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 =
|
|
961
|
-
const destDir =
|
|
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 =
|
|
807
|
+
let configContent = fs8.readFileSync(configFilePath, "utf-8");
|
|
967
808
|
configContent = configContent.replace(/mode:\s*workspace/, "mode: both");
|
|
968
|
-
|
|
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: ${
|
|
975
|
-
`Storage mode updated to: ${
|
|
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(
|
|
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
|
|
990
|
-
import
|
|
991
|
-
import * as
|
|
992
|
-
import * as
|
|
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 (
|
|
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(
|
|
863
|
+
copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", [dataPath]);
|
|
1023
864
|
}
|
|
1024
865
|
const configFilePath = getConfigPath(workspacePath);
|
|
1025
|
-
const configContent =
|
|
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(
|
|
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 =
|
|
1054
|
-
const workspacePath =
|
|
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(
|
|
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: ${
|
|
1079
|
-
Workspace: ${
|
|
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 =
|
|
929
|
+
const isAlreadyConfigured = fs10.existsSync(configFilePath);
|
|
1089
930
|
let currentStorageMode = null;
|
|
1090
931
|
if (isAlreadyConfigured) {
|
|
1091
932
|
try {
|
|
1092
|
-
const configContent =
|
|
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 =
|
|
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 (
|
|
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
|
|
1144
|
-
import
|
|
1145
|
-
import * as
|
|
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 =
|
|
1148
|
-
intro2(
|
|
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 (
|
|
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
|
-
${
|
|
1010
|
+
${pc6.bold(pc6.cyan(`@${prompt.frontmatter.name}`))}`,
|
|
1170
1011
|
"Agent Selected"
|
|
1171
1012
|
);
|
|
1172
1013
|
outro6("Done");
|