rrce-workflow 0.2.14 → 0.2.16

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 (47) hide show
  1. package/bin/rrce-workflow.js +3 -33
  2. package/dist/commands/selector.d.ts +1 -0
  3. package/dist/commands/selector.js +29 -0
  4. package/dist/commands/wizard/index.d.ts +1 -0
  5. package/dist/commands/wizard/index.js +86 -0
  6. package/dist/commands/wizard/link-flow.d.ts +5 -0
  7. package/dist/commands/wizard/link-flow.js +97 -0
  8. package/dist/commands/wizard/setup-flow.d.ts +4 -0
  9. package/dist/commands/wizard/setup-flow.js +262 -0
  10. package/dist/commands/wizard/sync-flow.d.ts +4 -0
  11. package/dist/commands/wizard/sync-flow.js +67 -0
  12. package/dist/commands/wizard/update-flow.d.ts +4 -0
  13. package/dist/commands/wizard/update-flow.js +85 -0
  14. package/dist/commands/wizard/utils.d.ts +9 -0
  15. package/dist/commands/wizard/utils.js +33 -0
  16. package/dist/commands/wizard/vscode.d.ts +15 -0
  17. package/dist/commands/wizard/vscode.js +148 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1181 -0
  20. package/dist/lib/autocomplete-prompt.d.ts +14 -0
  21. package/dist/lib/autocomplete-prompt.js +167 -0
  22. package/dist/lib/detection.d.ts +44 -0
  23. package/dist/lib/detection.js +185 -0
  24. package/dist/lib/git.d.ts +12 -0
  25. package/dist/lib/git.js +37 -0
  26. package/dist/lib/paths.d.ts +108 -0
  27. package/dist/lib/paths.js +296 -0
  28. package/dist/lib/prompts.d.ts +18 -0
  29. package/dist/lib/prompts.js +62 -0
  30. package/dist/types/prompt.d.ts +54 -0
  31. package/dist/types/prompt.js +20 -0
  32. package/package.json +10 -7
  33. package/src/commands/selector.ts +0 -42
  34. package/src/commands/wizard/index.ts +0 -114
  35. package/src/commands/wizard/link-flow.ts +0 -118
  36. package/src/commands/wizard/setup-flow.ts +0 -347
  37. package/src/commands/wizard/sync-flow.ts +0 -93
  38. package/src/commands/wizard/update-flow.ts +0 -124
  39. package/src/commands/wizard/utils.ts +0 -38
  40. package/src/commands/wizard/vscode.ts +0 -197
  41. package/src/index.ts +0 -11
  42. package/src/lib/autocomplete-prompt.ts +0 -190
  43. package/src/lib/detection.ts +0 -235
  44. package/src/lib/git.ts +0 -37
  45. package/src/lib/paths.ts +0 -332
  46. package/src/lib/prompts.ts +0 -73
  47. package/src/types/prompt.ts +0 -54
