rrce-workflow 0.2.18 → 0.2.20
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 +310 -134
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/commands/wizard/index.ts
|
|
2
2
|
import { intro, select as select2, spinner as spinner5, note as note5, outro as outro5, isCancel as isCancel5 } from "@clack/prompts";
|
|
3
|
-
import
|
|
4
|
-
import * as
|
|
3
|
+
import pc6 from "picocolors";
|
|
4
|
+
import * as fs11 from "fs";
|
|
5
5
|
|
|
6
6
|
// src/lib/git.ts
|
|
7
7
|
import { execSync } from "child_process";
|
|
@@ -136,24 +136,42 @@ function getEffectiveRRCEHome(workspaceRoot) {
|
|
|
136
136
|
// src/lib/detection.ts
|
|
137
137
|
import * as fs2 from "fs";
|
|
138
138
|
import * as path2 from "path";
|
|
139
|
+
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
140
|
+
"node_modules",
|
|
141
|
+
".git",
|
|
142
|
+
".cache",
|
|
143
|
+
".npm",
|
|
144
|
+
".yarn",
|
|
145
|
+
".pnpm",
|
|
146
|
+
".local",
|
|
147
|
+
".config",
|
|
148
|
+
".vscode",
|
|
149
|
+
".vscode-server",
|
|
150
|
+
"Library",
|
|
151
|
+
"Applications",
|
|
152
|
+
".Trash",
|
|
153
|
+
"snap",
|
|
154
|
+
".cargo",
|
|
155
|
+
".rustup",
|
|
156
|
+
".go",
|
|
157
|
+
".docker"
|
|
158
|
+
]);
|
|
139
159
|
function scanForProjects(options = {}) {
|
|
140
|
-
const { excludeWorkspace, workspacePath
|
|
160
|
+
const { excludeWorkspace, workspacePath } = options;
|
|
141
161
|
const projects = [];
|
|
142
162
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
143
163
|
const globalProjects = scanGlobalStorage(excludeWorkspace);
|
|
144
164
|
for (const project of globalProjects) {
|
|
145
|
-
if (!seenPaths.has(project.
|
|
146
|
-
seenPaths.add(project.
|
|
165
|
+
if (!seenPaths.has(project.dataPath)) {
|
|
166
|
+
seenPaths.add(project.dataPath);
|
|
147
167
|
projects.push(project);
|
|
148
168
|
}
|
|
149
169
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
projects.push(project);
|
|
156
|
-
}
|
|
170
|
+
const homeProjects = scanHomeDirectory(workspacePath);
|
|
171
|
+
for (const project of homeProjects) {
|
|
172
|
+
if (!seenPaths.has(project.dataPath)) {
|
|
173
|
+
seenPaths.add(project.dataPath);
|
|
174
|
+
projects.push(project);
|
|
157
175
|
}
|
|
158
176
|
}
|
|
159
177
|
return projects;
|
|
@@ -189,44 +207,56 @@ function scanGlobalStorage(excludeWorkspace) {
|
|
|
189
207
|
}
|
|
190
208
|
return projects;
|
|
191
209
|
}
|
|
192
|
-
function
|
|
193
|
-
const
|
|
210
|
+
function scanHomeDirectory(excludePath) {
|
|
211
|
+
const home = process.env.HOME;
|
|
212
|
+
if (!home) return [];
|
|
194
213
|
const projects = [];
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
const maxDepth = 5;
|
|
215
|
+
function scanDir(dirPath, depth) {
|
|
216
|
+
if (depth > maxDepth) return;
|
|
217
|
+
try {
|
|
218
|
+
const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
|
|
219
|
+
for (const entry of entries) {
|
|
220
|
+
if (!entry.isDirectory()) continue;
|
|
221
|
+
const fullPath = path2.join(dirPath, entry.name);
|
|
222
|
+
if (excludePath && fullPath === excludePath) continue;
|
|
223
|
+
if (entry.name === ".rrce-workflow") {
|
|
224
|
+
const configPath = path2.join(fullPath, "config.yaml");
|
|
225
|
+
if (fs2.existsSync(configPath)) {
|
|
226
|
+
const projectPath = dirPath;
|
|
227
|
+
const projectName = path2.basename(projectPath);
|
|
228
|
+
const config = parseWorkspaceConfig(configPath);
|
|
229
|
+
const knowledgePath = path2.join(fullPath, "knowledge");
|
|
230
|
+
const refsPath = path2.join(fullPath, "refs");
|
|
231
|
+
const tasksPath = path2.join(fullPath, "tasks");
|
|
232
|
+
projects.push({
|
|
233
|
+
name: config?.name || projectName,
|
|
234
|
+
path: projectPath,
|
|
235
|
+
dataPath: fullPath,
|
|
236
|
+
source: "local",
|
|
237
|
+
storageMode: config?.storageMode,
|
|
238
|
+
knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
|
|
239
|
+
refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
|
|
240
|
+
tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (SKIP_DIRECTORIES.has(entry.name)) continue;
|
|
246
|
+
if (entry.name.startsWith(".") && entry.name !== ".rrce-workflow") continue;
|
|
247
|
+
scanDir(fullPath, depth + 1);
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
220
250
|
}
|
|
221
|
-
} catch {
|
|
222
251
|
}
|
|
252
|
+
scanDir(home, 0);
|
|
223
253
|
return projects;
|
|
224
254
|
}
|
|
225
255
|
function parseWorkspaceConfig(configPath) {
|
|
226
256
|
try {
|
|
227
257
|
const content = fs2.readFileSync(configPath, "utf-8");
|
|
228
258
|
const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
|
|
229
|
-
const modeMatch = content.match(/mode:\s*(global|workspace
|
|
259
|
+
const modeMatch = content.match(/mode:\s*(global|workspace)/);
|
|
230
260
|
const linkedProjects = [];
|
|
231
261
|
const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
|
|
232
262
|
if (linkedMatch && linkedMatch[1]) {
|
|
@@ -249,10 +279,10 @@ function parseWorkspaceConfig(configPath) {
|
|
|
249
279
|
}
|
|
250
280
|
|
|
251
281
|
// src/commands/wizard/setup-flow.ts
|
|
252
|
-
import { group,
|
|
253
|
-
import
|
|
254
|
-
import * as
|
|
255
|
-
import * as
|
|
282
|
+
import { group, select, multiselect, confirm, spinner, note, outro, cancel, isCancel } from "@clack/prompts";
|
|
283
|
+
import pc2 from "picocolors";
|
|
284
|
+
import * as fs7 from "fs";
|
|
285
|
+
import * as path7 from "path";
|
|
256
286
|
|
|
257
287
|
// src/lib/prompts.ts
|
|
258
288
|
import * as fs3 from "fs";
|
|
@@ -433,6 +463,113 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
|
|
|
433
463
|
fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
|
|
434
464
|
}
|
|
435
465
|
|
|
466
|
+
// src/lib/autocomplete-prompt.ts
|
|
467
|
+
import * as fs6 from "fs";
|
|
468
|
+
import * as path6 from "path";
|
|
469
|
+
import * as readline from "readline";
|
|
470
|
+
import pc from "picocolors";
|
|
471
|
+
function directoryPrompt(opts) {
|
|
472
|
+
return new Promise((resolve) => {
|
|
473
|
+
process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
|
|
474
|
+
`);
|
|
475
|
+
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
476
|
+
const rl = readline.createInterface({
|
|
477
|
+
input: process.stdin,
|
|
478
|
+
output: process.stdout,
|
|
479
|
+
completer: completeDirectory,
|
|
480
|
+
terminal: true
|
|
481
|
+
});
|
|
482
|
+
if (opts.defaultValue) {
|
|
483
|
+
rl.write(opts.defaultValue);
|
|
484
|
+
}
|
|
485
|
+
rl.on("line", (input) => {
|
|
486
|
+
const value = input.trim();
|
|
487
|
+
const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
|
|
488
|
+
if (opts.validate) {
|
|
489
|
+
const error = opts.validate(expandedPath);
|
|
490
|
+
if (error) {
|
|
491
|
+
process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
|
|
492
|
+
`);
|
|
493
|
+
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
494
|
+
rl.write(value);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
rl.close();
|
|
499
|
+
process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
|
|
500
|
+
`);
|
|
501
|
+
resolve(expandedPath);
|
|
502
|
+
});
|
|
503
|
+
rl.on("close", () => {
|
|
504
|
+
});
|
|
505
|
+
rl.on("SIGINT", () => {
|
|
506
|
+
rl.close();
|
|
507
|
+
process.stdout.write("\n");
|
|
508
|
+
resolve(/* @__PURE__ */ Symbol("cancel"));
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function completeDirectory(line) {
|
|
513
|
+
const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
|
|
514
|
+
try {
|
|
515
|
+
let dirToScan;
|
|
516
|
+
let prefix;
|
|
517
|
+
let basePath;
|
|
518
|
+
if (expanded === "" || expanded === "/") {
|
|
519
|
+
dirToScan = expanded || "/";
|
|
520
|
+
prefix = "";
|
|
521
|
+
basePath = expanded;
|
|
522
|
+
} else if (expanded.endsWith("/")) {
|
|
523
|
+
dirToScan = expanded;
|
|
524
|
+
prefix = "";
|
|
525
|
+
basePath = expanded;
|
|
526
|
+
} else {
|
|
527
|
+
dirToScan = path6.dirname(expanded);
|
|
528
|
+
prefix = path6.basename(expanded).toLowerCase();
|
|
529
|
+
basePath = dirToScan === "/" ? "/" : dirToScan + "/";
|
|
530
|
+
}
|
|
531
|
+
if (!fs6.existsSync(dirToScan)) {
|
|
532
|
+
return [[], line];
|
|
533
|
+
}
|
|
534
|
+
const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
|
|
535
|
+
if (!entry.isDirectory()) return false;
|
|
536
|
+
if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
|
|
537
|
+
return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
|
|
538
|
+
}).map((entry) => {
|
|
539
|
+
const fullPath = path6.join(dirToScan, entry.name);
|
|
540
|
+
const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
|
|
541
|
+
return displayPath + "/";
|
|
542
|
+
}).sort();
|
|
543
|
+
if (entries.length === 1) {
|
|
544
|
+
return [entries, line];
|
|
545
|
+
}
|
|
546
|
+
if (entries.length > 1) {
|
|
547
|
+
const commonPrefix = getCommonPrefix(entries);
|
|
548
|
+
if (commonPrefix.length > line.length) {
|
|
549
|
+
return [[commonPrefix], line];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return [entries, line];
|
|
553
|
+
} catch {
|
|
554
|
+
return [[], line];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function getCommonPrefix(strings) {
|
|
558
|
+
if (strings.length === 0) return "";
|
|
559
|
+
if (strings.length === 1) return strings[0] || "";
|
|
560
|
+
let prefix = strings[0] || "";
|
|
561
|
+
for (let i = 1; i < strings.length; i++) {
|
|
562
|
+
const str = strings[i] || "";
|
|
563
|
+
while (prefix.length > 0 && !str.startsWith(prefix)) {
|
|
564
|
+
prefix = prefix.slice(0, -1);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return prefix;
|
|
568
|
+
}
|
|
569
|
+
function isCancelled(value) {
|
|
570
|
+
return typeof value === "symbol";
|
|
571
|
+
}
|
|
572
|
+
|
|
436
573
|
// src/commands/wizard/setup-flow.ts
|
|
437
574
|
async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
438
575
|
const s = spinner();
|
|
@@ -441,9 +578,8 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
441
578
|
storageMode: () => select({
|
|
442
579
|
message: "Where should workflow data be stored?",
|
|
443
580
|
options: [
|
|
444
|
-
{ value: "global", label: "Global (~/.rrce-workflow/)" },
|
|
445
|
-
{ value: "workspace", label: "Workspace (.rrce-workflow/)" }
|
|
446
|
-
{ value: "both", label: "Both" }
|
|
581
|
+
{ value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
|
|
582
|
+
{ value: "workspace", label: "Workspace (.rrce-workflow/)", hint: "Self-contained, version with repo" }
|
|
447
583
|
],
|
|
448
584
|
initialValue: "global"
|
|
449
585
|
}),
|
|
@@ -464,14 +600,18 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
464
600
|
options: existingProjects.map((project) => ({
|
|
465
601
|
value: `${project.name}:${project.source}`,
|
|
466
602
|
// Unique key
|
|
467
|
-
label: `${project.name} ${
|
|
468
|
-
hint:
|
|
603
|
+
label: `${project.name} ${pc2.dim(`(${project.source})`)}`,
|
|
604
|
+
hint: pc2.dim(
|
|
469
605
|
project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
|
|
470
606
|
)
|
|
471
607
|
})),
|
|
472
608
|
required: false
|
|
473
609
|
});
|
|
474
610
|
},
|
|
611
|
+
addToGitignore: () => confirm({
|
|
612
|
+
message: "Add generated folders to .gitignore?",
|
|
613
|
+
initialValue: true
|
|
614
|
+
}),
|
|
475
615
|
confirm: () => confirm({
|
|
476
616
|
message: "Create configuration?",
|
|
477
617
|
initialValue: true
|
|
@@ -489,7 +629,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
489
629
|
process.exit(0);
|
|
490
630
|
}
|
|
491
631
|
let customGlobalPath;
|
|
492
|
-
if (config.storageMode === "global"
|
|
632
|
+
if (config.storageMode === "global") {
|
|
493
633
|
customGlobalPath = await resolveGlobalPath();
|
|
494
634
|
if (!customGlobalPath) {
|
|
495
635
|
cancel("Setup cancelled - no writable global path available.");
|
|
@@ -502,7 +642,8 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
502
642
|
storageMode: config.storageMode,
|
|
503
643
|
globalPath: customGlobalPath,
|
|
504
644
|
tools: config.tools,
|
|
505
|
-
linkedProjects: config.linkedProjects
|
|
645
|
+
linkedProjects: config.linkedProjects,
|
|
646
|
+
addToGitignore: config.addToGitignore
|
|
506
647
|
}, workspacePath, workspaceName, existingProjects);
|
|
507
648
|
s.stop("Configuration generated");
|
|
508
649
|
const dataPaths = getDataPaths(
|
|
@@ -512,10 +653,10 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
512
653
|
customGlobalPath
|
|
513
654
|
);
|
|
514
655
|
const summary = [
|
|
515
|
-
`Storage: ${config.storageMode
|
|
656
|
+
`Storage: ${config.storageMode}`
|
|
516
657
|
];
|
|
517
658
|
if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
|
|
518
|
-
summary.push(`Global path: ${
|
|
659
|
+
summary.push(`Global path: ${pc2.cyan(customGlobalPath)}`);
|
|
519
660
|
}
|
|
520
661
|
if (dataPaths.length > 0) {
|
|
521
662
|
summary.push(`Data paths:`);
|
|
@@ -528,13 +669,13 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
528
669
|
const linkedProjects = config.linkedProjects;
|
|
529
670
|
if (linkedProjects.length > 0) {
|
|
530
671
|
summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
|
|
531
|
-
summary.push(`Workspace file: ${
|
|
672
|
+
summary.push(`Workspace file: ${pc2.cyan(`${workspaceName}.code-workspace`)}`);
|
|
532
673
|
}
|
|
533
674
|
note(summary.join("\n"), "Setup Summary");
|
|
534
675
|
if (linkedProjects.length > 0) {
|
|
535
|
-
outro(
|
|
676
|
+
outro(pc2.green(`\u2713 Setup complete! Open ${pc2.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
|
|
536
677
|
} else {
|
|
537
|
-
outro(
|
|
678
|
+
outro(pc2.green(`\u2713 Setup complete! Your agents are ready to use.`));
|
|
538
679
|
}
|
|
539
680
|
} catch (error) {
|
|
540
681
|
s.stop("Error occurred");
|
|
@@ -549,7 +690,7 @@ async function resolveGlobalPath() {
|
|
|
549
690
|
options.push({
|
|
550
691
|
value: "default",
|
|
551
692
|
label: `Default (${defaultPath})`,
|
|
552
|
-
hint: isDefaultWritable ?
|
|
693
|
+
hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
|
|
553
694
|
});
|
|
554
695
|
options.push({
|
|
555
696
|
value: "custom",
|
|
@@ -567,8 +708,8 @@ async function resolveGlobalPath() {
|
|
|
567
708
|
if (choice === "default") {
|
|
568
709
|
if (!isDefaultWritable) {
|
|
569
710
|
note(
|
|
570
|
-
`${
|
|
571
|
-
${
|
|
711
|
+
`${pc2.yellow("\u26A0")} Cannot write to default path:
|
|
712
|
+
${pc2.dim(defaultPath)}
|
|
572
713
|
|
|
573
714
|
This can happen when running via npx/bunx in restricted environments.
|
|
574
715
|
Please choose a custom path instead.`,
|
|
@@ -578,28 +719,26 @@ Please choose a custom path instead.`,
|
|
|
578
719
|
}
|
|
579
720
|
return defaultPath;
|
|
580
721
|
}
|
|
581
|
-
const suggestedPath =
|
|
582
|
-
const customPath = await
|
|
583
|
-
message: "Enter custom global path:",
|
|
722
|
+
const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
|
|
723
|
+
const customPath = await directoryPrompt({
|
|
724
|
+
message: "Enter custom global path (Tab to autocomplete):",
|
|
584
725
|
defaultValue: suggestedPath,
|
|
585
|
-
placeholder: suggestedPath,
|
|
586
726
|
validate: (value) => {
|
|
587
727
|
if (!value.trim()) {
|
|
588
728
|
return "Path cannot be empty";
|
|
589
729
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return `Cannot write to ${expandedPath2}. Please choose a writable path.`;
|
|
730
|
+
if (!checkWriteAccess(value)) {
|
|
731
|
+
return `Cannot write to ${value}. Please choose a writable path.`;
|
|
593
732
|
}
|
|
594
733
|
return void 0;
|
|
595
734
|
}
|
|
596
735
|
});
|
|
597
|
-
if (
|
|
736
|
+
if (isCancelled(customPath)) {
|
|
598
737
|
return void 0;
|
|
599
738
|
}
|
|
600
|
-
let expandedPath = customPath
|
|
739
|
+
let expandedPath = customPath;
|
|
601
740
|
if (!expandedPath.endsWith(".rrce-workflow")) {
|
|
602
|
-
expandedPath =
|
|
741
|
+
expandedPath = path7.join(expandedPath, ".rrce-workflow");
|
|
603
742
|
}
|
|
604
743
|
return expandedPath;
|
|
605
744
|
}
|
|
@@ -607,14 +746,14 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
|
|
|
607
746
|
const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
|
|
608
747
|
for (const dataPath of dataPaths) {
|
|
609
748
|
ensureDir(dataPath);
|
|
610
|
-
ensureDir(
|
|
611
|
-
ensureDir(
|
|
612
|
-
ensureDir(
|
|
613
|
-
ensureDir(
|
|
749
|
+
ensureDir(path7.join(dataPath, "knowledge"));
|
|
750
|
+
ensureDir(path7.join(dataPath, "refs"));
|
|
751
|
+
ensureDir(path7.join(dataPath, "tasks"));
|
|
752
|
+
ensureDir(path7.join(dataPath, "templates"));
|
|
614
753
|
}
|
|
615
754
|
const agentCoreDir = getAgentCoreDir();
|
|
616
755
|
syncMetadataToAll(agentCoreDir, dataPaths);
|
|
617
|
-
copyDirToAllStoragePaths(
|
|
756
|
+
copyDirToAllStoragePaths(path7.join(agentCoreDir, "templates"), "templates", dataPaths);
|
|
618
757
|
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
619
758
|
if (config.tools.includes("copilot")) {
|
|
620
759
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
@@ -626,8 +765,8 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
|
|
|
626
765
|
ensureDir(antigravityPath);
|
|
627
766
|
copyPromptsToDir(prompts, antigravityPath, ".md");
|
|
628
767
|
}
|
|
629
|
-
const workspaceConfigPath =
|
|
630
|
-
ensureDir(
|
|
768
|
+
const workspaceConfigPath = path7.join(workspacePath, ".rrce-workflow", "config.yaml");
|
|
769
|
+
ensureDir(path7.dirname(workspaceConfigPath));
|
|
631
770
|
let configContent = `# RRCE-Workflow Configuration
|
|
632
771
|
version: 1
|
|
633
772
|
|
|
@@ -655,7 +794,10 @@ linked_projects:
|
|
|
655
794
|
`;
|
|
656
795
|
});
|
|
657
796
|
}
|
|
658
|
-
|
|
797
|
+
fs7.writeFileSync(workspaceConfigPath, configContent);
|
|
798
|
+
if (config.addToGitignore) {
|
|
799
|
+
updateGitignore(workspacePath, config.storageMode, config.tools);
|
|
800
|
+
}
|
|
659
801
|
if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
|
|
660
802
|
const selectedProjects = allProjects.filter(
|
|
661
803
|
(p) => config.linkedProjects.includes(`${p.name}:${p.source}`)
|
|
@@ -664,32 +806,74 @@ linked_projects:
|
|
|
664
806
|
}
|
|
665
807
|
}
|
|
666
808
|
function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
667
|
-
const globalPath =
|
|
668
|
-
const workspacePath =
|
|
809
|
+
const globalPath = path7.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
|
|
810
|
+
const workspacePath = path7.join(workspaceRoot, ".rrce-workflow");
|
|
669
811
|
switch (mode) {
|
|
670
812
|
case "global":
|
|
671
813
|
return [globalPath];
|
|
672
814
|
case "workspace":
|
|
673
815
|
return [workspacePath];
|
|
674
|
-
case "both":
|
|
675
|
-
return [workspacePath, globalPath];
|
|
676
816
|
default:
|
|
677
817
|
return [globalPath];
|
|
678
818
|
}
|
|
679
819
|
}
|
|
820
|
+
function updateGitignore(workspacePath, storageMode, tools) {
|
|
821
|
+
const gitignorePath = path7.join(workspacePath, ".gitignore");
|
|
822
|
+
const entries = [];
|
|
823
|
+
if (storageMode === "workspace") {
|
|
824
|
+
entries.push(".rrce-workflow/");
|
|
825
|
+
}
|
|
826
|
+
if (tools.includes("copilot")) {
|
|
827
|
+
entries.push(".github/agents/");
|
|
828
|
+
}
|
|
829
|
+
if (tools.includes("antigravity")) {
|
|
830
|
+
entries.push(".agent/");
|
|
831
|
+
}
|
|
832
|
+
if (entries.length === 0) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
let content = "";
|
|
837
|
+
if (fs7.existsSync(gitignorePath)) {
|
|
838
|
+
content = fs7.readFileSync(gitignorePath, "utf-8");
|
|
839
|
+
}
|
|
840
|
+
const lines = content.split("\n").map((line) => line.trim());
|
|
841
|
+
const newEntries = [];
|
|
842
|
+
for (const entry of entries) {
|
|
843
|
+
const entryWithoutSlash = entry.replace(/\/$/, "");
|
|
844
|
+
if (!lines.some((line) => line === entry || line === entryWithoutSlash)) {
|
|
845
|
+
newEntries.push(entry);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (newEntries.length === 0) {
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
let newContent = content;
|
|
852
|
+
if (!newContent.endsWith("\n") && newContent !== "") {
|
|
853
|
+
newContent += "\n";
|
|
854
|
+
}
|
|
855
|
+
if (newContent === "" || !content.includes("# rrce-workflow")) {
|
|
856
|
+
newContent += "\n# rrce-workflow generated folders\n";
|
|
857
|
+
}
|
|
858
|
+
newContent += newEntries.join("\n") + "\n";
|
|
859
|
+
fs7.writeFileSync(gitignorePath, newContent);
|
|
860
|
+
return true;
|
|
861
|
+
} catch {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
680
865
|
|
|
681
866
|
// src/commands/wizard/link-flow.ts
|
|
682
867
|
import { multiselect as multiselect2, spinner as spinner2, note as note2, outro as outro2, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
|
|
683
|
-
import
|
|
684
|
-
import * as
|
|
868
|
+
import pc3 from "picocolors";
|
|
869
|
+
import * as fs8 from "fs";
|
|
685
870
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
686
871
|
const projects = scanForProjects({
|
|
687
872
|
excludeWorkspace: workspaceName,
|
|
688
|
-
workspacePath
|
|
689
|
-
scanSiblings: true
|
|
873
|
+
workspacePath
|
|
690
874
|
});
|
|
691
875
|
if (projects.length === 0) {
|
|
692
|
-
outro2(
|
|
876
|
+
outro2(pc3.yellow("No other projects found. Try setting up another project first."));
|
|
693
877
|
return;
|
|
694
878
|
}
|
|
695
879
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
@@ -698,8 +882,8 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
698
882
|
options: projects.map((project) => ({
|
|
699
883
|
value: `${project.name}:${project.source}`,
|
|
700
884
|
// Unique key
|
|
701
|
-
label: `${project.name} ${
|
|
702
|
-
hint:
|
|
885
|
+
label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
|
|
886
|
+
hint: pc3.dim(
|
|
703
887
|
project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
|
|
704
888
|
)
|
|
705
889
|
})),
|
|
@@ -720,7 +904,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
720
904
|
const s = spinner2();
|
|
721
905
|
s.start("Linking projects");
|
|
722
906
|
const configFilePath = getConfigPath(workspacePath);
|
|
723
|
-
let configContent =
|
|
907
|
+
let configContent = fs8.readFileSync(configFilePath, "utf-8");
|
|
724
908
|
if (configContent.includes("linked_projects:")) {
|
|
725
909
|
const lines = configContent.split("\n");
|
|
726
910
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -747,42 +931,42 @@ linked_projects:
|
|
|
747
931
|
`;
|
|
748
932
|
});
|
|
749
933
|
}
|
|
750
|
-
|
|
934
|
+
fs8.writeFileSync(configFilePath, configContent);
|
|
751
935
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
752
936
|
s.stop("Projects linked");
|
|
753
937
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
754
938
|
const summary = [
|
|
755
939
|
`Linked projects:`,
|
|
756
|
-
...selectedProjects.map((p) => ` \u2713 ${p.name} ${
|
|
940
|
+
...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc3.dim(`(${p.source})`)}`),
|
|
757
941
|
``,
|
|
758
|
-
`Workspace file: ${
|
|
942
|
+
`Workspace file: ${pc3.cyan(workspaceFile)}`
|
|
759
943
|
];
|
|
760
944
|
note2(summary.join("\n"), "Link Summary");
|
|
761
|
-
outro2(
|
|
945
|
+
outro2(pc3.green(`\u2713 Projects linked! Open ${pc3.bold(workspaceFile)} in VSCode to access linked data.`));
|
|
762
946
|
}
|
|
763
947
|
|
|
764
948
|
// src/commands/wizard/sync-flow.ts
|
|
765
949
|
import { confirm as confirm2, spinner as spinner3, note as note3, outro as outro3, cancel as cancel3, isCancel as isCancel3 } from "@clack/prompts";
|
|
766
|
-
import
|
|
767
|
-
import * as
|
|
768
|
-
import * as
|
|
950
|
+
import pc4 from "picocolors";
|
|
951
|
+
import * as fs9 from "fs";
|
|
952
|
+
import * as path8 from "path";
|
|
769
953
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
770
954
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
771
955
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
772
|
-
const globalPath =
|
|
956
|
+
const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
|
|
773
957
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
774
958
|
const existingDirs = subdirs.filter(
|
|
775
|
-
(dir) =>
|
|
959
|
+
(dir) => fs9.existsSync(path8.join(localPath, dir))
|
|
776
960
|
);
|
|
777
961
|
if (existingDirs.length === 0) {
|
|
778
|
-
outro3(
|
|
962
|
+
outro3(pc4.yellow("No data found in workspace storage to sync."));
|
|
779
963
|
return;
|
|
780
964
|
}
|
|
781
965
|
note3(
|
|
782
966
|
`The following will be copied to global storage:
|
|
783
967
|
${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
|
|
784
968
|
|
|
785
|
-
Destination: ${
|
|
969
|
+
Destination: ${pc4.cyan(globalPath)}`,
|
|
786
970
|
"Sync Preview"
|
|
787
971
|
);
|
|
788
972
|
const shouldSync = await confirm2({
|
|
@@ -798,27 +982,22 @@ Destination: ${pc3.cyan(globalPath)}`,
|
|
|
798
982
|
try {
|
|
799
983
|
ensureDir(globalPath);
|
|
800
984
|
for (const dir of existingDirs) {
|
|
801
|
-
const srcDir =
|
|
802
|
-
const destDir =
|
|
985
|
+
const srcDir = path8.join(localPath, dir);
|
|
986
|
+
const destDir = path8.join(globalPath, dir);
|
|
803
987
|
ensureDir(destDir);
|
|
804
988
|
copyDirRecursive(srcDir, destDir);
|
|
805
989
|
}
|
|
806
|
-
const configFilePath = getConfigPath(workspacePath);
|
|
807
|
-
let configContent = fs8.readFileSync(configFilePath, "utf-8");
|
|
808
|
-
configContent = configContent.replace(/mode:\s*workspace/, "mode: both");
|
|
809
|
-
fs8.writeFileSync(configFilePath, configContent);
|
|
810
990
|
s.stop("Sync complete");
|
|
811
991
|
const summary = [
|
|
812
992
|
`Synced directories:`,
|
|
813
993
|
...existingDirs.map((d) => ` \u2713 ${d}/`),
|
|
814
994
|
``,
|
|
815
|
-
`Global path: ${
|
|
816
|
-
`Storage mode updated to: ${pc3.bold("both")}`,
|
|
995
|
+
`Global path: ${pc4.cyan(globalPath)}`,
|
|
817
996
|
``,
|
|
818
997
|
`Other projects can now link this knowledge!`
|
|
819
998
|
];
|
|
820
999
|
note3(summary.join("\n"), "Sync Summary");
|
|
821
|
-
outro3(
|
|
1000
|
+
outro3(pc4.green("\u2713 Workspace knowledge synced to global storage!"));
|
|
822
1001
|
} catch (error) {
|
|
823
1002
|
s.stop("Error occurred");
|
|
824
1003
|
cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -828,9 +1007,9 @@ Destination: ${pc3.cyan(globalPath)}`,
|
|
|
828
1007
|
|
|
829
1008
|
// src/commands/wizard/update-flow.ts
|
|
830
1009
|
import { confirm as confirm3, spinner as spinner4, note as note4, outro as outro4, cancel as cancel4, isCancel as isCancel4 } from "@clack/prompts";
|
|
831
|
-
import
|
|
832
|
-
import * as
|
|
833
|
-
import * as
|
|
1010
|
+
import pc5 from "picocolors";
|
|
1011
|
+
import * as fs10 from "fs";
|
|
1012
|
+
import * as path9 from "path";
|
|
834
1013
|
async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
835
1014
|
const s = spinner4();
|
|
836
1015
|
s.start("Checking for updates");
|
|
@@ -860,10 +1039,10 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
860
1039
|
}
|
|
861
1040
|
s.start("Updating from package");
|
|
862
1041
|
for (const dataPath of dataPaths) {
|
|
863
|
-
copyDirToAllStoragePaths(
|
|
1042
|
+
copyDirToAllStoragePaths(path9.join(agentCoreDir, "templates"), "templates", [dataPath]);
|
|
864
1043
|
}
|
|
865
1044
|
const configFilePath = getConfigPath(workspacePath);
|
|
866
|
-
const configContent =
|
|
1045
|
+
const configContent = fs10.readFileSync(configFilePath, "utf-8");
|
|
867
1046
|
if (configContent.includes("copilot: true")) {
|
|
868
1047
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
869
1048
|
ensureDir(copilotPath);
|
|
@@ -883,7 +1062,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
883
1062
|
`Your configuration and knowledge files were preserved.`
|
|
884
1063
|
];
|
|
885
1064
|
note4(summary.join("\n"), "Update Summary");
|
|
886
|
-
outro4(
|
|
1065
|
+
outro4(pc5.green("\u2713 Successfully updated from package!"));
|
|
887
1066
|
} catch (error) {
|
|
888
1067
|
s.stop("Error occurred");
|
|
889
1068
|
cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -891,15 +1070,13 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
891
1070
|
}
|
|
892
1071
|
}
|
|
893
1072
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
894
|
-
const globalPath =
|
|
895
|
-
const workspacePath =
|
|
1073
|
+
const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
|
|
1074
|
+
const workspacePath = path9.join(workspaceRoot, ".rrce-workflow");
|
|
896
1075
|
switch (mode) {
|
|
897
1076
|
case "global":
|
|
898
1077
|
return [globalPath];
|
|
899
1078
|
case "workspace":
|
|
900
1079
|
return [workspacePath];
|
|
901
|
-
case "both":
|
|
902
|
-
return [workspacePath, globalPath];
|
|
903
1080
|
default:
|
|
904
1081
|
return [globalPath];
|
|
905
1082
|
}
|
|
@@ -907,7 +1084,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
|
|
|
907
1084
|
|
|
908
1085
|
// src/commands/wizard/index.ts
|
|
909
1086
|
async function runWizard() {
|
|
910
|
-
intro(
|
|
1087
|
+
intro(pc6.cyan(pc6.inverse(" RRCE-Workflow Setup ")));
|
|
911
1088
|
const s = spinner5();
|
|
912
1089
|
s.start("Detecting environment");
|
|
913
1090
|
const workspacePath = detectWorkspaceRoot();
|
|
@@ -916,28 +1093,27 @@ async function runWizard() {
|
|
|
916
1093
|
await new Promise((r) => setTimeout(r, 800));
|
|
917
1094
|
s.stop("Environment detected");
|
|
918
1095
|
note5(
|
|
919
|
-
`Git User: ${
|
|
920
|
-
Workspace: ${
|
|
1096
|
+
`Git User: ${pc6.bold(gitUser || "(not found)")}
|
|
1097
|
+
Workspace: ${pc6.bold(workspaceName)}`,
|
|
921
1098
|
"Context"
|
|
922
1099
|
);
|
|
923
1100
|
const detectedProjects = scanForProjects({
|
|
924
1101
|
excludeWorkspace: workspaceName,
|
|
925
|
-
workspacePath
|
|
926
|
-
scanSiblings: true
|
|
1102
|
+
workspacePath
|
|
927
1103
|
});
|
|
928
1104
|
const configFilePath = getConfigPath(workspacePath);
|
|
929
|
-
const isAlreadyConfigured =
|
|
1105
|
+
const isAlreadyConfigured = fs11.existsSync(configFilePath);
|
|
930
1106
|
let currentStorageMode = null;
|
|
931
1107
|
if (isAlreadyConfigured) {
|
|
932
1108
|
try {
|
|
933
|
-
const configContent =
|
|
934
|
-
const modeMatch = configContent.match(/mode:\s*(global|workspace
|
|
1109
|
+
const configContent = fs11.readFileSync(configFilePath, "utf-8");
|
|
1110
|
+
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
935
1111
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
936
1112
|
} catch {
|
|
937
1113
|
}
|
|
938
1114
|
}
|
|
939
1115
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
940
|
-
const hasLocalData =
|
|
1116
|
+
const hasLocalData = fs11.existsSync(localDataPath);
|
|
941
1117
|
if (isAlreadyConfigured) {
|
|
942
1118
|
const menuOptions = [];
|
|
943
1119
|
if (detectedProjects.length > 0) {
|
|
@@ -982,11 +1158,11 @@ Workspace: ${pc5.bold(workspaceName)}`,
|
|
|
982
1158
|
|
|
983
1159
|
// src/commands/selector.ts
|
|
984
1160
|
import { intro as intro2, select as select3, note as note6, cancel as cancel6, isCancel as isCancel6, outro as outro6 } from "@clack/prompts";
|
|
985
|
-
import
|
|
986
|
-
import * as
|
|
1161
|
+
import pc7 from "picocolors";
|
|
1162
|
+
import * as path10 from "path";
|
|
987
1163
|
async function runSelector() {
|
|
988
|
-
const workspaceName =
|
|
989
|
-
intro2(
|
|
1164
|
+
const workspaceName = path10.basename(process.cwd());
|
|
1165
|
+
intro2(pc7.cyan(pc7.inverse(` RRCE-Workflow | ${workspaceName} `)));
|
|
990
1166
|
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
991
1167
|
if (prompts.length === 0) {
|
|
992
1168
|
cancel6("No agents found. Run `rrce-workflow` to set up.");
|
|
@@ -1007,7 +1183,7 @@ async function runSelector() {
|
|
|
1007
1183
|
const prompt = selection;
|
|
1008
1184
|
note6(
|
|
1009
1185
|
`Use this agent in your IDE by invoking:
|
|
1010
|
-
${
|
|
1186
|
+
${pc7.bold(pc7.cyan(`@${prompt.frontmatter.name}`))}`,
|
|
1011
1187
|
"Agent Selected"
|
|
1012
1188
|
);
|
|
1013
1189
|
outro6("Done");
|