package/dist/index.js ADDED
@@ -0,0 +1,1181 @@
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";
5
+
6
+ // src/lib/git.ts
7
+ import { execSync } from "child_process";
8
+ function getGitUser() {
9
+ try {
10
+ const result = execSync("git config user.name", { encoding: "utf-8" });
11
+ return result.trim() || null;
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+
17
+ // src/lib/paths.ts
18
+ import * as fs from "fs";
19
+ import * as path from "path";
20
+ var RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
21
+ var RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
22
+ function detectWorkspaceRoot() {
23
+ if (RRCE_WORKSPACE) {
24
+ return RRCE_WORKSPACE;
25
+ }
26
+ let current = process.cwd();
27
+ while (current !== "/") {
28
+ if (fs.existsSync(path.join(current, ".git")) || fs.existsSync(path.join(current, ".rrce-workflow", "config.yaml")) || fs.existsSync(path.join(current, ".rrce-workflow.yaml"))) {
29
+ return current;
30
+ }
31
+ current = path.dirname(current);
32
+ }
33
+ return process.cwd();
34
+ }
35
+ function getConfigPath(workspaceRoot) {
36
+ const newPath = path.join(workspaceRoot, ".rrce-workflow", "config.yaml");
37
+ const legacyPath = path.join(workspaceRoot, ".rrce-workflow.yaml");
38
+ if (fs.existsSync(newPath)) {
39
+ return newPath;
40
+ }
41
+ if (fs.existsSync(legacyPath)) {
42
+ return legacyPath;
43
+ }
44
+ return newPath;
45
+ }
46
+ function getWorkspaceName(workspaceRoot) {
47
+ return path.basename(workspaceRoot);
48
+ }
49
+ function getRRCEHome() {
50
+ return RRCE_HOME;
51
+ }
52
+ function getLocalWorkspacePath(workspaceRoot) {
53
+ return path.join(workspaceRoot, ".rrce-workflow");
54
+ }
55
+ function ensureDir(dirPath) {
56
+ if (!fs.existsSync(dirPath)) {
57
+ fs.mkdirSync(dirPath, { recursive: true });
58
+ }
59
+ }
60
+ function getAgentPromptPath(workspaceRoot, tool) {
61
+ if (tool === "copilot") {
62
+ return path.join(workspaceRoot, ".github", "agents");
63
+ } else {
64
+ return path.join(workspaceRoot, ".agent", "workflows");
65
+ }
66
+ }
67
+ function copyToAllStoragePaths(sourceFile, relativePath, dataPaths) {
68
+ const content = fs.readFileSync(sourceFile);
69
+ for (const dataPath of dataPaths) {
70
+ const targetPath = path.join(dataPath, relativePath);
71
+ ensureDir(path.dirname(targetPath));
72
+ fs.writeFileSync(targetPath, content);
73
+ }
74
+ }
75
+ function copyDirToAllStoragePaths(sourceDir, relativeDir, dataPaths) {
76
+ if (!fs.existsSync(sourceDir)) {
77
+ return;
78
+ }
79
+ const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ const sourcePath = path.join(sourceDir, entry.name);
82
+ const relativePath = path.join(relativeDir, entry.name);
83
+ if (entry.isDirectory()) {
84
+ copyDirToAllStoragePaths(sourcePath, relativePath, dataPaths);
85
+ } else {
86
+ copyToAllStoragePaths(sourcePath, relativePath, dataPaths);
87
+ }
88
+ }
89
+ }
90
+ function syncMetadataToAll(agentCorePath, dataPaths) {
91
+ const metadataDirs = ["knowledge", "refs", "tasks"];
92
+ for (const dir of metadataDirs) {
93
+ const sourceDir = path.join(agentCorePath, dir);
94
+ copyDirToAllStoragePaths(sourceDir, dir, dataPaths);
95
+ }
96
+ }
97
+ function checkWriteAccess(dirPath) {
98
+ const testFile = path.join(dirPath, ".rrce-write-test");
99
+ try {
100
+ if (!fs.existsSync(dirPath)) {
101
+ fs.mkdirSync(dirPath, { recursive: true });
102
+ }
103
+ fs.writeFileSync(testFile, "write-test");
104
+ fs.unlinkSync(testFile);
105
+ return true;
106
+ } catch {
107
+ try {
108
+ if (fs.existsSync(testFile)) {
109
+ fs.unlinkSync(testFile);
110
+ }
111
+ } catch {
112
+ }
113
+ return false;
114
+ }
115
+ }
116
+ function getDefaultRRCEHome() {
117
+ return process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
118
+ }
119
+ function getEffectiveRRCEHome(workspaceRoot) {
120
+ if (workspaceRoot) {
121
+ const configPath = getConfigPath(workspaceRoot);
122
+ if (fs.existsSync(configPath)) {
123
+ try {
124
+ const content = fs.readFileSync(configPath, "utf-8");
125
+ const globalPathMatch = content.match(/globalPath:\s*["']?([^"'\n]+)["']?/);
126
+ if (globalPathMatch?.[1]) {
127
+ return globalPathMatch[1].trim();
128
+ }
129
+ } catch {
130
+ }
131
+ }
132
+ }
133
+ return getDefaultRRCEHome();
134
+ }
135
+
136
+ // src/lib/detection.ts
137
+ import * as fs2 from "fs";
138
+ import * as path2 from "path";
139
+ function scanForProjects(options = {}) {
140
+ const { excludeWorkspace, workspacePath, scanSiblings = true } = options;
141
+ const projects = [];
142
+ const seenPaths = /* @__PURE__ */ new Set();
143
+ const globalProjects = scanGlobalStorage(excludeWorkspace);
144
+ for (const project of globalProjects) {
145
+ if (!seenPaths.has(project.path)) {
146
+ seenPaths.add(project.path);
147
+ projects.push(project);
148
+ }
149
+ }
150
+ if (scanSiblings && workspacePath) {
151
+ const siblingProjects = scanSiblingDirectories(workspacePath, excludeWorkspace);
152
+ for (const project of siblingProjects) {
153
+ if (!seenPaths.has(project.path)) {
154
+ seenPaths.add(project.path);
155
+ projects.push(project);
156
+ }
157
+ }
158
+ }
159
+ return projects;
160
+ }
161
+ function scanGlobalStorage(excludeWorkspace) {
162
+ const rrceHome = getDefaultRRCEHome();
163
+ const workspacesDir = path2.join(rrceHome, "workspaces");
164
+ const projects = [];
165
+ if (!fs2.existsSync(workspacesDir)) {
166
+ return projects;
167
+ }
168
+ try {
169
+ const entries = fs2.readdirSync(workspacesDir, { withFileTypes: true });
170
+ for (const entry of entries) {
171
+ if (!entry.isDirectory()) continue;
172
+ if (entry.name === excludeWorkspace) continue;
173
+ const projectDataPath = path2.join(workspacesDir, entry.name);
174
+ const knowledgePath = path2.join(projectDataPath, "knowledge");
175
+ const refsPath = path2.join(projectDataPath, "refs");
176
+ const tasksPath = path2.join(projectDataPath, "tasks");
177
+ projects.push({
178
+ name: entry.name,
179
+ path: projectDataPath,
180
+ // For global projects, path is the data path
181
+ dataPath: projectDataPath,
182
+ source: "global",
183
+ knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
184
+ refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
185
+ tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
186
+ });
187
+ }
188
+ } catch {
189
+ }
190
+ return projects;
191
+ }
192
+ function scanSiblingDirectories(workspacePath, excludeWorkspace) {
193
+ const parentDir = path2.dirname(workspacePath);
194
+ const projects = [];
195
+ try {
196
+ const entries = fs2.readdirSync(parentDir, { withFileTypes: true });
197
+ for (const entry of entries) {
198
+ if (!entry.isDirectory()) continue;
199
+ const projectPath = path2.join(parentDir, entry.name);
200
+ if (projectPath === workspacePath) continue;
201
+ if (entry.name === excludeWorkspace) continue;
202
+ const configPath = path2.join(projectPath, ".rrce-workflow", "config.yaml");
203
+ if (!fs2.existsSync(configPath)) continue;
204
+ const config = parseWorkspaceConfig(configPath);
205
+ if (!config) continue;
206
+ const dataPath = path2.join(projectPath, ".rrce-workflow");
207
+ const knowledgePath = path2.join(dataPath, "knowledge");
208
+ const refsPath = path2.join(dataPath, "refs");
209
+ const tasksPath = path2.join(dataPath, "tasks");
210
+ projects.push({
211
+ name: config.name || entry.name,
212
+ path: projectPath,
213
+ dataPath,
214
+ source: "sibling",
215
+ storageMode: config.storageMode,
216
+ knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
217
+ refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
218
+ tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
219
+ });
220
+ }
221
+ } catch {
222
+ }
223
+ return projects;
224
+ }
225
+ function parseWorkspaceConfig(configPath) {
226
+ try {
227
+ const content = fs2.readFileSync(configPath, "utf-8");
228
+ const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
229
+ const modeMatch = content.match(/mode:\s*(global|workspace|both)/);
230
+ const linkedProjects = [];
231
+ const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
232
+ if (linkedMatch && linkedMatch[1]) {
233
+ const lines = linkedMatch[1].split("\n");
234
+ for (const line of lines) {
235
+ const projectMatch = line.match(/^\s+-\s+(.+)$/);
236
+ if (projectMatch && projectMatch[1]) {
237
+ linkedProjects.push(projectMatch[1].trim());
238
+ }
239
+ }
240
+ }
241
+ return {
242
+ name: nameMatch?.[1]?.trim() || path2.basename(path2.dirname(path2.dirname(configPath))),
243
+ storageMode: modeMatch?.[1] || "global",
244
+ linkedProjects: linkedProjects.length > 0 ? linkedProjects : void 0
245
+ };
246
+ } catch {
247
+ return null;
248
+ }
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
+
286
+ // 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";
291
+
292
+ // src/lib/prompts.ts
293
+ import * as fs3 from "fs";
294
+ import * as path3 from "path";
295
+ import { fileURLToPath } from "url";
296
+ import matter from "gray-matter";
297
+
298
+ // src/types/prompt.ts
299
+ import { z } from "zod";
300
+ var PromptArgSchema = z.object({
301
+ name: z.string(),
302
+ default: z.string().optional(),
303
+ prompt: z.string().optional()
304
+ });
305
+ var AutoIdentitySchema = z.object({
306
+ user: z.string(),
307
+ model: z.string()
308
+ });
309
+ var PromptFrontmatterSchema = z.object({
310
+ name: z.string(),
311
+ description: z.string(),
312
+ "argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
313
+ tools: z.array(z.string()).optional(),
314
+ "required-args": z.array(PromptArgSchema).optional(),
315
+ "optional-args": z.array(PromptArgSchema).optional(),
316
+ "auto-identity": AutoIdentitySchema.optional()
317
+ });
318
+
319
+ // src/lib/prompts.ts
320
+ var __filename = fileURLToPath(import.meta.url);
321
+ var __dirname = path3.dirname(__filename);
322
+ function parsePromptFile(filePath) {
323
+ try {
324
+ const fileContent = fs3.readFileSync(filePath, "utf-8");
325
+ const { data, content } = matter(fileContent);
326
+ const parsed = PromptFrontmatterSchema.safeParse(data);
327
+ if (!parsed.success) {
328
+ console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
329
+ return null;
330
+ }
331
+ return {
332
+ frontmatter: parsed.data,
333
+ content: content.trim(),
334
+ filePath
335
+ };
336
+ } catch (error) {
337
+ console.error(`Error reading prompt file ${filePath}:`, error);
338
+ return null;
339
+ }
340
+ }
341
+ function loadPromptsFromDir(dirPath) {
342
+ if (!fs3.existsSync(dirPath)) {
343
+ return [];
344
+ }
345
+ const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
346
+ const prompts = [];
347
+ for (const file of files) {
348
+ const prompt = parsePromptFile(path3.join(dirPath, file));
349
+ if (prompt) {
350
+ prompts.push(prompt);
351
+ }
352
+ }
353
+ return prompts;
354
+ }
355
+ function getAgentCoreDir() {
356
+ return path3.join(__dirname, "..", "..", "agent-core");
357
+ }
358
+ function getAgentCorePromptsDir() {
359
+ return path3.join(getAgentCoreDir(), "prompts");
360
+ }
361
+
362
+ // src/commands/wizard/utils.ts
363
+ import * as fs4 from "fs";
364
+ import * as path4 from "path";
365
+ function copyPromptsToDir(prompts, targetDir, extension) {
366
+ for (const prompt of prompts) {
367
+ const baseName = path4.basename(prompt.filePath, ".md");
368
+ const targetName = baseName + extension;
369
+ const targetPath = path4.join(targetDir, targetName);
370
+ const content = fs4.readFileSync(prompt.filePath, "utf-8");
371
+ fs4.writeFileSync(targetPath, content);
372
+ }
373
+ }
374
+ function copyDirRecursive(src, dest) {
375
+ const entries = fs4.readdirSync(src, { withFileTypes: true });
376
+ for (const entry of entries) {
377
+ const srcPath = path4.join(src, entry.name);
378
+ const destPath = path4.join(dest, entry.name);
379
+ if (entry.isDirectory()) {
380
+ ensureDir(destPath);
381
+ copyDirRecursive(srcPath, destPath);
382
+ } else {
383
+ fs4.copyFileSync(srcPath, destPath);
384
+ }
385
+ }
386
+ }
387
+
388
+ // src/commands/wizard/vscode.ts
389
+ import * as fs5 from "fs";
390
+ import * as path5 from "path";
391
+ var REFERENCE_GROUP_PREFIX = "\u{1F4C1} References";
392
+ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
393
+ const workspaceFilePath = path5.join(workspacePath, `${workspaceName}.code-workspace`);
394
+ let workspace;
395
+ if (fs5.existsSync(workspaceFilePath)) {
396
+ try {
397
+ const content = fs5.readFileSync(workspaceFilePath, "utf-8");
398
+ workspace = JSON.parse(content);
399
+ } catch {
400
+ workspace = { folders: [], settings: {} };
401
+ }
402
+ } else {
403
+ workspace = { folders: [], settings: {} };
404
+ }
405
+ if (!workspace.settings) {
406
+ workspace.settings = {};
407
+ }
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}")
410
+ );
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
+ }
421
+ }
422
+ const referenceFolderPaths = [];
423
+ const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
424
+ if (isDetectedProjects) {
425
+ const projects = linkedProjects;
426
+ for (const project of projects) {
427
+ const folders = getProjectFolders(project);
428
+ 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
+ }
438
+ }
439
+ }
440
+ } else {
441
+ const projectNames = linkedProjects;
442
+ const rrceHome = customGlobalPath || getRRCEHome();
443
+ 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
+ }
462
+ }
463
+ }
464
+ }
465
+ if (referenceFolderPaths.length > 0) {
466
+ const readonlyPatterns = {};
467
+ for (const folderPath of referenceFolderPaths) {
468
+ readonlyPatterns[`${folderPath}/**`] = true;
469
+ }
470
+ const existingReadonly = workspace.settings["files.readonlyInclude"] || {};
471
+ workspace.settings["files.readonlyInclude"] = {
472
+ ...existingReadonly,
473
+ ...readonlyPatterns
474
+ };
475
+ }
476
+ workspace.settings["explorer.sortOrder"] = workspace.settings["explorer.sortOrder"] || "default";
477
+ fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
478
+ }
479
+
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
+ // src/commands/wizard/setup-flow.ts
609
+ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
610
+ const s = spinner();
611
+ const config = await group(
612
+ {
613
+ storageMode: () => select({
614
+ message: "Where should workflow data be stored?",
615
+ options: [
616
+ { value: "global", label: "Global (~/.rrce-workflow/)" },
617
+ { value: "workspace", label: "Workspace (.rrce-workflow/)" },
618
+ { value: "both", label: "Both" }
619
+ ],
620
+ initialValue: "global"
621
+ }),
622
+ tools: () => multiselect({
623
+ message: "Which AI tools do you use?",
624
+ options: [
625
+ { value: "copilot", label: "GitHub Copilot", hint: "VSCode" },
626
+ { value: "antigravity", label: "Antigravity IDE" }
627
+ ],
628
+ required: false
629
+ }),
630
+ linkedProjects: () => {
631
+ if (existingProjects.length === 0) {
632
+ return Promise.resolve([]);
633
+ }
634
+ return multiselect({
635
+ message: "Link knowledge from other projects?",
636
+ options: existingProjects.map((project) => ({
637
+ value: project.name,
638
+ label: project.name,
639
+ hint: pc2.dim(getProjectDisplayLabel(project))
640
+ })),
641
+ required: false
642
+ });
643
+ },
644
+ confirm: () => confirm({
645
+ message: "Create configuration?",
646
+ initialValue: true
647
+ })
648
+ },
649
+ {
650
+ onCancel: () => {
651
+ cancel("Setup process cancelled.");
652
+ process.exit(0);
653
+ }
654
+ }
655
+ );
656
+ if (!config.confirm) {
657
+ outro("Setup cancelled by user.");
658
+ process.exit(0);
659
+ }
660
+ let customGlobalPath;
661
+ if (config.storageMode === "global" || config.storageMode === "both") {
662
+ customGlobalPath = await resolveGlobalPath();
663
+ if (!customGlobalPath) {
664
+ cancel("Setup cancelled - no writable global path available.");
665
+ process.exit(1);
666
+ }
667
+ }
668
+ s.start("Generating configuration");
669
+ try {
670
+ await generateConfiguration({
671
+ storageMode: config.storageMode,
672
+ globalPath: customGlobalPath,
673
+ tools: config.tools,
674
+ linkedProjects: config.linkedProjects
675
+ }, workspacePath, workspaceName, existingProjects);
676
+ s.stop("Configuration generated");
677
+ const dataPaths = getDataPaths(
678
+ config.storageMode,
679
+ workspaceName,
680
+ workspacePath,
681
+ customGlobalPath
682
+ );
683
+ const summary = [
684
+ `Storage: ${config.storageMode === "both" ? "global + workspace" : config.storageMode}`
685
+ ];
686
+ if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
687
+ summary.push(`Global path: ${pc2.cyan(customGlobalPath)}`);
688
+ }
689
+ if (dataPaths.length > 0) {
690
+ summary.push(`Data paths:`);
691
+ dataPaths.forEach((p) => summary.push(` - ${p}`));
692
+ }
693
+ const selectedTools = config.tools;
694
+ if (selectedTools.length > 0) {
695
+ summary.push(`Tools: ${selectedTools.join(", ")}`);
696
+ }
697
+ const linkedProjects = config.linkedProjects;
698
+ if (linkedProjects.length > 0) {
699
+ summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
700
+ summary.push(`Workspace file: ${pc2.cyan(`${workspaceName}.code-workspace`)}`);
701
+ }
702
+ note(summary.join("\n"), "Setup Summary");
703
+ if (linkedProjects.length > 0) {
704
+ outro(pc2.green(`\u2713 Setup complete! Open ${pc2.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
705
+ } else {
706
+ outro(pc2.green(`\u2713 Setup complete! Your agents are ready to use.`));
707
+ }
708
+ } catch (error) {
709
+ s.stop("Error occurred");
710
+ cancel(`Failed to setup: ${error instanceof Error ? error.message : String(error)}`);
711
+ process.exit(1);
712
+ }
713
+ }
714
+ async function resolveGlobalPath() {
715
+ const defaultPath = getDefaultRRCEHome();
716
+ const isDefaultWritable = checkWriteAccess(defaultPath);
717
+ const options = [];
718
+ options.push({
719
+ value: "default",
720
+ label: `Default (${defaultPath})`,
721
+ hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
722
+ });
723
+ options.push({
724
+ value: "custom",
725
+ label: "Custom path",
726
+ hint: "Specify your own directory"
727
+ });
728
+ const choice = await select({
729
+ message: "Global storage location:",
730
+ options,
731
+ initialValue: isDefaultWritable ? "default" : "custom"
732
+ });
733
+ if (isCancel2(choice)) {
734
+ return void 0;
735
+ }
736
+ if (choice === "default") {
737
+ if (!isDefaultWritable) {
738
+ note(
739
+ `${pc2.yellow("\u26A0")} Cannot write to default path:
740
+ ${pc2.dim(defaultPath)}
741
+
742
+ This can happen when running via npx/bunx in restricted environments.
743
+ Please choose a custom path instead.`,
744
+ "Write Access Issue"
745
+ );
746
+ return resolveGlobalPath();
747
+ }
748
+ return defaultPath;
749
+ }
750
+ const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
751
+ const customPath = await directoryAutocomplete({
752
+ message: "Enter custom global path:",
753
+ initialValue: suggestedPath,
754
+ hint: "Tab to autocomplete",
755
+ validate: (value) => {
756
+ if (!value.trim()) {
757
+ return "Path cannot be empty";
758
+ }
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.`;
762
+ }
763
+ return void 0;
764
+ }
765
+ });
766
+ if (isCancel(customPath)) {
767
+ return void 0;
768
+ }
769
+ return customPath;
770
+ }
771
+ async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
772
+ const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
773
+ for (const dataPath of dataPaths) {
774
+ 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"));
779
+ }
780
+ const agentCoreDir = getAgentCoreDir();
781
+ syncMetadataToAll(agentCoreDir, dataPaths);
782
+ copyDirToAllStoragePaths(path7.join(agentCoreDir, "templates"), "templates", dataPaths);
783
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
784
+ if (config.tools.includes("copilot")) {
785
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
786
+ ensureDir(copilotPath);
787
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
788
+ }
789
+ if (config.tools.includes("antigravity")) {
790
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
791
+ ensureDir(antigravityPath);
792
+ copyPromptsToDir(prompts, antigravityPath, ".md");
793
+ }
794
+ const workspaceConfigPath = path7.join(workspacePath, ".rrce-workflow", "config.yaml");
795
+ ensureDir(path7.dirname(workspaceConfigPath));
796
+ let configContent = `# RRCE-Workflow Configuration
797
+ version: 1
798
+
799
+ storage:
800
+ mode: ${config.storageMode}`;
801
+ if (config.globalPath && config.globalPath !== getDefaultRRCEHome()) {
802
+ configContent += `
803
+ globalPath: "${config.globalPath}"`;
804
+ }
805
+ configContent += `
806
+
807
+ project:
808
+ name: "${workspaceName}"
809
+
810
+ tools:
811
+ copilot: ${config.tools.includes("copilot")}
812
+ antigravity: ${config.tools.includes("antigravity")}
813
+ `;
814
+ if (config.linkedProjects.length > 0) {
815
+ configContent += `
816
+ linked_projects:
817
+ `;
818
+ config.linkedProjects.forEach((name) => {
819
+ configContent += ` - ${name}
820
+ `;
821
+ });
822
+ }
823
+ fs7.writeFileSync(workspaceConfigPath, configContent);
824
+ if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
825
+ const selectedProjects = allProjects.filter((p) => config.linkedProjects.includes(p.name));
826
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, config.globalPath);
827
+ }
828
+ }
829
+ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
830
+ const globalPath = path7.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
831
+ const workspacePath = path7.join(workspaceRoot, ".rrce-workflow");
832
+ switch (mode) {
833
+ case "global":
834
+ return [globalPath];
835
+ case "workspace":
836
+ return [workspacePath];
837
+ case "both":
838
+ return [workspacePath, globalPath];
839
+ default:
840
+ return [globalPath];
841
+ }
842
+ }
843
+
844
+ // 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";
848
+ async function runLinkProjectsFlow(workspacePath, workspaceName) {
849
+ const projects = scanForProjects({
850
+ excludeWorkspace: workspaceName,
851
+ workspacePath,
852
+ scanSiblings: true
853
+ });
854
+ if (projects.length === 0) {
855
+ outro2(pc3.yellow("No other projects found. Try setting up another project first."));
856
+ return;
857
+ }
858
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
859
+ const linkedProjects = await multiselect2({
860
+ message: "Select projects to link:",
861
+ options: projects.map((project) => ({
862
+ value: project.name,
863
+ label: project.name,
864
+ hint: pc3.dim(getProjectDisplayLabel(project))
865
+ })),
866
+ required: true
867
+ });
868
+ if (isCancel3(linkedProjects)) {
869
+ cancel2("Cancelled.");
870
+ process.exit(0);
871
+ }
872
+ const selectedProjectNames = linkedProjects;
873
+ if (selectedProjectNames.length === 0) {
874
+ outro2("No projects selected.");
875
+ return;
876
+ }
877
+ const selectedProjects = projects.filter((p) => selectedProjectNames.includes(p.name));
878
+ const s = spinner2();
879
+ s.start("Linking projects");
880
+ const configFilePath = getConfigPath(workspacePath);
881
+ let configContent = fs8.readFileSync(configFilePath, "utf-8");
882
+ if (configContent.includes("linked_projects:")) {
883
+ const lines = configContent.split("\n");
884
+ const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
885
+ if (linkedIndex !== -1) {
886
+ let insertIndex = linkedIndex + 1;
887
+ while (insertIndex < lines.length && lines[insertIndex]?.startsWith(" - ")) {
888
+ insertIndex++;
889
+ }
890
+ for (const name of selectedProjectNames) {
891
+ if (!configContent.includes(` - ${name}`)) {
892
+ lines.splice(insertIndex, 0, ` - ${name}`);
893
+ insertIndex++;
894
+ }
895
+ }
896
+ configContent = lines.join("\n");
897
+ }
898
+ } else {
899
+ configContent += `
900
+ linked_projects:
901
+ `;
902
+ selectedProjectNames.forEach((name) => {
903
+ configContent += ` - ${name}
904
+ `;
905
+ });
906
+ }
907
+ fs8.writeFileSync(configFilePath, configContent);
908
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
909
+ s.stop("Projects linked");
910
+ const workspaceFile = `${workspaceName}.code-workspace`;
911
+ const summary = [
912
+ `Linked projects:`,
913
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc3.dim(`(${p.source})`)}`),
914
+ ``,
915
+ `Workspace file: ${pc3.cyan(workspaceFile)}`,
916
+ ``,
917
+ pc3.dim("Includes: knowledge, refs, tasks folders")
918
+ ];
919
+ note2(summary.join("\n"), "Link Summary");
920
+ outro2(pc3.green(`\u2713 Projects linked! Open ${pc3.bold(workspaceFile)} in VSCode to access linked knowledge.`));
921
+ }
922
+
923
+ // 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";
928
+ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
929
+ const localPath = getLocalWorkspacePath(workspacePath);
930
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
931
+ const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
932
+ const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
933
+ const existingDirs = subdirs.filter(
934
+ (dir) => fs9.existsSync(path8.join(localPath, dir))
935
+ );
936
+ if (existingDirs.length === 0) {
937
+ outro3(pc4.yellow("No data found in workspace storage to sync."));
938
+ return;
939
+ }
940
+ note3(
941
+ `The following will be copied to global storage:
942
+ ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
943
+
944
+ Destination: ${pc4.cyan(globalPath)}`,
945
+ "Sync Preview"
946
+ );
947
+ const shouldSync = await confirm2({
948
+ message: "Proceed with sync to global storage?",
949
+ initialValue: true
950
+ });
951
+ if (isCancel4(shouldSync) || !shouldSync) {
952
+ outro3("Sync cancelled.");
953
+ return;
954
+ }
955
+ const s = spinner3();
956
+ s.start("Syncing to global storage");
957
+ try {
958
+ ensureDir(globalPath);
959
+ for (const dir of existingDirs) {
960
+ const srcDir = path8.join(localPath, dir);
961
+ const destDir = path8.join(globalPath, dir);
962
+ ensureDir(destDir);
963
+ copyDirRecursive(srcDir, destDir);
964
+ }
965
+ const configFilePath = getConfigPath(workspacePath);
966
+ let configContent = fs9.readFileSync(configFilePath, "utf-8");
967
+ configContent = configContent.replace(/mode:\s*workspace/, "mode: both");
968
+ fs9.writeFileSync(configFilePath, configContent);
969
+ s.stop("Sync complete");
970
+ const summary = [
971
+ `Synced directories:`,
972
+ ...existingDirs.map((d) => ` \u2713 ${d}/`),
973
+ ``,
974
+ `Global path: ${pc4.cyan(globalPath)}`,
975
+ `Storage mode updated to: ${pc4.bold("both")}`,
976
+ ``,
977
+ `Other projects can now link this knowledge!`
978
+ ];
979
+ note3(summary.join("\n"), "Sync Summary");
980
+ outro3(pc4.green("\u2713 Workspace knowledge synced to global storage!"));
981
+ } catch (error) {
982
+ s.stop("Error occurred");
983
+ cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
984
+ process.exit(1);
985
+ }
986
+ }
987
+
988
+ // 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";
993
+ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
994
+ const s = spinner4();
995
+ s.start("Checking for updates");
996
+ try {
997
+ const agentCoreDir = getAgentCoreDir();
998
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
999
+ const mode = currentStorageMode || "global";
1000
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
1001
+ const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
1002
+ s.stop("Updates found");
1003
+ note4(
1004
+ `The following will be updated from the package:
1005
+ \u2022 prompts/ (${prompts.length} agent prompts)
1006
+ \u2022 templates/ (output templates)
1007
+
1008
+ Target locations:
1009
+ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1010
+ "Update Preview"
1011
+ );
1012
+ const shouldUpdate = await confirm3({
1013
+ message: "Proceed with update?",
1014
+ initialValue: true
1015
+ });
1016
+ if (isCancel5(shouldUpdate) || !shouldUpdate) {
1017
+ outro4("Update cancelled.");
1018
+ return;
1019
+ }
1020
+ s.start("Updating from package");
1021
+ for (const dataPath of dataPaths) {
1022
+ copyDirToAllStoragePaths(path9.join(agentCoreDir, "templates"), "templates", [dataPath]);
1023
+ }
1024
+ const configFilePath = getConfigPath(workspacePath);
1025
+ const configContent = fs10.readFileSync(configFilePath, "utf-8");
1026
+ if (configContent.includes("copilot: true")) {
1027
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
1028
+ ensureDir(copilotPath);
1029
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
1030
+ }
1031
+ if (configContent.includes("antigravity: true")) {
1032
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
1033
+ ensureDir(antigravityPath);
1034
+ copyPromptsToDir(prompts, antigravityPath, ".md");
1035
+ }
1036
+ s.stop("Update complete");
1037
+ const summary = [
1038
+ `Updated:`,
1039
+ ` \u2713 ${prompts.length} agent prompts`,
1040
+ ` \u2713 Output templates`,
1041
+ ``,
1042
+ `Your configuration and knowledge files were preserved.`
1043
+ ];
1044
+ note4(summary.join("\n"), "Update Summary");
1045
+ outro4(pc5.green("\u2713 Successfully updated from package!"));
1046
+ } catch (error) {
1047
+ s.stop("Error occurred");
1048
+ cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
1049
+ process.exit(1);
1050
+ }
1051
+ }
1052
+ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
1053
+ const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
1054
+ const workspacePath = path9.join(workspaceRoot, ".rrce-workflow");
1055
+ switch (mode) {
1056
+ case "global":
1057
+ return [globalPath];
1058
+ case "workspace":
1059
+ return [workspacePath];
1060
+ case "both":
1061
+ return [workspacePath, globalPath];
1062
+ default:
1063
+ return [globalPath];
1064
+ }
1065
+ }
1066
+
1067
+ // src/commands/wizard/index.ts
1068
+ async function runWizard() {
1069
+ intro(pc6.cyan(pc6.inverse(" RRCE-Workflow Setup ")));
1070
+ const s = spinner5();
1071
+ s.start("Detecting environment");
1072
+ const workspacePath = detectWorkspaceRoot();
1073
+ const workspaceName = getWorkspaceName(workspacePath);
1074
+ const gitUser = getGitUser();
1075
+ await new Promise((r) => setTimeout(r, 800));
1076
+ s.stop("Environment detected");
1077
+ note5(
1078
+ `Git User: ${pc6.bold(gitUser || "(not found)")}
1079
+ Workspace: ${pc6.bold(workspaceName)}`,
1080
+ "Context"
1081
+ );
1082
+ const detectedProjects = scanForProjects({
1083
+ excludeWorkspace: workspaceName,
1084
+ workspacePath,
1085
+ scanSiblings: true
1086
+ });
1087
+ const configFilePath = getConfigPath(workspacePath);
1088
+ const isAlreadyConfigured = fs11.existsSync(configFilePath);
1089
+ let currentStorageMode = null;
1090
+ if (isAlreadyConfigured) {
1091
+ try {
1092
+ const configContent = fs11.readFileSync(configFilePath, "utf-8");
1093
+ const modeMatch = configContent.match(/mode:\s*(global|workspace|both)/);
1094
+ currentStorageMode = modeMatch?.[1] ?? null;
1095
+ } catch {
1096
+ }
1097
+ }
1098
+ const localDataPath = getLocalWorkspacePath(workspacePath);
1099
+ const hasLocalData = fs11.existsSync(localDataPath);
1100
+ if (isAlreadyConfigured) {
1101
+ const menuOptions = [];
1102
+ if (detectedProjects.length > 0) {
1103
+ menuOptions.push({
1104
+ value: "link",
1105
+ label: "Link other project knowledge",
1106
+ hint: `${detectedProjects.length} projects detected (global + sibling)`
1107
+ });
1108
+ }
1109
+ if (currentStorageMode === "workspace" && hasLocalData) {
1110
+ menuOptions.push({
1111
+ value: "sync-global",
1112
+ label: "Sync to global storage",
1113
+ hint: "Share knowledge with other projects"
1114
+ });
1115
+ }
1116
+ menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
1117
+ menuOptions.push({ value: "exit", label: "Exit" });
1118
+ const action = await select2({
1119
+ message: "This workspace is already configured. What would you like to do?",
1120
+ options: menuOptions
1121
+ });
1122
+ if (isCancel6(action) || action === "exit") {
1123
+ outro5("Exited.");
1124
+ process.exit(0);
1125
+ }
1126
+ if (action === "link") {
1127
+ await runLinkProjectsFlow(workspacePath, workspaceName);
1128
+ return;
1129
+ }
1130
+ if (action === "sync-global") {
1131
+ await runSyncToGlobalFlow(workspacePath, workspaceName);
1132
+ return;
1133
+ }
1134
+ if (action === "update") {
1135
+ await runUpdateFlow(workspacePath, workspaceName, currentStorageMode);
1136
+ return;
1137
+ }
1138
+ }
1139
+ await runSetupFlow(workspacePath, workspaceName, detectedProjects);
1140
+ }
1141
+
1142
+ // 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";
1146
+ async function runSelector() {
1147
+ const workspaceName = path10.basename(process.cwd());
1148
+ intro2(pc7.cyan(pc7.inverse(` RRCE-Workflow | ${workspaceName} `)));
1149
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1150
+ if (prompts.length === 0) {
1151
+ cancel6("No agents found. Run `rrce-workflow` to set up.");
1152
+ process.exit(0);
1153
+ }
1154
+ const selection = await select3({
1155
+ message: "Select an agent:",
1156
+ options: prompts.map((p) => ({
1157
+ value: p,
1158
+ label: p.frontmatter.name,
1159
+ hint: p.frontmatter.description
1160
+ }))
1161
+ });
1162
+ if (isCancel7(selection)) {
1163
+ cancel6("Selection cancelled.");
1164
+ process.exit(0);
1165
+ }
1166
+ const prompt = selection;
1167
+ note6(
1168
+ `Use this agent in your IDE by invoking:
1169
+ ${pc7.bold(pc7.cyan(`@${prompt.frontmatter.name}`))}`,
1170
+ "Agent Selected"
1171
+ );
1172
+ outro6("Done");
1173
+ }
1174
+
1175
+ // src/index.ts
1176
+ var command = process.argv[2];
1177
+ if (!command || command === "wizard") {
1178
+ runWizard();
1179
+ } else {
1180
+ runSelector();
1181
+ }