rrce-workflow 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1988 -1535
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -133,6 +133,239 @@ var init_paths = __esm({
133
133
  }
134
134
  });
135
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 } = 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.dataPath)) {
146
+ seenPaths.add(project.dataPath);
147
+ projects.push(project);
148
+ }
149
+ }
150
+ const homeProjects = scanHomeDirectory(workspacePath);
151
+ for (const project of homeProjects) {
152
+ if (!seenPaths.has(project.dataPath)) {
153
+ seenPaths.add(project.dataPath);
154
+ projects.push(project);
155
+ }
156
+ }
157
+ return projects;
158
+ }
159
+ function scanGlobalStorage(excludeWorkspace) {
160
+ const rrceHome = getDefaultRRCEHome();
161
+ const workspacesDir = path2.join(rrceHome, "workspaces");
162
+ const projects = [];
163
+ if (!fs2.existsSync(workspacesDir)) {
164
+ return projects;
165
+ }
166
+ try {
167
+ const entries = fs2.readdirSync(workspacesDir, { withFileTypes: true });
168
+ for (const entry of entries) {
169
+ if (!entry.isDirectory()) continue;
170
+ if (entry.name === excludeWorkspace) continue;
171
+ const projectDataPath = path2.join(workspacesDir, entry.name);
172
+ const knowledgePath = path2.join(projectDataPath, "knowledge");
173
+ const refsPath = path2.join(projectDataPath, "refs");
174
+ const tasksPath = path2.join(projectDataPath, "tasks");
175
+ projects.push({
176
+ name: entry.name,
177
+ path: projectDataPath,
178
+ // For global projects, path is the data path
179
+ dataPath: projectDataPath,
180
+ source: "global",
181
+ knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
182
+ refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
183
+ tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
184
+ });
185
+ }
186
+ } catch {
187
+ }
188
+ return projects;
189
+ }
190
+ function scanHomeDirectory(excludePath) {
191
+ const home = process.env.HOME;
192
+ if (!home) return [];
193
+ const projects = [];
194
+ const maxDepth = 5;
195
+ function scanDir(dirPath, depth) {
196
+ if (depth > maxDepth) return;
197
+ try {
198
+ const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
199
+ for (const entry of entries) {
200
+ if (!entry.isDirectory()) continue;
201
+ const fullPath = path2.join(dirPath, entry.name);
202
+ if (excludePath && fullPath === excludePath) continue;
203
+ if (entry.name === ".rrce-workflow") {
204
+ const configPath = path2.join(fullPath, "config.yaml");
205
+ if (fs2.existsSync(configPath)) {
206
+ const projectPath = dirPath;
207
+ const projectName = path2.basename(projectPath);
208
+ const config = parseWorkspaceConfig(configPath);
209
+ const knowledgePath = path2.join(fullPath, "knowledge");
210
+ const refsPath = path2.join(fullPath, "refs");
211
+ const tasksPath = path2.join(fullPath, "tasks");
212
+ projects.push({
213
+ name: config?.name || projectName,
214
+ path: projectPath,
215
+ dataPath: fullPath,
216
+ source: "local",
217
+ storageMode: config?.storageMode,
218
+ knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
219
+ refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
220
+ tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
221
+ });
222
+ }
223
+ continue;
224
+ }
225
+ if (SKIP_DIRECTORIES.has(entry.name)) continue;
226
+ if (entry.name.startsWith(".") && entry.name !== ".rrce-workflow") continue;
227
+ scanDir(fullPath, depth + 1);
228
+ }
229
+ } catch {
230
+ }
231
+ }
232
+ scanDir(home, 0);
233
+ return projects;
234
+ }
235
+ function parseWorkspaceConfig(configPath) {
236
+ try {
237
+ const content = fs2.readFileSync(configPath, "utf-8");
238
+ const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
239
+ const modeMatch = content.match(/mode:\s*(global|workspace)/);
240
+ const linkedProjects = [];
241
+ const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
242
+ if (linkedMatch && linkedMatch[1]) {
243
+ const lines = linkedMatch[1].split("\n");
244
+ for (const line of lines) {
245
+ const projectMatch = line.match(/^\s+-\s+(.+)$/);
246
+ if (projectMatch && projectMatch[1]) {
247
+ linkedProjects.push(projectMatch[1].trim());
248
+ }
249
+ }
250
+ }
251
+ return {
252
+ name: nameMatch?.[1]?.trim() || path2.basename(path2.dirname(path2.dirname(configPath))),
253
+ storageMode: modeMatch?.[1] || "global",
254
+ linkedProjects: linkedProjects.length > 0 ? linkedProjects : void 0
255
+ };
256
+ } catch {
257
+ return null;
258
+ }
259
+ }
260
+ var SKIP_DIRECTORIES;
261
+ var init_detection = __esm({
262
+ "src/lib/detection.ts"() {
263
+ "use strict";
264
+ init_paths();
265
+ SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
266
+ "node_modules",
267
+ ".git",
268
+ ".cache",
269
+ ".npm",
270
+ ".yarn",
271
+ ".pnpm",
272
+ ".local",
273
+ ".config",
274
+ ".vscode",
275
+ ".vscode-server",
276
+ "Library",
277
+ "Applications",
278
+ ".Trash",
279
+ "snap",
280
+ ".cargo",
281
+ ".rustup",
282
+ ".go",
283
+ ".docker"
284
+ ]);
285
+ }
286
+ });
287
+
288
+ // src/types/prompt.ts
289
+ import { z } from "zod";
290
+ var PromptArgSchema, AutoIdentitySchema, PromptFrontmatterSchema;
291
+ var init_prompt = __esm({
292
+ "src/types/prompt.ts"() {
293
+ "use strict";
294
+ PromptArgSchema = z.object({
295
+ name: z.string(),
296
+ default: z.string().optional(),
297
+ prompt: z.string().optional()
298
+ });
299
+ AutoIdentitySchema = z.object({
300
+ user: z.string(),
301
+ model: z.string()
302
+ });
303
+ PromptFrontmatterSchema = z.object({
304
+ name: z.string(),
305
+ description: z.string(),
306
+ "argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
307
+ tools: z.array(z.string()).optional(),
308
+ "required-args": z.array(PromptArgSchema).optional(),
309
+ "optional-args": z.array(PromptArgSchema).optional(),
310
+ "auto-identity": AutoIdentitySchema.optional()
311
+ });
312
+ }
313
+ });
314
+
315
+ // src/lib/prompts.ts
316
+ import * as fs3 from "fs";
317
+ import * as path3 from "path";
318
+ import { fileURLToPath } from "url";
319
+ import matter from "gray-matter";
320
+ function parsePromptFile(filePath) {
321
+ try {
322
+ const fileContent = fs3.readFileSync(filePath, "utf-8");
323
+ const { data, content } = matter(fileContent);
324
+ const parsed = PromptFrontmatterSchema.safeParse(data);
325
+ if (!parsed.success) {
326
+ console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
327
+ return null;
328
+ }
329
+ return {
330
+ frontmatter: parsed.data,
331
+ content: content.trim(),
332
+ filePath
333
+ };
334
+ } catch (error) {
335
+ console.error(`Error reading prompt file ${filePath}:`, error);
336
+ return null;
337
+ }
338
+ }
339
+ function loadPromptsFromDir(dirPath) {
340
+ if (!fs3.existsSync(dirPath)) {
341
+ return [];
342
+ }
343
+ const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
344
+ const prompts = [];
345
+ for (const file of files) {
346
+ const prompt = parsePromptFile(path3.join(dirPath, file));
347
+ if (prompt) {
348
+ prompts.push(prompt);
349
+ }
350
+ }
351
+ return prompts;
352
+ }
353
+ function getAgentCoreDir() {
354
+ return path3.join(__dirname, "..", "agent-core");
355
+ }
356
+ function getAgentCorePromptsDir() {
357
+ return path3.join(getAgentCoreDir(), "prompts");
358
+ }
359
+ var __filename, __dirname;
360
+ var init_prompts = __esm({
361
+ "src/lib/prompts.ts"() {
362
+ "use strict";
363
+ init_prompt();
364
+ __filename = fileURLToPath(import.meta.url);
365
+ __dirname = path3.dirname(__filename);
366
+ }
367
+ });
368
+
136
369
  // src/lib/autocomplete-prompt.ts
137
370
  import * as fs6 from "fs";
138
371
  import * as path6 from "path";
@@ -321,1716 +554,1936 @@ var init_tui_utils = __esm({
321
554
  }
322
555
  });
323
556
 
324
- // src/commands/wizard/index.ts
325
- import { intro, select as select3, spinner as spinner5, note as note6, outro as outro5, isCancel as isCancel6 } from "@clack/prompts";
326
- import pc7 from "picocolors";
327
- import * as fs11 from "fs";
557
+ // src/mcp/types.ts
558
+ var DEFAULT_MCP_CONFIG, DEFAULT_PERMISSIONS;
559
+ var init_types = __esm({
560
+ "src/mcp/types.ts"() {
561
+ "use strict";
562
+ DEFAULT_MCP_CONFIG = {
563
+ server: {
564
+ port: 3e3,
565
+ autoStart: false
566
+ },
567
+ projects: [],
568
+ defaults: {
569
+ includeNew: true,
570
+ permissions: {
571
+ knowledge: true,
572
+ tasks: true,
573
+ refs: true
574
+ }
575
+ }
576
+ };
577
+ DEFAULT_PERMISSIONS = {
578
+ knowledge: true,
579
+ tasks: true,
580
+ refs: true
581
+ };
582
+ }
583
+ });
328
584
 
329
- // src/lib/git.ts
330
- import { execSync } from "child_process";
331
- function getGitUser() {
585
+ // src/mcp/config.ts
586
+ import * as fs12 from "fs";
587
+ import * as path11 from "path";
588
+ function getMCPConfigPath() {
589
+ const workspaceRoot = detectWorkspaceRoot();
590
+ const rrceHome = getEffectiveRRCEHome(workspaceRoot);
591
+ return path11.join(rrceHome, "mcp.yaml");
592
+ }
593
+ function ensureMCPGlobalPath() {
594
+ const workspaceRoot = detectWorkspaceRoot();
595
+ const rrceHome = getEffectiveRRCEHome(workspaceRoot);
596
+ if (rrceHome.startsWith(".") || rrceHome.includes(".rrce-workflow/")) {
597
+ const configPath = path11.join(workspaceRoot, ".rrce-workflow", "config.yaml");
598
+ if (fs12.existsSync(configPath)) {
599
+ const content = fs12.readFileSync(configPath, "utf-8");
600
+ const modeMatch = content.match(/mode:\s*(global|workspace)/);
601
+ if (modeMatch?.[1] === "workspace") {
602
+ return {
603
+ configured: false,
604
+ path: rrceHome,
605
+ reason: "Workspace mode configured. MCP requires a global storage path."
606
+ };
607
+ }
608
+ }
609
+ }
610
+ return {
611
+ configured: true,
612
+ path: rrceHome
613
+ };
614
+ }
615
+ function loadMCPConfig() {
616
+ const configPath = getMCPConfigPath();
617
+ if (!fs12.existsSync(configPath)) {
618
+ return { ...DEFAULT_MCP_CONFIG };
619
+ }
332
620
  try {
333
- const result = execSync("git config user.name", { encoding: "utf-8" });
334
- return result.trim() || null;
621
+ const content = fs12.readFileSync(configPath, "utf-8");
622
+ return parseMCPConfig(content);
335
623
  } catch {
336
- return null;
624
+ return { ...DEFAULT_MCP_CONFIG };
337
625
  }
338
626
  }
339
-
340
- // src/commands/wizard/index.ts
341
- init_paths();
342
-
343
- // src/lib/detection.ts
344
- init_paths();
345
- import * as fs2 from "fs";
346
- import * as path2 from "path";
347
- var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
348
- "node_modules",
349
- ".git",
350
- ".cache",
351
- ".npm",
352
- ".yarn",
353
- ".pnpm",
354
- ".local",
355
- ".config",
356
- ".vscode",
357
- ".vscode-server",
358
- "Library",
359
- "Applications",
360
- ".Trash",
361
- "snap",
362
- ".cargo",
363
- ".rustup",
364
- ".go",
365
- ".docker"
366
- ]);
367
- function scanForProjects(options = {}) {
368
- const { excludeWorkspace, workspacePath } = options;
369
- const projects = [];
370
- const seenPaths = /* @__PURE__ */ new Set();
371
- const globalProjects = scanGlobalStorage(excludeWorkspace);
372
- for (const project of globalProjects) {
373
- if (!seenPaths.has(project.dataPath)) {
374
- seenPaths.add(project.dataPath);
375
- projects.push(project);
376
- }
377
- }
378
- const homeProjects = scanHomeDirectory(workspacePath);
379
- for (const project of homeProjects) {
380
- if (!seenPaths.has(project.dataPath)) {
381
- seenPaths.add(project.dataPath);
382
- projects.push(project);
383
- }
627
+ function saveMCPConfig(config) {
628
+ const configPath = getMCPConfigPath();
629
+ const dir = path11.dirname(configPath);
630
+ if (!fs12.existsSync(dir)) {
631
+ fs12.mkdirSync(dir, { recursive: true });
384
632
  }
385
- return projects;
633
+ const content = serializeMCPConfig(config);
634
+ fs12.writeFileSync(configPath, content);
386
635
  }
387
- function scanGlobalStorage(excludeWorkspace) {
388
- const rrceHome = getDefaultRRCEHome();
389
- const workspacesDir = path2.join(rrceHome, "workspaces");
390
- const projects = [];
391
- if (!fs2.existsSync(workspacesDir)) {
392
- return projects;
393
- }
394
- try {
395
- const entries = fs2.readdirSync(workspacesDir, { withFileTypes: true });
396
- for (const entry of entries) {
397
- if (!entry.isDirectory()) continue;
398
- if (entry.name === excludeWorkspace) continue;
399
- const projectDataPath = path2.join(workspacesDir, entry.name);
400
- const knowledgePath = path2.join(projectDataPath, "knowledge");
401
- const refsPath = path2.join(projectDataPath, "refs");
402
- const tasksPath = path2.join(projectDataPath, "tasks");
403
- projects.push({
404
- name: entry.name,
405
- path: projectDataPath,
406
- // For global projects, path is the data path
407
- dataPath: projectDataPath,
408
- source: "global",
409
- knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
410
- refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
411
- tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
412
- });
636
+ function parseMCPConfig(content) {
637
+ const config = { ...DEFAULT_MCP_CONFIG, projects: [] };
638
+ const lines = content.split("\n");
639
+ let currentSection = null;
640
+ let currentProject = null;
641
+ let inPermissions = false;
642
+ for (const line of lines) {
643
+ const trimmed = line.trim();
644
+ if (trimmed.startsWith("#") || trimmed === "") continue;
645
+ if (line.match(/^server:/)) {
646
+ currentSection = "server";
647
+ currentProject = null;
648
+ inPermissions = false;
649
+ continue;
650
+ }
651
+ if (line.match(/^defaults:/)) {
652
+ currentSection = "defaults";
653
+ currentProject = null;
654
+ inPermissions = false;
655
+ continue;
656
+ }
657
+ if (line.match(/^projects:/)) {
658
+ currentSection = "projects";
659
+ currentProject = null;
660
+ inPermissions = false;
661
+ continue;
662
+ }
663
+ if (currentSection === "server") {
664
+ const portMatch = trimmed.match(/^port:\s*(\d+)/);
665
+ if (portMatch?.[1]) config.server.port = parseInt(portMatch[1], 10);
666
+ const autoStartMatch = trimmed.match(/^autoStart:\s*(true|false)/);
667
+ if (autoStartMatch) config.server.autoStart = autoStartMatch[1] === "true";
668
+ }
669
+ if (currentSection === "defaults") {
670
+ const includeNewMatch = trimmed.match(/^includeNew:\s*(true|false)/);
671
+ if (includeNewMatch) config.defaults.includeNew = includeNewMatch[1] === "true";
672
+ if (trimmed === "permissions:") {
673
+ inPermissions = true;
674
+ continue;
675
+ }
676
+ if (inPermissions) {
677
+ const knowledgeMatch = trimmed.match(/^knowledge:\s*(true|false)/);
678
+ if (knowledgeMatch) config.defaults.permissions.knowledge = knowledgeMatch[1] === "true";
679
+ const tasksMatch = trimmed.match(/^tasks:\s*(true|false)/);
680
+ if (tasksMatch) config.defaults.permissions.tasks = tasksMatch[1] === "true";
681
+ const refsMatch = trimmed.match(/^refs:\s*(true|false)/);
682
+ if (refsMatch) config.defaults.permissions.refs = refsMatch[1] === "true";
683
+ }
413
684
  }
414
- } catch {
415
- }
416
- return projects;
417
- }
418
- function scanHomeDirectory(excludePath) {
419
- const home = process.env.HOME;
420
- if (!home) return [];
421
- const projects = [];
422
- const maxDepth = 5;
423
- function scanDir(dirPath, depth) {
424
- if (depth > maxDepth) return;
425
- try {
426
- const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
427
- for (const entry of entries) {
428
- if (!entry.isDirectory()) continue;
429
- const fullPath = path2.join(dirPath, entry.name);
430
- if (excludePath && fullPath === excludePath) continue;
431
- if (entry.name === ".rrce-workflow") {
432
- const configPath = path2.join(fullPath, "config.yaml");
433
- if (fs2.existsSync(configPath)) {
434
- const projectPath = dirPath;
435
- const projectName = path2.basename(projectPath);
436
- const config = parseWorkspaceConfig(configPath);
437
- const knowledgePath = path2.join(fullPath, "knowledge");
438
- const refsPath = path2.join(fullPath, "refs");
439
- const tasksPath = path2.join(fullPath, "tasks");
440
- projects.push({
441
- name: config?.name || projectName,
442
- path: projectPath,
443
- dataPath: fullPath,
444
- source: "local",
445
- storageMode: config?.storageMode,
446
- knowledgePath: fs2.existsSync(knowledgePath) ? knowledgePath : void 0,
447
- refsPath: fs2.existsSync(refsPath) ? refsPath : void 0,
448
- tasksPath: fs2.existsSync(tasksPath) ? tasksPath : void 0
449
- });
450
- }
451
- continue;
685
+ if (currentSection === "projects") {
686
+ const projectNameMatch = line.match(/^\s+-\s+name:\s*["']?([^"'\n]+)["']?/);
687
+ if (projectNameMatch) {
688
+ if (currentProject && currentProject.name) {
689
+ config.projects.push(currentProject);
452
690
  }
453
- if (SKIP_DIRECTORIES.has(entry.name)) continue;
454
- if (entry.name.startsWith(".") && entry.name !== ".rrce-workflow") continue;
455
- scanDir(fullPath, depth + 1);
691
+ currentProject = {
692
+ name: projectNameMatch[1].trim(),
693
+ expose: true,
694
+ permissions: { ...DEFAULT_PERMISSIONS }
695
+ };
696
+ inPermissions = false;
697
+ continue;
456
698
  }
457
- } catch {
458
- }
459
- }
460
- scanDir(home, 0);
461
- return projects;
462
- }
463
- function parseWorkspaceConfig(configPath) {
464
- try {
465
- const content = fs2.readFileSync(configPath, "utf-8");
466
- const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
467
- const modeMatch = content.match(/mode:\s*(global|workspace)/);
468
- const linkedProjects = [];
469
- const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
470
- if (linkedMatch && linkedMatch[1]) {
471
- const lines = linkedMatch[1].split("\n");
472
- for (const line of lines) {
473
- const projectMatch = line.match(/^\s+-\s+(.+)$/);
474
- if (projectMatch && projectMatch[1]) {
475
- linkedProjects.push(projectMatch[1].trim());
699
+ if (currentProject) {
700
+ const exposeMatch = trimmed.match(/^expose:\s*(true|false)/);
701
+ if (exposeMatch) {
702
+ currentProject.expose = exposeMatch[1] === "true";
703
+ }
704
+ if (trimmed === "permissions:") {
705
+ inPermissions = true;
706
+ continue;
707
+ }
708
+ if (inPermissions) {
709
+ const knowledgeMatch = trimmed.match(/^knowledge:\s*(true|false)/);
710
+ if (knowledgeMatch) currentProject.permissions.knowledge = knowledgeMatch[1] === "true";
711
+ const tasksMatch = trimmed.match(/^tasks:\s*(true|false)/);
712
+ if (tasksMatch) currentProject.permissions.tasks = tasksMatch[1] === "true";
713
+ const refsMatch = trimmed.match(/^refs:\s*(true|false)/);
714
+ if (refsMatch) currentProject.permissions.refs = refsMatch[1] === "true";
476
715
  }
477
716
  }
478
717
  }
479
- return {
480
- name: nameMatch?.[1]?.trim() || path2.basename(path2.dirname(path2.dirname(configPath))),
481
- storageMode: modeMatch?.[1] || "global",
482
- linkedProjects: linkedProjects.length > 0 ? linkedProjects : void 0
483
- };
484
- } catch {
485
- return null;
486
718
  }
719
+ if (currentProject && currentProject.name) {
720
+ config.projects.push(currentProject);
721
+ }
722
+ return config;
487
723
  }
724
+ function serializeMCPConfig(config) {
725
+ let content = `# RRCE MCP Hub Configuration
726
+ # Manages which projects are exposed via MCP
488
727
 
489
- // src/commands/wizard/setup-flow.ts
490
- init_paths();
491
- import { group, select as select2, multiselect, confirm, spinner, note as note2, outro, cancel } from "@clack/prompts";
492
- import pc3 from "picocolors";
493
- import * as fs7 from "fs";
494
- import * as path8 from "path";
495
-
496
- // src/lib/prompts.ts
497
- import * as fs3 from "fs";
498
- import * as path3 from "path";
499
- import { fileURLToPath } from "url";
500
- import matter from "gray-matter";
728
+ server:
729
+ port: ${config.server.port}
730
+ autoStart: ${config.server.autoStart}
501
731
 
502
- // src/types/prompt.ts
503
- import { z } from "zod";
504
- var PromptArgSchema = z.object({
505
- name: z.string(),
506
- default: z.string().optional(),
507
- prompt: z.string().optional()
508
- });
509
- var AutoIdentitySchema = z.object({
510
- user: z.string(),
511
- model: z.string()
512
- });
513
- var PromptFrontmatterSchema = z.object({
514
- name: z.string(),
515
- description: z.string(),
516
- "argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
517
- tools: z.array(z.string()).optional(),
518
- "required-args": z.array(PromptArgSchema).optional(),
519
- "optional-args": z.array(PromptArgSchema).optional(),
520
- "auto-identity": AutoIdentitySchema.optional()
521
- });
732
+ defaults:
733
+ includeNew: ${config.defaults.includeNew}
734
+ permissions:
735
+ knowledge: ${config.defaults.permissions.knowledge}
736
+ tasks: ${config.defaults.permissions.tasks}
737
+ refs: ${config.defaults.permissions.refs}
522
738
 
523
- // src/lib/prompts.ts
524
- var __filename = fileURLToPath(import.meta.url);
525
- var __dirname = path3.dirname(__filename);
526
- function parsePromptFile(filePath) {
527
- try {
528
- const fileContent = fs3.readFileSync(filePath, "utf-8");
529
- const { data, content } = matter(fileContent);
530
- const parsed = PromptFrontmatterSchema.safeParse(data);
531
- if (!parsed.success) {
532
- console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
533
- return null;
739
+ projects:
740
+ `;
741
+ if (config.projects.length === 0) {
742
+ content += ' # No projects configured yet. Run "rrce-workflow mcp" to add projects.\n';
743
+ } else {
744
+ for (const project of config.projects) {
745
+ content += ` - name: "${project.name}"
746
+ expose: ${project.expose}
747
+ permissions:
748
+ knowledge: ${project.permissions.knowledge}
749
+ tasks: ${project.permissions.tasks}
750
+ refs: ${project.permissions.refs}
751
+ `;
534
752
  }
535
- return {
536
- frontmatter: parsed.data,
537
- content: content.trim(),
538
- filePath
539
- };
540
- } catch (error) {
541
- console.error(`Error reading prompt file ${filePath}:`, error);
542
- return null;
543
753
  }
754
+ return content;
544
755
  }
545
- function loadPromptsFromDir(dirPath) {
546
- if (!fs3.existsSync(dirPath)) {
547
- return [];
548
- }
549
- const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
550
- const prompts = [];
551
- for (const file of files) {
552
- const prompt = parsePromptFile(path3.join(dirPath, file));
553
- if (prompt) {
554
- prompts.push(prompt);
756
+ function setProjectConfig(config, name, expose, permissions) {
757
+ const existing = config.projects.find((p) => p.name === name);
758
+ if (existing) {
759
+ existing.expose = expose;
760
+ if (permissions) {
761
+ existing.permissions = { ...existing.permissions, ...permissions };
555
762
  }
763
+ } else {
764
+ config.projects.push({
765
+ name,
766
+ expose,
767
+ permissions: permissions ? { ...DEFAULT_PERMISSIONS, ...permissions } : { ...DEFAULT_PERMISSIONS }
768
+ });
556
769
  }
557
- return prompts;
558
- }
559
- function getAgentCoreDir() {
560
- return path3.join(__dirname, "..", "agent-core");
561
- }
562
- function getAgentCorePromptsDir() {
563
- return path3.join(getAgentCoreDir(), "prompts");
770
+ return config;
564
771
  }
565
-
566
- // src/commands/wizard/utils.ts
567
- init_paths();
568
- import * as fs4 from "fs";
569
- import * as path4 from "path";
570
- function copyPromptsToDir(prompts, targetDir, extension) {
571
- for (const prompt of prompts) {
572
- const baseName = path4.basename(prompt.filePath, ".md");
573
- const targetName = baseName + extension;
574
- const targetPath = path4.join(targetDir, targetName);
575
- const content = fs4.readFileSync(prompt.filePath, "utf-8");
576
- fs4.writeFileSync(targetPath, content);
772
+ function isProjectExposed(config, name) {
773
+ const project = config.projects.find((p) => p.name === name);
774
+ if (project) {
775
+ return project.expose;
577
776
  }
777
+ return config.defaults.includeNew;
578
778
  }
579
- function copyDirRecursive(src, dest) {
580
- const entries = fs4.readdirSync(src, { withFileTypes: true });
581
- for (const entry of entries) {
582
- const srcPath = path4.join(src, entry.name);
583
- const destPath = path4.join(dest, entry.name);
584
- if (entry.isDirectory()) {
585
- ensureDir(destPath);
586
- copyDirRecursive(srcPath, destPath);
587
- } else {
588
- fs4.copyFileSync(srcPath, destPath);
589
- }
590
- }
779
+ function getProjectPermissions(config, name) {
780
+ const project = config.projects.find((p) => p.name === name);
781
+ return project?.permissions ?? config.defaults.permissions;
591
782
  }
783
+ var init_config = __esm({
784
+ "src/mcp/config.ts"() {
785
+ "use strict";
786
+ init_paths();
787
+ init_types();
788
+ }
789
+ });
592
790
 
593
- // src/commands/wizard/vscode.ts
594
- init_paths();
595
- import * as fs5 from "fs";
596
- import * as path5 from "path";
597
- function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
598
- const workspaceFilePath = path5.join(workspacePath, `${workspaceName}.code-workspace`);
599
- let workspace;
600
- if (fs5.existsSync(workspaceFilePath)) {
791
+ // src/mcp/logger.ts
792
+ var logger_exports = {};
793
+ __export(logger_exports, {
794
+ getLogFilePath: () => getLogFilePath,
795
+ logger: () => logger
796
+ });
797
+ import * as fs13 from "fs";
798
+ import * as path12 from "path";
799
+ function getLogFilePath() {
800
+ const workspaceRoot = detectWorkspaceRoot();
801
+ const rrceHome = getEffectiveRRCEHome(workspaceRoot);
802
+ return path12.join(rrceHome, "mcp-server.log");
803
+ }
804
+ var Logger, logger;
805
+ var init_logger = __esm({
806
+ "src/mcp/logger.ts"() {
807
+ "use strict";
808
+ init_paths();
809
+ Logger = class {
810
+ logPath;
811
+ constructor() {
812
+ this.logPath = getLogFilePath();
813
+ }
814
+ write(level, message, data) {
815
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
816
+ let logMessage = `[${timestamp}] [${level}] ${message}`;
817
+ if (data) {
818
+ if (data instanceof Error) {
819
+ logMessage += `
820
+ ${data.stack || data.message}`;
821
+ } else {
822
+ try {
823
+ logMessage += `
824
+ ${JSON.stringify(data, null, 2)}`;
825
+ } catch (e) {
826
+ logMessage += `
827
+ [Circular or invalid data]`;
828
+ }
829
+ }
830
+ }
831
+ logMessage += "\n";
832
+ try {
833
+ const dir = path12.dirname(this.logPath);
834
+ if (!fs13.existsSync(dir)) {
835
+ fs13.mkdirSync(dir, { recursive: true });
836
+ }
837
+ fs13.appendFileSync(this.logPath, logMessage);
838
+ } catch (e) {
839
+ console.error(`[Logger Failure] Could not write to ${this.logPath}`, e);
840
+ console.error(logMessage);
841
+ }
842
+ }
843
+ info(message, data) {
844
+ this.write("INFO", message, data);
845
+ }
846
+ error(message, error) {
847
+ this.write("ERROR", message, error);
848
+ }
849
+ warn(message, data) {
850
+ this.write("WARN", message, data);
851
+ }
852
+ debug(message, data) {
853
+ this.write("DEBUG", message, data);
854
+ }
855
+ };
856
+ logger = new Logger();
857
+ }
858
+ });
859
+
860
+ // src/mcp/resources.ts
861
+ import * as fs14 from "fs";
862
+ import * as path13 from "path";
863
+ function getExposedProjects() {
864
+ const config = loadMCPConfig();
865
+ const allProjects = scanForProjects();
866
+ return allProjects.filter((project) => isProjectExposed(config, project.name));
867
+ }
868
+ function detectActiveProject() {
869
+ const exposed = getExposedProjects();
870
+ const cwd = process.cwd();
871
+ const matches = exposed.filter((p) => cwd.startsWith(p.path));
872
+ matches.sort((a, b) => b.path.length - a.path.length);
873
+ return matches[0];
874
+ }
875
+ function getProjectContext(projectName) {
876
+ const config = loadMCPConfig();
877
+ if (!isProjectExposed(config, projectName)) {
878
+ return null;
879
+ }
880
+ const permissions = getProjectPermissions(config, projectName);
881
+ if (!permissions.knowledge) {
882
+ return null;
883
+ }
884
+ const projects = scanForProjects();
885
+ const project = projects.find((p) => p.name === projectName);
886
+ if (!project?.knowledgePath) {
887
+ return null;
888
+ }
889
+ const contextPath = path13.join(project.knowledgePath, "project-context.md");
890
+ if (!fs14.existsSync(contextPath)) {
891
+ return null;
892
+ }
893
+ return fs14.readFileSync(contextPath, "utf-8");
894
+ }
895
+ function getProjectTasks(projectName) {
896
+ const config = loadMCPConfig();
897
+ if (!isProjectExposed(config, projectName)) {
898
+ return [];
899
+ }
900
+ const permissions = getProjectPermissions(config, projectName);
901
+ if (!permissions.tasks) {
902
+ return [];
903
+ }
904
+ const projects = scanForProjects();
905
+ const project = projects.find((p) => p.name === projectName);
906
+ if (!project?.tasksPath || !fs14.existsSync(project.tasksPath)) {
907
+ return [];
908
+ }
909
+ const tasks = [];
910
+ try {
911
+ const taskDirs = fs14.readdirSync(project.tasksPath, { withFileTypes: true });
912
+ for (const dir of taskDirs) {
913
+ if (!dir.isDirectory()) continue;
914
+ const metaPath = path13.join(project.tasksPath, dir.name, "meta.json");
915
+ if (fs14.existsSync(metaPath)) {
916
+ try {
917
+ const meta = JSON.parse(fs14.readFileSync(metaPath, "utf-8"));
918
+ tasks.push(meta);
919
+ } catch {
920
+ }
921
+ }
922
+ }
923
+ } catch {
924
+ }
925
+ return tasks;
926
+ }
927
+ function searchKnowledge(query) {
928
+ const config = loadMCPConfig();
929
+ const projects = getExposedProjects();
930
+ const results = [];
931
+ const queryLower = query.toLowerCase();
932
+ for (const project of projects) {
933
+ const permissions = getProjectPermissions(config, project.name);
934
+ if (!permissions.knowledge || !project.knowledgePath) continue;
601
935
  try {
602
- const content = fs5.readFileSync(workspaceFilePath, "utf-8");
603
- workspace = JSON.parse(content);
936
+ const files = fs14.readdirSync(project.knowledgePath);
937
+ for (const file of files) {
938
+ if (!file.endsWith(".md")) continue;
939
+ const filePath = path13.join(project.knowledgePath, file);
940
+ const content = fs14.readFileSync(filePath, "utf-8");
941
+ const lines = content.split("\n");
942
+ const matches = [];
943
+ for (const line of lines) {
944
+ if (line.toLowerCase().includes(queryLower)) {
945
+ matches.push(line.trim());
946
+ }
947
+ }
948
+ if (matches.length > 0) {
949
+ results.push({
950
+ project: project.name,
951
+ file,
952
+ matches: matches.slice(0, 5)
953
+ // Limit to 5 matches per file
954
+ });
955
+ }
956
+ }
604
957
  } catch {
605
- workspace = { folders: [], settings: {} };
606
958
  }
607
- } else {
608
- workspace = { folders: [], settings: {} };
609
959
  }
610
- if (!workspace.settings) {
611
- workspace.settings = {};
960
+ return results;
961
+ }
962
+ var init_resources = __esm({
963
+ "src/mcp/resources.ts"() {
964
+ "use strict";
965
+ init_config();
966
+ init_detection();
612
967
  }
613
- workspace.folders = workspace.folders.filter(
614
- (f) => f.path === "." || !f.name?.startsWith("\u{1F4C1}") && !f.name?.startsWith("\u{1F4DA}") && !f.name?.startsWith("\u{1F4CE}") && !f.name?.startsWith("\u{1F4CB}")
615
- );
616
- const mainFolderIndex = workspace.folders.findIndex((f) => f.path === ".");
617
- if (mainFolderIndex === -1) {
618
- workspace.folders.unshift({
619
- path: ".",
620
- name: `\u{1F3E0} ${workspaceName} (workspace)`
968
+ });
969
+
970
+ // src/mcp/prompts.ts
971
+ function getAllPrompts() {
972
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
973
+ return prompts.map((p) => {
974
+ const args = [];
975
+ if (p.frontmatter["required-args"]) {
976
+ args.push(...p.frontmatter["required-args"].map((a) => ({
977
+ name: a.name,
978
+ description: a.prompt || a.name,
979
+ required: true
980
+ })));
981
+ }
982
+ if (p.frontmatter["optional-args"]) {
983
+ args.push(...p.frontmatter["optional-args"].map((a) => ({
984
+ name: a.name,
985
+ description: a.prompt || a.name,
986
+ required: false
987
+ })));
988
+ }
989
+ return {
990
+ name: p.frontmatter.name,
991
+ description: p.frontmatter.description,
992
+ arguments: args,
993
+ content: p.content
994
+ };
995
+ });
996
+ }
997
+ function getPromptDef(name) {
998
+ return getAllPrompts().find((p) => p.name === name);
999
+ }
1000
+ function renderPrompt(content, args) {
1001
+ let rendered = content;
1002
+ for (const [key, val] of Object.entries(args)) {
1003
+ rendered = rendered.replace(new RegExp(`{{${key}}}`, "g"), val);
1004
+ }
1005
+ return rendered;
1006
+ }
1007
+ var init_prompts2 = __esm({
1008
+ "src/mcp/prompts.ts"() {
1009
+ "use strict";
1010
+ init_prompts();
1011
+ }
1012
+ });
1013
+
1014
+ // src/mcp/server.ts
1015
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1016
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1017
+ import {
1018
+ CallToolRequestSchema,
1019
+ ListResourcesRequestSchema,
1020
+ ListToolsRequestSchema,
1021
+ ReadResourceRequestSchema,
1022
+ ListPromptsRequestSchema,
1023
+ GetPromptRequestSchema
1024
+ } from "@modelcontextprotocol/sdk/types.js";
1025
+ async function startMCPServer() {
1026
+ try {
1027
+ logger.info("Starting MCP Server...");
1028
+ process.on("uncaughtException", (error) => {
1029
+ logger.error("Uncaught Exception", error);
1030
+ console.error("Uncaught Exception:", error);
621
1031
  });
622
- } else {
623
- workspace.folders[mainFolderIndex] = {
624
- path: ".",
625
- name: `\u{1F3E0} ${workspaceName} (workspace)`
1032
+ process.on("unhandledRejection", (reason) => {
1033
+ logger.error("Unhandled Rejection", reason);
1034
+ console.error("Unhandled Rejection:", reason);
1035
+ });
1036
+ const config = loadMCPConfig();
1037
+ mcpServer = new Server(
1038
+ { name: "rrce-mcp-hub", version: "1.0.0" },
1039
+ { capabilities: { resources: {}, tools: {}, prompts: {} } }
1040
+ );
1041
+ mcpServer.onerror = (error) => {
1042
+ logger.error("MCP Server Error", error);
626
1043
  };
1044
+ registerResourceHandlers(mcpServer);
1045
+ registerToolHandlers(mcpServer);
1046
+ registerPromptHandlers(mcpServer);
1047
+ const transport = new StdioServerTransport();
1048
+ await mcpServer.connect(transport);
1049
+ serverState = { running: true, port: config.server.port, pid: process.pid };
1050
+ const exposed = getExposedProjects().map((p) => p.name).join(", ");
1051
+ logger.info(`RRCE MCP Hub started (pid: ${process.pid})`, { exposedProjects: exposed });
1052
+ console.error(`RRCE MCP Hub started (pid: ${process.pid})`);
1053
+ console.error(`Exposed projects: ${exposed}`);
1054
+ return { port: config.server.port, pid: process.pid };
1055
+ } catch (error) {
1056
+ logger.error("Failed to start MCP server", error);
1057
+ throw error;
627
1058
  }
628
- const referenceFolderPaths = [];
629
- const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
630
- if (isDetectedProjects) {
631
- const projects = linkedProjects;
1059
+ }
1060
+ function registerResourceHandlers(server) {
1061
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1062
+ logger.debug("Listing resources");
1063
+ const projects = getExposedProjects();
1064
+ const resources = [];
1065
+ resources.push({
1066
+ uri: "rrce://projects",
1067
+ name: "Project List",
1068
+ description: "List of all RRCE projects exposed via MCP",
1069
+ mimeType: "application/json"
1070
+ });
632
1071
  for (const project of projects) {
633
- const sourceLabel = project.source === "global" ? "global" : "local";
634
- const folderPath = project.dataPath;
635
- if (fs5.existsSync(folderPath)) {
636
- referenceFolderPaths.push(folderPath);
637
- workspace.folders.push({
638
- path: folderPath,
639
- name: `\u{1F4C1} ${project.name} [${sourceLabel}]`
1072
+ const config = loadMCPConfig();
1073
+ const permissions = getProjectPermissions(config, project.name);
1074
+ if (permissions.knowledge) {
1075
+ resources.push({
1076
+ uri: `rrce://projects/${project.name}/context`,
1077
+ name: `${project.name} - Project Context`,
1078
+ description: `Project context and architecture for ${project.name}`,
1079
+ mimeType: "text/markdown"
640
1080
  });
641
1081
  }
642
- }
643
- } else {
644
- const projectNames = linkedProjects;
645
- const rrceHome = customGlobalPath || getRRCEHome();
646
- for (const projectName of projectNames) {
647
- const folderPath = path5.join(rrceHome, "workspaces", projectName);
648
- if (fs5.existsSync(folderPath)) {
649
- referenceFolderPaths.push(folderPath);
650
- workspace.folders.push({
651
- path: folderPath,
652
- name: `\u{1F4C1} ${projectName} [global]`
1082
+ if (permissions.tasks) {
1083
+ resources.push({
1084
+ uri: `rrce://projects/${project.name}/tasks`,
1085
+ name: `${project.name} - Tasks`,
1086
+ description: `Task list and status for ${project.name}`,
1087
+ mimeType: "application/json"
653
1088
  });
654
1089
  }
655
1090
  }
656
- }
657
- if (referenceFolderPaths.length > 0) {
658
- const readonlyPatterns = {};
659
- for (const folderPath of referenceFolderPaths) {
660
- readonlyPatterns[`${folderPath}/**`] = true;
661
- }
662
- const existingReadonly = workspace.settings["files.readonlyInclude"] || {};
663
- const cleanedReadonly = {};
664
- for (const [pattern, value] of Object.entries(existingReadonly)) {
665
- if (!pattern.includes(".rrce-workflow") && !pattern.includes("rrce-workflow/workspaces")) {
666
- cleanedReadonly[pattern] = value;
1091
+ return { resources };
1092
+ });
1093
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1094
+ const { uri } = request.params;
1095
+ logger.info(`Reading resource: ${uri}`);
1096
+ try {
1097
+ if (uri === "rrce://projects") {
1098
+ const projects = getExposedProjects();
1099
+ return {
1100
+ contents: [{
1101
+ uri,
1102
+ mimeType: "application/json",
1103
+ text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1104
+ }]
1105
+ };
1106
+ }
1107
+ const projectMatch = uri.match(/^rrce:\/\/projects\/([^/]+)\/(.+)$/);
1108
+ if (projectMatch) {
1109
+ const [, projectName, resourceType] = projectMatch;
1110
+ const content = resourceType === "context" ? getProjectContext(projectName) : JSON.stringify(getProjectTasks(projectName), null, 2);
1111
+ if (content === null) throw new Error(`Resource not found: ${uri}`);
1112
+ return {
1113
+ contents: [{
1114
+ uri,
1115
+ mimeType: resourceType === "tasks" ? "application/json" : "text/markdown",
1116
+ text: content
1117
+ }]
1118
+ };
667
1119
  }
1120
+ throw new Error(`Unknown resource: ${uri}`);
1121
+ } catch (error) {
1122
+ logger.error(`Failed to read resource: ${uri}`, error);
1123
+ throw error;
668
1124
  }
669
- workspace.settings["files.readonlyInclude"] = {
670
- ...cleanedReadonly,
671
- ...readonlyPatterns
672
- };
673
- }
674
- fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
1125
+ });
675
1126
  }
676
-
677
- // src/commands/wizard/setup-flow.ts
678
- init_tui_utils();
679
- async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
680
- const s = spinner();
681
- const config = await group(
682
- {
683
- storageMode: () => select2({
684
- message: "Where should workflow data be stored?",
685
- options: [
686
- { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
687
- { value: "workspace", label: "Workspace (.rrce-workflow/)", hint: "Self-contained, version with repo" }
688
- ],
689
- initialValue: "global"
690
- }),
691
- tools: () => multiselect({
692
- message: "Which AI tools do you use?",
693
- options: [
694
- { value: "copilot", label: "GitHub Copilot", hint: "VSCode" },
695
- { value: "antigravity", label: "Antigravity IDE" }
696
- ],
697
- required: false
698
- }),
699
- linkedProjects: () => {
700
- if (existingProjects.length === 0) {
701
- return Promise.resolve([]);
1127
+ function registerToolHandlers(server) {
1128
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1129
+ tools: [
1130
+ {
1131
+ name: "search_knowledge",
1132
+ description: "Search across all exposed project knowledge bases",
1133
+ inputSchema: {
1134
+ type: "object",
1135
+ properties: { query: { type: "string", description: "Search query to find in knowledge files" } },
1136
+ required: ["query"]
702
1137
  }
703
- return multiselect({
704
- message: "Link knowledge from other projects?",
705
- options: existingProjects.map((project) => ({
706
- value: `${project.name}:${project.source}`,
707
- // Unique key
708
- label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
709
- hint: pc3.dim(
710
- project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
711
- )
712
- })),
713
- required: false
714
- });
715
1138
  },
716
- addToGitignore: () => confirm({
717
- message: "Add generated folders to .gitignore? (as comments - uncomment if needed)",
718
- initialValue: true
719
- }),
720
- confirm: () => confirm({
721
- message: "Create configuration?",
722
- initialValue: true
723
- })
724
- },
725
- {
726
- onCancel: () => {
727
- cancel("Setup process cancelled.");
728
- process.exit(0);
1139
+ {
1140
+ name: "list_projects",
1141
+ description: "List all projects exposed via MCP",
1142
+ inputSchema: { type: "object", properties: {} }
1143
+ },
1144
+ {
1145
+ name: "get_project_context",
1146
+ description: "Get the project context/architecture for a specific project",
1147
+ inputSchema: {
1148
+ type: "object",
1149
+ properties: { project: { type: "string", description: "Name of the project to get context for" } },
1150
+ required: ["project"]
1151
+ }
729
1152
  }
1153
+ ]
1154
+ }));
1155
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1156
+ const { name, arguments: args } = request.params;
1157
+ logger.info(`Calling tool: ${name}`, args);
1158
+ try {
1159
+ switch (name) {
1160
+ case "search_knowledge": {
1161
+ const results = searchKnowledge(args.query);
1162
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
1163
+ }
1164
+ case "list_projects": {
1165
+ const projects = getExposedProjects();
1166
+ return {
1167
+ content: [{
1168
+ type: "text",
1169
+ text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1170
+ }]
1171
+ };
1172
+ }
1173
+ case "get_project_context": {
1174
+ const context = getProjectContext(args.project);
1175
+ if (!context) {
1176
+ const msg = `No project context found for "${args.project}"`;
1177
+ logger.warn(msg);
1178
+ return { content: [{ type: "text", text: msg }], isError: true };
1179
+ }
1180
+ return { content: [{ type: "text", text: context }] };
1181
+ }
1182
+ default:
1183
+ throw new Error(`Unknown tool: ${name}`);
1184
+ }
1185
+ } catch (error) {
1186
+ logger.error(`Tool execution failed: ${name}`, error);
1187
+ throw error;
730
1188
  }
731
- );
732
- if (!config.confirm) {
733
- outro("Setup cancelled by user.");
734
- process.exit(0);
735
- }
736
- let customGlobalPath;
737
- if (config.storageMode === "global") {
738
- customGlobalPath = await resolveGlobalPath();
739
- if (!customGlobalPath) {
740
- cancel("Setup cancelled - no writable global path available.");
741
- process.exit(1);
742
- }
743
- }
744
- s.start("Generating configuration");
745
- try {
746
- await generateConfiguration({
747
- storageMode: config.storageMode,
748
- globalPath: customGlobalPath,
749
- tools: config.tools,
750
- linkedProjects: config.linkedProjects,
751
- addToGitignore: config.addToGitignore
752
- }, workspacePath, workspaceName, existingProjects);
753
- s.stop("Configuration generated");
754
- const dataPaths = getDataPaths(
755
- config.storageMode,
756
- workspaceName,
757
- workspacePath,
758
- customGlobalPath
759
- );
760
- const summary = [
761
- `Storage: ${config.storageMode}`
762
- ];
763
- if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
764
- summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
765
- }
766
- if (dataPaths.length > 0) {
767
- summary.push(`Data paths:`);
768
- dataPaths.forEach((p) => summary.push(` - ${p}`));
769
- }
770
- const selectedTools = config.tools;
771
- if (selectedTools.length > 0) {
772
- summary.push(`Tools: ${selectedTools.join(", ")}`);
773
- }
774
- const linkedProjects = config.linkedProjects;
775
- if (linkedProjects.length > 0) {
776
- summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
777
- summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
778
- }
779
- note2(summary.join("\n"), "Setup Summary");
780
- if (linkedProjects.length > 0) {
781
- outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
782
- } else {
783
- outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
784
- }
785
- } catch (error) {
786
- s.stop("Error occurred");
787
- cancel(`Failed to setup: ${error instanceof Error ? error.message : String(error)}`);
788
- process.exit(1);
789
- }
1189
+ });
790
1190
  }
791
- async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
792
- const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
793
- for (const dataPath of dataPaths) {
794
- ensureDir(dataPath);
795
- ensureDir(path8.join(dataPath, "knowledge"));
796
- ensureDir(path8.join(dataPath, "refs"));
797
- ensureDir(path8.join(dataPath, "tasks"));
798
- ensureDir(path8.join(dataPath, "templates"));
799
- }
800
- const agentCoreDir = getAgentCoreDir();
801
- syncMetadataToAll(agentCoreDir, dataPaths);
802
- copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
803
- const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
804
- if (config.tools.includes("copilot")) {
805
- const copilotPath = getAgentPromptPath(workspacePath, "copilot");
806
- ensureDir(copilotPath);
807
- copyPromptsToDir(prompts, copilotPath, ".agent.md");
808
- }
809
- if (config.tools.includes("antigravity")) {
810
- const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
811
- ensureDir(antigravityPath);
812
- copyPromptsToDir(prompts, antigravityPath, ".md");
813
- }
814
- const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
815
- ensureDir(path8.dirname(workspaceConfigPath));
816
- let configContent = `# RRCE-Workflow Configuration
817
- version: 1
818
-
819
- storage:
820
- mode: ${config.storageMode}`;
821
- if (config.globalPath && config.globalPath !== getDefaultRRCEHome()) {
822
- configContent += `
823
- globalPath: "${config.globalPath}"`;
824
- }
825
- configContent += `
826
-
827
- project:
828
- name: "${workspaceName}"
829
-
830
- tools:
831
- copilot: ${config.tools.includes("copilot")}
832
- antigravity: ${config.tools.includes("antigravity")}
1191
+ function registerPromptHandlers(server) {
1192
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
1193
+ logger.debug("Listing prompts");
1194
+ const prompts = getAllPrompts();
1195
+ return {
1196
+ prompts: prompts.map((p) => ({
1197
+ name: p.name,
1198
+ description: p.description,
1199
+ arguments: p.arguments.map((a) => ({
1200
+ name: a.name,
1201
+ description: a.description,
1202
+ required: a.required
1203
+ }))
1204
+ }))
1205
+ };
1206
+ });
1207
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1208
+ const { name, arguments: args } = request.params;
1209
+ logger.info(`Getting prompt: ${name}`, args);
1210
+ const promptDef = getPromptDef(name);
1211
+ if (!promptDef) {
1212
+ logger.error(`Prompt not found: ${name}`);
1213
+ throw new Error(`Prompt not found: ${name}`);
1214
+ }
1215
+ try {
1216
+ const providedArgs = args || {};
1217
+ const missingArgs = promptDef.arguments.filter((a) => a.required && !providedArgs[a.name]).map((a) => a.name);
1218
+ if (missingArgs.length > 0) {
1219
+ throw new Error(`Missing required arguments: ${missingArgs.join(", ")}`);
1220
+ }
1221
+ const renderArgs = {};
1222
+ for (const [key, val] of Object.entries(providedArgs)) {
1223
+ renderArgs[key] = String(val);
1224
+ }
1225
+ const content = renderPrompt(promptDef.content, renderArgs);
1226
+ const projects = getExposedProjects();
1227
+ const activeProject = detectActiveProject();
1228
+ const projectList = projects.map((p) => {
1229
+ const isActive = activeProject && p.dataPath === activeProject.dataPath;
1230
+ return `- ${p.name} (${p.source}) ${isActive ? "**[ACTIVE]**" : ""}`;
1231
+ }).join("\n");
1232
+ let contextPreamble = `
1233
+ Context - Available Projects (MCP Hub):
1234
+ ${projectList}
833
1235
  `;
834
- if (config.linkedProjects.length > 0) {
835
- configContent += `
836
- linked_projects:
1236
+ if (activeProject) {
1237
+ contextPreamble += `
1238
+ Current Active Workspace: ${activeProject.name} (${activeProject.path})
837
1239
  `;
838
- config.linkedProjects.forEach((name) => {
839
- configContent += ` - ${name}
1240
+ contextPreamble += `IMPORTANT: Treat '${activeProject.path}' as the {{WORKSPACE_ROOT}}. All relative path operations (file reads/writes) MUST be performed relative to this directory.
840
1241
  `;
841
- });
842
- }
843
- fs7.writeFileSync(workspaceConfigPath, configContent);
844
- if (config.addToGitignore) {
845
- updateGitignore(workspacePath, config.storageMode, config.tools);
846
- }
847
- if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
848
- const selectedProjects = allProjects.filter(
849
- (p) => config.linkedProjects.includes(`${p.name}:${p.source}`)
850
- );
851
- generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, config.globalPath);
1242
+ }
1243
+ contextPreamble += `
1244
+ Note: If the user's request refers to a project not listed here, ask them to expose it via 'rrce-workflow mcp configure'.
1245
+
1246
+ ---
1247
+ `;
1248
+ return {
1249
+ messages: [
1250
+ {
1251
+ role: "user",
1252
+ content: {
1253
+ type: "text",
1254
+ text: contextPreamble + content
1255
+ }
1256
+ }
1257
+ ]
1258
+ };
1259
+ } catch (error) {
1260
+ logger.error(`Failed to get prompt: ${name}`, error);
1261
+ throw error;
1262
+ }
1263
+ });
1264
+ }
1265
+ function stopMCPServer() {
1266
+ if (mcpServer) {
1267
+ logger.info("Stopping MCP Server...");
1268
+ mcpServer.close();
1269
+ mcpServer = null;
852
1270
  }
1271
+ serverState = { running: false };
1272
+ logger.info("RRCE MCP Hub stopped");
1273
+ console.error("RRCE MCP Hub stopped");
853
1274
  }
854
- function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
855
- const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
856
- const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
857
- switch (mode) {
858
- case "global":
859
- return [globalPath];
860
- case "workspace":
861
- return [workspacePath];
862
- default:
863
- return [globalPath];
1275
+ function getMCPServerStatus() {
1276
+ return { ...serverState };
1277
+ }
1278
+ var serverState, mcpServer;
1279
+ var init_server = __esm({
1280
+ "src/mcp/server.ts"() {
1281
+ "use strict";
1282
+ init_logger();
1283
+ init_config();
1284
+ init_resources();
1285
+ init_prompts2();
1286
+ init_prompts();
1287
+ serverState = { running: false };
1288
+ mcpServer = null;
864
1289
  }
1290
+ });
1291
+
1292
+ // src/mcp/install.ts
1293
+ import * as fs15 from "fs";
1294
+ import * as path14 from "path";
1295
+ import * as os from "os";
1296
+ function checkInstallStatus() {
1297
+ return {
1298
+ antigravity: checkConfigFile(ANTIGRAVITY_CONFIG),
1299
+ claude: checkConfigFile(CLAUDE_CONFIG)
1300
+ };
865
1301
  }
866
- function updateGitignore(workspacePath, storageMode, tools) {
867
- const gitignorePath = path8.join(workspacePath, ".gitignore");
868
- const entries = [];
869
- if (storageMode === "workspace") {
870
- entries.push(".rrce-workflow/");
1302
+ function checkConfigFile(configPath) {
1303
+ if (!fs15.existsSync(configPath)) return false;
1304
+ try {
1305
+ const content = JSON.parse(fs15.readFileSync(configPath, "utf-8"));
1306
+ return !!content.mcpServers?.["rrce"];
1307
+ } catch {
1308
+ return false;
871
1309
  }
872
- if (tools.includes("copilot")) {
873
- entries.push(".github/agents/");
1310
+ }
1311
+ function installToConfig(target) {
1312
+ const configPath = target === "antigravity" ? ANTIGRAVITY_CONFIG : CLAUDE_CONFIG;
1313
+ const dir = path14.dirname(configPath);
1314
+ if (!fs15.existsSync(dir)) {
1315
+ fs15.mkdirSync(dir, { recursive: true });
874
1316
  }
875
- if (tools.includes("antigravity")) {
876
- entries.push(".agent/");
1317
+ let config = { mcpServers: {} };
1318
+ if (fs15.existsSync(configPath)) {
1319
+ try {
1320
+ config = JSON.parse(fs15.readFileSync(configPath, "utf-8"));
1321
+ } catch {
1322
+ }
877
1323
  }
878
- if (entries.length === 0) {
1324
+ if (!config.mcpServers) config.mcpServers = {};
1325
+ config.mcpServers["rrce"] = {
1326
+ command: "npx",
1327
+ args: ["-y", "rrce-workflow", "mcp", "start"]
1328
+ // -y to avoid interactive prompts
1329
+ };
1330
+ try {
1331
+ fs15.writeFileSync(configPath, JSON.stringify(config, null, 2));
1332
+ return true;
1333
+ } catch {
879
1334
  return false;
880
1335
  }
881
- try {
882
- let content = "";
883
- if (fs7.existsSync(gitignorePath)) {
884
- content = fs7.readFileSync(gitignorePath, "utf-8");
1336
+ }
1337
+ var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG;
1338
+ var init_install = __esm({
1339
+ "src/mcp/install.ts"() {
1340
+ "use strict";
1341
+ ANTIGRAVITY_CONFIG = path14.join(os.homedir(), ".gemini/antigravity/mcp_config.json");
1342
+ CLAUDE_CONFIG = path14.join(os.homedir(), ".config/claude/claude_desktop_config.json");
1343
+ }
1344
+ });
1345
+
1346
+ // src/mcp/index.ts
1347
+ var mcp_exports = {};
1348
+ __export(mcp_exports, {
1349
+ runMCP: () => runMCP
1350
+ });
1351
+ import { intro as intro2, outro as outro6, select as select4, multiselect as multiselect3, confirm as confirm4, spinner as spinner6, note as note7, cancel as cancel6, isCancel as isCancel7, text as text2 } from "@clack/prompts";
1352
+ import pc8 from "picocolors";
1353
+ async function runMCP(subcommand2) {
1354
+ if (subcommand2) {
1355
+ switch (subcommand2) {
1356
+ case "start":
1357
+ await startMCPServer();
1358
+ await new Promise(() => {
1359
+ });
1360
+ return;
1361
+ case "stop":
1362
+ await handleStopServer();
1363
+ return;
1364
+ case "status":
1365
+ await handleShowStatus();
1366
+ return;
1367
+ case "help":
1368
+ showHelp();
1369
+ return;
885
1370
  }
886
- const lines = content.split("\n").map((line) => line.trim());
887
- const newEntries = [];
888
- for (const entry of entries) {
889
- const entryWithoutSlash = entry.replace(/\/$/, "");
890
- const commentedEntry = `# ${entry}`;
891
- const commentedEntryNoSlash = `# ${entryWithoutSlash}`;
892
- if (!lines.some(
893
- (line) => line === entry || line === entryWithoutSlash || line === commentedEntry || line === commentedEntryNoSlash
894
- )) {
895
- newEntries.push(entry);
896
- }
1371
+ }
1372
+ intro2(pc8.bgCyan(pc8.black(" RRCE MCP Hub ")));
1373
+ const globalPathCheck = await ensureMCPGlobalPath();
1374
+ if (!globalPathCheck.configured) {
1375
+ const configured = await handleConfigureGlobalPath();
1376
+ if (!configured) {
1377
+ outro6(pc8.yellow("MCP requires a global storage path. Setup cancelled."));
1378
+ return;
897
1379
  }
898
- if (newEntries.length === 0) {
899
- return false;
1380
+ }
1381
+ const status = checkInstallStatus();
1382
+ if (!status.antigravity && !status.claude) {
1383
+ const shouldInstall = await confirm4({
1384
+ message: "MCP server is not installed in your IDEs. Install now?",
1385
+ initialValue: true
1386
+ });
1387
+ if (shouldInstall && !isCancel7(shouldInstall)) {
1388
+ await handleInstallWizard();
900
1389
  }
901
- let newContent = content;
902
- if (!newContent.endsWith("\n") && newContent !== "") {
903
- newContent += "\n";
1390
+ }
1391
+ let running = true;
1392
+ while (running) {
1393
+ const serverStatus = getMCPServerStatus();
1394
+ const serverLabel = serverStatus.running ? pc8.green("Running") : pc8.dim("Stopped");
1395
+ const action = await select4({
1396
+ message: "What would you like to do?",
1397
+ options: [
1398
+ { value: "start", label: `\u25B6\uFE0F Start MCP server`, hint: `Current status: ${serverLabel}` },
1399
+ { value: "configure", label: "\u2699\uFE0F Configure projects", hint: "Choose which projects to expose" },
1400
+ { value: "install", label: "\u{1F4E5} Install to IDE", hint: "Add to Antigravity or Claude Desktop" },
1401
+ { value: "status", label: "\u{1F4CB} View status", hint: "See details" },
1402
+ { value: "help", label: "\u2753 Help", hint: "Learn about MCP Hub" },
1403
+ { value: "exit", label: "\u21A9 Exit", hint: "Return to shell" }
1404
+ ]
1405
+ });
1406
+ if (isCancel7(action)) {
1407
+ cancel6("MCP Hub closed.");
1408
+ return;
904
1409
  }
905
- if (newContent === "" || !content.includes("# rrce-workflow")) {
906
- newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
1410
+ switch (action) {
1411
+ case "start":
1412
+ await handleStartServer();
1413
+ break;
1414
+ case "configure":
1415
+ await handleConfigure();
1416
+ break;
1417
+ case "install":
1418
+ await handleInstallWizard();
1419
+ break;
1420
+ case "status":
1421
+ await handleShowStatus();
1422
+ break;
1423
+ case "help":
1424
+ showHelp();
1425
+ break;
1426
+ case "exit":
1427
+ running = false;
1428
+ break;
907
1429
  }
908
- newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
909
- fs7.writeFileSync(gitignorePath, newContent);
910
- return true;
911
- } catch {
912
- return false;
913
1430
  }
1431
+ outro6(pc8.green("MCP Hub closed."));
914
1432
  }
915
-
916
- // src/commands/wizard/link-flow.ts
917
- init_paths();
918
- import { multiselect as multiselect2, spinner as spinner2, note as note3, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
919
- import pc4 from "picocolors";
920
- import * as fs8 from "fs";
921
- async function runLinkProjectsFlow(workspacePath, workspaceName) {
922
- const projects = scanForProjects({
923
- excludeWorkspace: workspaceName,
924
- workspacePath
1433
+ async function handleInstallWizard() {
1434
+ const status = checkInstallStatus();
1435
+ const options = [
1436
+ { value: "antigravity", label: "Antigravity IDE", hint: status.antigravity ? "Installed" : "Not installed" },
1437
+ { value: "claude", label: "Claude Desktop", hint: status.claude ? "Installed" : "Not installed" }
1438
+ ];
1439
+ const selected = await multiselect3({
1440
+ message: "Select where to install RRCE MCP Server:",
1441
+ options,
1442
+ initialValues: [
1443
+ ...status.antigravity ? ["antigravity"] : [],
1444
+ ...status.claude ? ["claude"] : []
1445
+ ],
1446
+ required: false
925
1447
  });
926
- if (projects.length === 0) {
927
- outro2(pc4.yellow("No other projects found. Try setting up another project first."));
928
- return;
1448
+ if (isCancel7(selected)) return;
1449
+ const targets = selected;
1450
+ const results = [];
1451
+ for (const target of targets) {
1452
+ const success = installToConfig(target);
1453
+ results.push(`${target}: ${success ? pc8.green("Success") : pc8.red("Failed")}`);
929
1454
  }
930
- const customGlobalPath = getEffectiveRRCEHome(workspacePath);
931
- const linkedProjects = await multiselect2({
932
- message: "Select projects to link:",
933
- options: projects.map((project) => ({
934
- value: `${project.name}:${project.source}`,
935
- // Unique key
936
- label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
937
- hint: pc4.dim(
938
- project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
939
- )
940
- })),
941
- required: true
942
- });
943
- if (isCancel3(linkedProjects)) {
944
- cancel2("Cancelled.");
945
- process.exit(0);
1455
+ if (results.length > 0) {
1456
+ note7(results.join("\n"), "Installation Results");
946
1457
  }
947
- const selectedKeys = linkedProjects;
948
- if (selectedKeys.length === 0) {
949
- outro2("No projects selected.");
950
- return;
1458
+ }
1459
+ async function handleStartServer() {
1460
+ const fs16 = await import("fs");
1461
+ const { getLogFilePath: getLogFilePath2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1462
+ const config = loadMCPConfig();
1463
+ const exposedCount = config.projects.filter((p) => p.expose).length;
1464
+ if (exposedCount === 0 && !config.defaults.includeNew) {
1465
+ const shouldConfig = await confirm4({
1466
+ message: "No projects are currently exposed. Configure now?",
1467
+ initialValue: true
1468
+ });
1469
+ if (shouldConfig && !isCancel7(shouldConfig)) {
1470
+ await handleConfigure();
1471
+ }
951
1472
  }
952
- const selectedProjects = projects.filter(
953
- (p) => selectedKeys.includes(`${p.name}:${p.source}`)
954
- );
955
- const s = spinner2();
956
- s.start("Linking projects");
957
- const configFilePath = getConfigPath(workspacePath);
958
- let configContent = fs8.readFileSync(configFilePath, "utf-8");
959
- if (configContent.includes("linked_projects:")) {
960
- const lines = configContent.split("\n");
961
- const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
962
- if (linkedIndex !== -1) {
963
- let insertIndex = linkedIndex + 1;
964
- while (insertIndex < lines.length && lines[insertIndex]?.startsWith(" - ")) {
965
- insertIndex++;
966
- }
967
- for (const project of selectedProjects) {
968
- const entry = ` - ${project.name}:${project.source}`;
969
- if (!configContent.includes(entry)) {
970
- lines.splice(insertIndex, 0, entry);
971
- insertIndex++;
972
- }
973
- }
974
- configContent = lines.join("\n");
1473
+ const portInput = await text2({
1474
+ message: "Select port for MCP Server",
1475
+ initialValue: config.server.port.toString(),
1476
+ placeholder: "3200",
1477
+ validate(value) {
1478
+ if (isNaN(Number(value))) return "Port must be a number";
975
1479
  }
976
- } else {
977
- configContent += `
978
- linked_projects:
979
- `;
980
- selectedProjects.forEach((project) => {
981
- configContent += ` - ${project.name}:${project.source}
982
- `;
1480
+ });
1481
+ if (isCancel7(portInput)) return;
1482
+ const newPort = parseInt(portInput, 10);
1483
+ if (newPort !== config.server.port) {
1484
+ config.server.port = newPort;
1485
+ saveMCPConfig(config);
1486
+ }
1487
+ console.clear();
1488
+ const logPath = getLogFilePath2();
1489
+ const renderHeader = () => {
1490
+ console.clear();
1491
+ console.log(pc8.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1492
+ console.log(pc8.cyan("\u2551 RRCE MCP Hub Running \u2551"));
1493
+ console.log(pc8.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1494
+ console.log(pc8.dim("Server is running in Stdio mode (JSON-RPC)."));
1495
+ console.log(pc8.dim(`Port: ${newPort} | PID: ${process.pid}`));
1496
+ console.log(pc8.dim(`Logging to: ${logPath}`));
1497
+ console.log(pc8.dim("---------------------------------------------"));
1498
+ };
1499
+ const renderFooter = () => {
1500
+ console.log(pc8.dim("---------------------------------------------"));
1501
+ console.log(pc8.bgBlue(pc8.white(pc8.bold(" COMMANDS "))));
1502
+ console.log(`${pc8.bold("q")}: Stop & Quit ${pc8.bold("g")}: Configure Projects ${pc8.bold("p")}: Change Port`);
1503
+ };
1504
+ renderHeader();
1505
+ renderFooter();
1506
+ try {
1507
+ await startMCPServer();
1508
+ let lastSize = 0;
1509
+ if (fs16.existsSync(logPath)) {
1510
+ const stats = fs16.statSync(logPath);
1511
+ lastSize = stats.size;
1512
+ }
1513
+ let isRunning = true;
1514
+ if (process.stdin.setRawMode) {
1515
+ process.stdin.setRawMode(true);
1516
+ process.stdin.resume();
1517
+ process.stdin.setEncoding("utf8");
1518
+ }
1519
+ return new Promise((resolve) => {
1520
+ const onKey = async (key) => {
1521
+ if (key === "" || key.toLowerCase() === "q") {
1522
+ cleanup();
1523
+ resolve();
1524
+ }
1525
+ if (key.toLowerCase() === "p" || key.toLowerCase() === "g") {
1526
+ cleanup();
1527
+ resolve();
1528
+ }
1529
+ };
1530
+ process.stdin.on("data", onKey);
1531
+ const interval = setInterval(() => {
1532
+ if (!isRunning) return;
1533
+ if (fs16.existsSync(logPath)) {
1534
+ const stats = fs16.statSync(logPath);
1535
+ if (stats.size > lastSize) {
1536
+ const stream = fs16.createReadStream(logPath, {
1537
+ start: lastSize,
1538
+ end: stats.size,
1539
+ encoding: "utf-8"
1540
+ });
1541
+ stream.on("data", (chunk) => {
1542
+ process.stderr.write(chunk);
1543
+ });
1544
+ lastSize = stats.size;
1545
+ }
1546
+ }
1547
+ }, 500);
1548
+ const cleanup = () => {
1549
+ isRunning = false;
1550
+ clearInterval(interval);
1551
+ if (process.stdin.setRawMode) {
1552
+ process.stdin.setRawMode(false);
1553
+ }
1554
+ process.stdin.removeListener("data", onKey);
1555
+ process.stdin.pause();
1556
+ stopMCPServer();
1557
+ console.log("");
1558
+ };
983
1559
  });
1560
+ } catch (error) {
1561
+ if (process.stdin.setRawMode) {
1562
+ process.stdin.setRawMode(false);
1563
+ }
1564
+ console.error(pc8.red("\nFailed to start server:"));
1565
+ console.error(error);
984
1566
  }
985
- fs8.writeFileSync(configFilePath, configContent);
986
- generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
987
- s.stop("Projects linked");
988
- const workspaceFile = `${workspaceName}.code-workspace`;
989
- const summary = [
990
- `Linked projects:`,
991
- ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
992
- ``,
993
- `Workspace file: ${pc4.cyan(workspaceFile)}`
994
- ];
995
- note3(summary.join("\n"), "Link Summary");
996
- outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
997
1567
  }
1568
+ async function handleConfigureGlobalPath() {
1569
+ const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
1570
+ const fs16 = await import("fs");
1571
+ const path16 = await import("path");
1572
+ note7(
1573
+ `MCP Hub requires a ${pc8.bold("global storage path")} to store its configuration
1574
+ and coordinate across projects.
998
1575
 
999
- // src/commands/wizard/sync-flow.ts
1000
- init_paths();
1001
- import { confirm as confirm2, spinner as spinner3, note as note4, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
1002
- import pc5 from "picocolors";
1003
- import * as fs9 from "fs";
1004
- import * as path9 from "path";
1005
- async function runSyncToGlobalFlow(workspacePath, workspaceName) {
1006
- const localPath = getLocalWorkspacePath(workspacePath);
1007
- const customGlobalPath = getEffectiveRRCEHome(workspacePath);
1008
- const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
1009
- const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
1010
- const existingDirs = subdirs.filter(
1011
- (dir) => fs9.existsSync(path9.join(localPath, dir))
1576
+ Your current setup uses ${pc8.cyan("workspace")} mode, which stores data
1577
+ locally in each project. MCP needs a central location.`,
1578
+ "Global Path Required"
1012
1579
  );
1013
- if (existingDirs.length === 0) {
1014
- outro3(pc5.yellow("No data found in workspace storage to sync."));
1015
- return;
1580
+ const resolvedPath = await resolveGlobalPath2();
1581
+ if (!resolvedPath) {
1582
+ return false;
1016
1583
  }
1017
- note4(
1018
- `The following will be copied to global storage:
1019
- ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
1584
+ try {
1585
+ if (!fs16.existsSync(resolvedPath)) {
1586
+ fs16.mkdirSync(resolvedPath, { recursive: true });
1587
+ }
1588
+ const config = loadMCPConfig();
1589
+ saveMCPConfig(config);
1590
+ note7(
1591
+ `${pc8.green("\u2713")} Global path configured: ${pc8.cyan(resolvedPath)}
1020
1592
 
1021
- Destination: ${pc5.cyan(globalPath)}`,
1022
- "Sync Preview"
1023
- );
1024
- const shouldSync = await confirm2({
1025
- message: "Proceed with sync to global storage?",
1026
- initialValue: true
1027
- });
1028
- if (isCancel4(shouldSync) || !shouldSync) {
1029
- outro3("Sync cancelled.");
1593
+ MCP config will be stored at:
1594
+ ${path16.join(resolvedPath, "mcp.yaml")}`,
1595
+ "Configuration Saved"
1596
+ );
1597
+ return true;
1598
+ } catch (error) {
1599
+ note7(
1600
+ `${pc8.red("\u2717")} Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,
1601
+ "Error"
1602
+ );
1603
+ return false;
1604
+ }
1605
+ }
1606
+ async function handleShowStatus() {
1607
+ const s = spinner6();
1608
+ s.start("Loading projects...");
1609
+ const config = loadMCPConfig();
1610
+ const projects = scanForProjects();
1611
+ s.stop("Projects loaded");
1612
+ if (projects.length === 0) {
1613
+ note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1030
1614
  return;
1031
1615
  }
1032
- const s = spinner3();
1033
- s.start("Syncing to global storage");
1034
- try {
1035
- ensureDir(globalPath);
1036
- for (const dir of existingDirs) {
1037
- const srcDir = path9.join(localPath, dir);
1038
- const destDir = path9.join(globalPath, dir);
1039
- ensureDir(destDir);
1040
- copyDirRecursive(srcDir, destDir);
1041
- }
1042
- s.stop("Sync complete");
1043
- const summary = [
1044
- `Synced directories:`,
1045
- ...existingDirs.map((d) => ` \u2713 ${d}/`),
1046
- ``,
1047
- `Global path: ${pc5.cyan(globalPath)}`,
1048
- ``,
1049
- `Other projects can now link this knowledge!`
1050
- ];
1051
- note4(summary.join("\n"), "Sync Summary");
1052
- outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
1053
- } catch (error) {
1054
- s.stop("Error occurred");
1055
- cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
1056
- process.exit(1);
1616
+ const lines = [
1617
+ `${pc8.bold("Project Status")}`,
1618
+ ""
1619
+ ];
1620
+ for (const project of projects) {
1621
+ const projectConfig = config.projects.find((p) => p.name === project.name);
1622
+ const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1623
+ const status = isExposed ? pc8.green("\u2713 exposed") : pc8.dim("\u25CB hidden");
1624
+ const source = pc8.dim(`(${project.source})`);
1625
+ lines.push(` ${status} ${project.name} ${source}`);
1057
1626
  }
1058
- }
1059
-
1060
- // src/commands/wizard/update-flow.ts
1061
- init_paths();
1062
- import { confirm as confirm3, spinner as spinner4, note as note5, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
1063
- import pc6 from "picocolors";
1064
- import * as fs10 from "fs";
1065
- import * as path10 from "path";
1066
- async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
1067
- const s = spinner4();
1068
- s.start("Checking for updates");
1069
- try {
1070
- const agentCoreDir = getAgentCoreDir();
1071
- const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1072
- const mode = currentStorageMode || "global";
1073
- const customGlobalPath = getEffectiveRRCEHome(workspacePath);
1074
- const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
1075
- s.stop("Updates found");
1076
- note5(
1077
- `The following will be updated from the package:
1078
- \u2022 prompts/ (${prompts.length} agent prompts)
1079
- \u2022 templates/ (output templates)
1080
-
1081
- Target locations:
1082
- ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1083
- "Update Preview"
1084
- );
1085
- const shouldUpdate = await confirm3({
1086
- message: "Proceed with update?",
1087
- initialValue: true
1088
- });
1089
- if (isCancel5(shouldUpdate) || !shouldUpdate) {
1090
- outro4("Update cancelled.");
1091
- return;
1092
- }
1093
- s.start("Updating from package");
1094
- for (const dataPath of dataPaths) {
1095
- copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
1096
- }
1097
- const configFilePath = getConfigPath(workspacePath);
1098
- const configContent = fs10.readFileSync(configFilePath, "utf-8");
1099
- if (configContent.includes("copilot: true")) {
1100
- const copilotPath = getAgentPromptPath(workspacePath, "copilot");
1101
- ensureDir(copilotPath);
1102
- copyPromptsToDir(prompts, copilotPath, ".agent.md");
1103
- }
1104
- if (configContent.includes("antigravity: true")) {
1105
- const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
1106
- ensureDir(antigravityPath);
1107
- copyPromptsToDir(prompts, antigravityPath, ".md");
1108
- }
1109
- s.stop("Update complete");
1110
- const summary = [
1111
- `Updated:`,
1112
- ` \u2713 ${prompts.length} agent prompts`,
1113
- ` \u2713 Output templates`,
1114
- ``,
1115
- `Your configuration and knowledge files were preserved.`
1116
- ];
1117
- note5(summary.join("\n"), "Update Summary");
1118
- outro4(pc6.green("\u2713 Successfully updated from package!"));
1119
- } catch (error) {
1120
- s.stop("Error occurred");
1121
- cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
1122
- process.exit(1);
1627
+ lines.push("");
1628
+ lines.push(pc8.dim(`Config: ${getMCPConfigPath()}`));
1629
+ const serverStatus = getMCPServerStatus();
1630
+ if (serverStatus.running) {
1631
+ lines.push(pc8.green(`Server: running on port ${serverStatus.port}`));
1632
+ } else {
1633
+ lines.push(pc8.dim("Server: not running"));
1123
1634
  }
1635
+ note7(lines.join("\n"), "MCP Hub Status");
1124
1636
  }
1125
- function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
1126
- const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
1127
- const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
1128
- switch (mode) {
1129
- case "global":
1130
- return [globalPath];
1131
- case "workspace":
1132
- return [workspacePath];
1133
- default:
1134
- return [globalPath];
1637
+ async function handleConfigure() {
1638
+ const { logger: logger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1639
+ const s = spinner6();
1640
+ s.start("Scanning for projects...");
1641
+ const config = loadMCPConfig();
1642
+ const projects = scanForProjects();
1643
+ logger2.info("Configure: Loaded config", { projects: config.projects, defaultMode: config.defaults.includeNew });
1644
+ s.stop("Projects found");
1645
+ if (projects.length === 0) {
1646
+ note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1647
+ return;
1135
1648
  }
1136
- }
1137
-
1138
- // src/commands/wizard/index.ts
1139
- async function runWizard() {
1140
- intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
1141
- const s = spinner5();
1142
- s.start("Detecting environment");
1143
- const workspacePath = detectWorkspaceRoot();
1144
- const workspaceName = getWorkspaceName(workspacePath);
1145
- const gitUser = getGitUser();
1146
- await new Promise((r) => setTimeout(r, 800));
1147
- s.stop("Environment detected");
1148
- note6(
1149
- `Git User: ${pc7.bold(gitUser || "(not found)")}
1150
- Workspace: ${pc7.bold(workspaceName)}`,
1151
- "Context"
1152
- );
1153
- const detectedProjects = scanForProjects({
1154
- excludeWorkspace: workspaceName,
1155
- workspacePath
1649
+ const options = projects.map((project) => {
1650
+ const projectConfig = config.projects.find((p) => p.name === project.name);
1651
+ const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1652
+ return {
1653
+ value: project.name,
1654
+ label: `${project.name} ${pc8.dim(`(${project.source})`)}`,
1655
+ hint: project.dataPath
1656
+ };
1156
1657
  });
1157
- const configFilePath = getConfigPath(workspacePath);
1158
- const isAlreadyConfigured = fs11.existsSync(configFilePath);
1159
- let currentStorageMode = null;
1160
- if (isAlreadyConfigured) {
1161
- try {
1162
- const configContent = fs11.readFileSync(configFilePath, "utf-8");
1163
- const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
1164
- currentStorageMode = modeMatch?.[1] ?? null;
1165
- } catch {
1166
- }
1658
+ const currentlyExposed = projects.filter((p) => {
1659
+ const cfg = config.projects.find((c) => c.name === p.name);
1660
+ return cfg?.expose ?? config.defaults.includeNew;
1661
+ }).map((p) => p.name);
1662
+ const selected = await multiselect3({
1663
+ message: "Select projects to expose via MCP:",
1664
+ options,
1665
+ initialValues: currentlyExposed,
1666
+ required: false
1667
+ });
1668
+ if (isCancel7(selected)) {
1669
+ return;
1167
1670
  }
1168
- const localDataPath = getLocalWorkspacePath(workspacePath);
1169
- const hasLocalData = fs11.existsSync(localDataPath);
1170
- if (isAlreadyConfigured) {
1171
- const menuOptions = [];
1172
- if (detectedProjects.length > 0) {
1173
- menuOptions.push({
1174
- value: "link",
1175
- label: "Link other project knowledge",
1176
- hint: `${detectedProjects.length} projects detected (global + sibling)`
1177
- });
1178
- }
1179
- if (currentStorageMode === "workspace" && hasLocalData) {
1180
- menuOptions.push({
1181
- value: "sync-global",
1182
- label: "Sync to global storage",
1183
- hint: "Share knowledge with other projects"
1184
- });
1185
- }
1186
- menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
1187
- menuOptions.push({ value: "exit", label: "Exit" });
1188
- const action = await select3({
1189
- message: "This workspace is already configured. What would you like to do?",
1190
- options: menuOptions
1191
- });
1192
- if (isCancel6(action) || action === "exit") {
1193
- outro5("Exited.");
1194
- process.exit(0);
1195
- }
1196
- if (action === "link") {
1197
- await runLinkProjectsFlow(workspacePath, workspaceName);
1198
- return;
1199
- }
1200
- if (action === "sync-global") {
1201
- await runSyncToGlobalFlow(workspacePath, workspaceName);
1202
- return;
1203
- }
1204
- if (action === "update") {
1205
- await runUpdateFlow(workspacePath, workspaceName, currentStorageMode);
1206
- return;
1207
- }
1671
+ const selectedNames = selected;
1672
+ logger2.info("Configure: User selected projects", selectedNames);
1673
+ for (const project of projects) {
1674
+ const shouldExpose = selectedNames.includes(project.name);
1675
+ setProjectConfig(config, project.name, shouldExpose);
1208
1676
  }
1209
- await runSetupFlow(workspacePath, workspaceName, detectedProjects);
1210
- }
1677
+ saveMCPConfig(config);
1678
+ logger2.info("Configure: Config saved", config);
1679
+ const exposedCount = selectedNames.length;
1680
+ note7(
1681
+ `${pc8.green("\u2713")} Configuration saved!
1211
1682
 
1212
- // src/commands/selector.ts
1213
- import { intro as intro2, select as select4, note as note7, cancel as cancel6, isCancel as isCancel7, outro as outro6 } from "@clack/prompts";
1214
- import pc8 from "picocolors";
1215
- import * as path11 from "path";
1216
- async function runSelector() {
1217
- const workspaceName = path11.basename(process.cwd());
1218
- intro2(pc8.cyan(pc8.inverse(` RRCE-Workflow | ${workspaceName} `)));
1219
- const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1220
- if (prompts.length === 0) {
1221
- cancel6("No agents found. Run `rrce-workflow` to set up.");
1222
- process.exit(0);
1683
+ Exposed projects: ${exposedCount}
1684
+ Hidden projects: ${projects.length - exposedCount}`,
1685
+ "Configuration Updated"
1686
+ );
1687
+ }
1688
+ async function handleStopServer() {
1689
+ const status = getMCPServerStatus();
1690
+ if (!status.running) {
1691
+ note7("MCP server is not running.", "Status");
1692
+ return;
1223
1693
  }
1224
- const selection = await select4({
1225
- message: "Select an agent:",
1226
- options: prompts.map((p) => ({
1227
- value: p,
1228
- label: p.frontmatter.name,
1229
- hint: p.frontmatter.description
1230
- }))
1694
+ const confirmed = await confirm4({
1695
+ message: "Stop the MCP server?",
1696
+ initialValue: true
1231
1697
  });
1232
- if (isCancel7(selection)) {
1233
- cancel6("Selection cancelled.");
1234
- process.exit(0);
1698
+ if (isCancel7(confirmed) || !confirmed) {
1699
+ return;
1235
1700
  }
1236
- const prompt = selection;
1237
- note7(
1238
- `Use this agent in your IDE by invoking:
1239
- ${pc8.bold(pc8.cyan(`@${prompt.frontmatter.name}`))}`,
1240
- "Agent Selected"
1241
- );
1242
- outro6("Done");
1701
+ stopMCPServer();
1702
+ note7(pc8.green("MCP server stopped."), "Server Stopped");
1243
1703
  }
1704
+ function showHelp() {
1705
+ const help = `
1706
+ ${pc8.bold("RRCE MCP Hub")} - Cross-project AI assistant server
1244
1707
 
1245
- // src/mcp/index.ts
1246
- import { intro as intro3, outro as outro7, select as select5, multiselect as multiselect3, confirm as confirm4, spinner as spinner6, note as note8, cancel as cancel7, isCancel as isCancel8 } from "@clack/prompts";
1247
- import pc9 from "picocolors";
1708
+ ${pc8.bold("ABOUT")}
1709
+ MCP (Model Context Protocol) allows AI assistants like Claude to
1710
+ access your project knowledge in real-time. The RRCE MCP Hub
1711
+ provides a central server that exposes selected projects.
1248
1712
 
1249
- // src/mcp/config.ts
1250
- init_paths();
1251
- import * as fs12 from "fs";
1252
- import * as path12 from "path";
1713
+ ${pc8.bold("MENU OPTIONS")}
1714
+ ${pc8.cyan("View project status")} See which projects are exposed
1715
+ ${pc8.cyan("Configure projects")} Choose which projects to expose
1716
+ ${pc8.cyan("Start MCP server")} Start the server for AI access
1717
+ ${pc8.cyan("Stop MCP server")} Stop the running server
1253
1718
 
1254
- // src/mcp/types.ts
1255
- var DEFAULT_MCP_CONFIG = {
1256
- server: {
1257
- port: 3e3,
1258
- autoStart: false
1259
- },
1260
- projects: [],
1261
- defaults: {
1262
- includeNew: true,
1263
- permissions: {
1264
- knowledge: true,
1265
- tasks: true,
1266
- refs: true
1267
- }
1268
- }
1269
- };
1270
- var DEFAULT_PERMISSIONS = {
1271
- knowledge: true,
1272
- tasks: true,
1273
- refs: true
1274
- };
1719
+ ${pc8.bold("DIRECT COMMANDS")}
1720
+ ${pc8.dim("rrce-workflow mcp start")} Start server directly
1721
+ ${pc8.dim("rrce-workflow mcp stop")} Stop server directly
1722
+ ${pc8.dim("rrce-workflow mcp status")} Show status directly
1723
+ ${pc8.dim("rrce-workflow mcp help")} Show this help
1275
1724
 
1276
- // src/mcp/config.ts
1277
- function getMCPConfigPath() {
1278
- const workspaceRoot = detectWorkspaceRoot();
1279
- const rrceHome = getEffectiveRRCEHome(workspaceRoot);
1280
- return path12.join(rrceHome, "mcp.yaml");
1281
- }
1282
- function ensureMCPGlobalPath() {
1283
- const workspaceRoot = detectWorkspaceRoot();
1284
- const rrceHome = getEffectiveRRCEHome(workspaceRoot);
1285
- if (rrceHome.startsWith(".") || rrceHome.includes(".rrce-workflow/")) {
1286
- const configPath = path12.join(workspaceRoot, ".rrce-workflow", "config.yaml");
1287
- if (fs12.existsSync(configPath)) {
1288
- const content = fs12.readFileSync(configPath, "utf-8");
1289
- const modeMatch = content.match(/mode:\s*(global|workspace)/);
1290
- if (modeMatch?.[1] === "workspace") {
1291
- return {
1292
- configured: false,
1293
- path: rrceHome,
1294
- reason: "Workspace mode configured. MCP requires a global storage path."
1295
- };
1296
- }
1725
+ ${pc8.bold("CLAUDE DESKTOP SETUP")}
1726
+ Add to ${pc8.cyan("~/.config/claude/claude_desktop_config.json")}:
1727
+ ${pc8.dim(`{
1728
+ "mcpServers": {
1729
+ "rrce": {
1730
+ "command": "npx",
1731
+ "args": ["rrce-workflow", "mcp", "start"]
1297
1732
  }
1298
1733
  }
1299
- return {
1300
- configured: true,
1301
- path: rrceHome
1302
- };
1734
+ }`)}
1735
+
1736
+ ${pc8.bold("RESOURCES EXPOSED")}
1737
+ ${pc8.cyan("rrce://projects")} List all exposed projects
1738
+ ${pc8.cyan("rrce://projects/{name}/context")} Get project context
1739
+ ${pc8.cyan("rrce://projects/{name}/tasks")} Get project tasks
1740
+
1741
+ ${pc8.bold("PROMPTS (Agent Commands)")}
1742
+ ${pc8.cyan("init")} Initialize project context
1743
+ ${pc8.cyan("research")} Research requirements for a task
1744
+ ${pc8.cyan("plan")} Create execution plan
1745
+ ${pc8.cyan("execute")} Implement planned work
1746
+ ${pc8.cyan("docs")} Generate documentation
1747
+ ${pc8.cyan("sync")} Sync knowledge with codebase
1748
+ `;
1749
+ note7(help.trim(), "Help");
1303
1750
  }
1304
- function loadMCPConfig() {
1305
- const configPath = getMCPConfigPath();
1306
- if (!fs12.existsSync(configPath)) {
1307
- return { ...DEFAULT_MCP_CONFIG };
1751
+ var init_mcp = __esm({
1752
+ "src/mcp/index.ts"() {
1753
+ "use strict";
1754
+ init_config();
1755
+ init_detection();
1756
+ init_server();
1757
+ init_install();
1308
1758
  }
1759
+ });
1760
+
1761
+ // src/commands/wizard/index.ts
1762
+ import { intro, select as select3, spinner as spinner5, note as note6, outro as outro5, isCancel as isCancel6 } from "@clack/prompts";
1763
+ import pc7 from "picocolors";
1764
+ import * as fs11 from "fs";
1765
+
1766
+ // src/lib/git.ts
1767
+ import { execSync } from "child_process";
1768
+ function getGitUser() {
1309
1769
  try {
1310
- const content = fs12.readFileSync(configPath, "utf-8");
1311
- return parseMCPConfig(content);
1770
+ const result = execSync("git config user.name", { encoding: "utf-8" });
1771
+ return result.trim() || null;
1312
1772
  } catch {
1313
- return { ...DEFAULT_MCP_CONFIG };
1314
- }
1315
- }
1316
- function saveMCPConfig(config) {
1317
- const configPath = getMCPConfigPath();
1318
- const dir = path12.dirname(configPath);
1319
- if (!fs12.existsSync(dir)) {
1320
- fs12.mkdirSync(dir, { recursive: true });
1321
- }
1322
- const content = serializeMCPConfig(config);
1323
- fs12.writeFileSync(configPath, content);
1324
- }
1325
- function parseMCPConfig(content) {
1326
- const config = { ...DEFAULT_MCP_CONFIG, projects: [] };
1327
- const portMatch = content.match(/port:\s*(\d+)/);
1328
- if (portMatch?.[1]) config.server.port = parseInt(portMatch[1], 10);
1329
- const autoStartMatch = content.match(/autoStart:\s*(true|false)/);
1330
- if (autoStartMatch) config.server.autoStart = autoStartMatch[1] === "true";
1331
- const includeNewMatch = content.match(/includeNew:\s*(true|false)/);
1332
- if (includeNewMatch) config.defaults.includeNew = includeNewMatch[1] === "true";
1333
- const projectsMatch = content.match(/projects:\s*\n((?:\s+-[\s\S]*?(?=\n\w|\n*$))+)/);
1334
- if (projectsMatch?.[1]) {
1335
- const projectsContent = projectsMatch[1];
1336
- const projectBlocks = projectsContent.split(/\n\s+-\s+name:/).filter(Boolean);
1337
- for (const block of projectBlocks) {
1338
- const nameMatch = block.match(/(?:^|\n\s*)name:\s*[\"']?([^\"'\n]+)[\"']?/) || block.match(/^[\"']?([^\"'\n:]+)[\"']?/);
1339
- const exposeMatch = block.match(/expose:\s*(true|false)/);
1340
- if (nameMatch?.[1]) {
1341
- const permissions = parsePermissions(block);
1342
- config.projects.push({
1343
- name: nameMatch[1].trim(),
1344
- expose: exposeMatch ? exposeMatch[1] === "true" : true,
1345
- permissions
1346
- });
1347
- }
1348
- }
1773
+ return null;
1349
1774
  }
1350
- return config;
1351
1775
  }
1352
- function parsePermissions(block) {
1353
- const permissions = { ...DEFAULT_PERMISSIONS };
1354
- const knowledgeMatch = block.match(/knowledge:\s*(true|false)/);
1355
- if (knowledgeMatch) permissions.knowledge = knowledgeMatch[1] === "true";
1356
- const tasksMatch = block.match(/tasks:\s*(true|false)/);
1357
- if (tasksMatch) permissions.tasks = tasksMatch[1] === "true";
1358
- const refsMatch = block.match(/refs:\s*(true|false)/);
1359
- if (refsMatch) permissions.refs = refsMatch[1] === "true";
1360
- return permissions;
1361
- }
1362
- function serializeMCPConfig(config) {
1363
- let content = `# RRCE MCP Hub Configuration
1364
- # Manages which projects are exposed via MCP
1365
1776
 
1366
- server:
1367
- port: ${config.server.port}
1368
- autoStart: ${config.server.autoStart}
1777
+ // src/commands/wizard/index.ts
1778
+ init_paths();
1779
+ init_detection();
1369
1780
 
1370
- defaults:
1371
- includeNew: ${config.defaults.includeNew}
1372
- permissions:
1373
- knowledge: ${config.defaults.permissions.knowledge}
1374
- tasks: ${config.defaults.permissions.tasks}
1375
- refs: ${config.defaults.permissions.refs}
1781
+ // src/commands/wizard/setup-flow.ts
1782
+ init_paths();
1783
+ init_prompts();
1784
+ import { group, select as select2, multiselect, confirm, spinner, note as note2, outro, cancel } from "@clack/prompts";
1785
+ import pc3 from "picocolors";
1786
+ import * as fs7 from "fs";
1787
+ import * as path8 from "path";
1376
1788
 
1377
- projects:
1378
- `;
1379
- if (config.projects.length === 0) {
1380
- content += ' # No projects configured yet. Run "rrce-workflow mcp" to add projects.\n';
1381
- } else {
1382
- for (const project of config.projects) {
1383
- content += ` - name: "${project.name}"
1384
- expose: ${project.expose}
1385
- permissions:
1386
- knowledge: ${project.permissions.knowledge}
1387
- tasks: ${project.permissions.tasks}
1388
- refs: ${project.permissions.refs}
1389
- `;
1390
- }
1789
+ // src/commands/wizard/utils.ts
1790
+ init_paths();
1791
+ import * as fs4 from "fs";
1792
+ import * as path4 from "path";
1793
+ function copyPromptsToDir(prompts, targetDir, extension) {
1794
+ for (const prompt of prompts) {
1795
+ const baseName = path4.basename(prompt.filePath, ".md");
1796
+ const targetName = baseName + extension;
1797
+ const targetPath = path4.join(targetDir, targetName);
1798
+ const content = fs4.readFileSync(prompt.filePath, "utf-8");
1799
+ fs4.writeFileSync(targetPath, content);
1391
1800
  }
1392
- return content;
1393
1801
  }
1394
- function setProjectConfig(config, name, expose, permissions) {
1395
- const existing = config.projects.find((p) => p.name === name);
1396
- if (existing) {
1397
- existing.expose = expose;
1398
- if (permissions) {
1399
- existing.permissions = { ...existing.permissions, ...permissions };
1802
+ function copyDirRecursive(src, dest) {
1803
+ const entries = fs4.readdirSync(src, { withFileTypes: true });
1804
+ for (const entry of entries) {
1805
+ const srcPath = path4.join(src, entry.name);
1806
+ const destPath = path4.join(dest, entry.name);
1807
+ if (entry.isDirectory()) {
1808
+ ensureDir(destPath);
1809
+ copyDirRecursive(srcPath, destPath);
1810
+ } else {
1811
+ fs4.copyFileSync(srcPath, destPath);
1400
1812
  }
1401
- } else {
1402
- config.projects.push({
1403
- name,
1404
- expose,
1405
- permissions: permissions ? { ...DEFAULT_PERMISSIONS, ...permissions } : { ...DEFAULT_PERMISSIONS }
1406
- });
1407
- }
1408
- return config;
1409
- }
1410
- function isProjectExposed(config, name) {
1411
- const project = config.projects.find((p) => p.name === name);
1412
- if (project) {
1413
- return project.expose;
1414
1813
  }
1415
- return config.defaults.includeNew;
1416
- }
1417
- function getProjectPermissions(config, name) {
1418
- const project = config.projects.find((p) => p.name === name);
1419
- return project?.permissions ?? config.defaults.permissions;
1420
1814
  }
1421
1815
 
1422
- // src/mcp/server.ts
1423
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1424
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1425
- import {
1426
- CallToolRequestSchema,
1427
- ListResourcesRequestSchema,
1428
- ListToolsRequestSchema,
1429
- ReadResourceRequestSchema,
1430
- ListPromptsRequestSchema,
1431
- GetPromptRequestSchema
1432
- } from "@modelcontextprotocol/sdk/types.js";
1433
- import * as fs14 from "fs";
1434
- import * as path14 from "path";
1435
-
1436
- // src/mcp/resources.ts
1437
- import * as fs13 from "fs";
1438
- import * as path13 from "path";
1439
- function getExposedProjects() {
1440
- const config = loadMCPConfig();
1441
- const allProjects = scanForProjects();
1442
- return allProjects.filter((project) => isProjectExposed(config, project.name));
1443
- }
1444
- function getProjectContext(projectName) {
1445
- const config = loadMCPConfig();
1446
- if (!isProjectExposed(config, projectName)) {
1447
- return null;
1448
- }
1449
- const permissions = getProjectPermissions(config, projectName);
1450
- if (!permissions.knowledge) {
1451
- return null;
1452
- }
1453
- const projects = scanForProjects();
1454
- const project = projects.find((p) => p.name === projectName);
1455
- if (!project?.knowledgePath) {
1456
- return null;
1457
- }
1458
- const contextPath = path13.join(project.knowledgePath, "project-context.md");
1459
- if (!fs13.existsSync(contextPath)) {
1460
- return null;
1461
- }
1462
- return fs13.readFileSync(contextPath, "utf-8");
1463
- }
1464
- function getProjectTasks(projectName) {
1465
- const config = loadMCPConfig();
1466
- if (!isProjectExposed(config, projectName)) {
1467
- return [];
1816
+ // src/commands/wizard/vscode.ts
1817
+ init_paths();
1818
+ init_detection();
1819
+ import * as fs5 from "fs";
1820
+ import * as path5 from "path";
1821
+ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
1822
+ const workspaceFilePath = path5.join(workspacePath, `${workspaceName}.code-workspace`);
1823
+ let workspace;
1824
+ if (fs5.existsSync(workspaceFilePath)) {
1825
+ try {
1826
+ const content = fs5.readFileSync(workspaceFilePath, "utf-8");
1827
+ workspace = JSON.parse(content);
1828
+ } catch {
1829
+ workspace = { folders: [], settings: {} };
1830
+ }
1831
+ } else {
1832
+ workspace = { folders: [], settings: {} };
1468
1833
  }
1469
- const permissions = getProjectPermissions(config, projectName);
1470
- if (!permissions.tasks) {
1471
- return [];
1834
+ if (!workspace.settings) {
1835
+ workspace.settings = {};
1472
1836
  }
1473
- const projects = scanForProjects();
1474
- const project = projects.find((p) => p.name === projectName);
1475
- if (!project?.tasksPath || !fs13.existsSync(project.tasksPath)) {
1476
- return [];
1837
+ workspace.folders = workspace.folders.filter(
1838
+ (f) => f.path === "." || !f.name?.startsWith("\u{1F4C1}") && !f.name?.startsWith("\u{1F4DA}") && !f.name?.startsWith("\u{1F4CE}") && !f.name?.startsWith("\u{1F4CB}")
1839
+ );
1840
+ const mainFolderIndex = workspace.folders.findIndex((f) => f.path === ".");
1841
+ if (mainFolderIndex === -1) {
1842
+ workspace.folders.unshift({
1843
+ path: ".",
1844
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
1845
+ });
1846
+ } else {
1847
+ workspace.folders[mainFolderIndex] = {
1848
+ path: ".",
1849
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
1850
+ };
1477
1851
  }
1478
- const tasks = [];
1479
- try {
1480
- const taskDirs = fs13.readdirSync(project.tasksPath, { withFileTypes: true });
1481
- for (const dir of taskDirs) {
1482
- if (!dir.isDirectory()) continue;
1483
- const metaPath = path13.join(project.tasksPath, dir.name, "meta.json");
1484
- if (fs13.existsSync(metaPath)) {
1485
- try {
1486
- const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
1487
- tasks.push(meta);
1488
- } catch {
1489
- }
1852
+ const referenceFolderPaths = [];
1853
+ const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
1854
+ if (isDetectedProjects) {
1855
+ const projects = linkedProjects;
1856
+ for (const project of projects) {
1857
+ const sourceLabel = project.source === "global" ? "global" : "local";
1858
+ const folderPath = project.dataPath;
1859
+ if (fs5.existsSync(folderPath)) {
1860
+ referenceFolderPaths.push(folderPath);
1861
+ workspace.folders.push({
1862
+ path: folderPath,
1863
+ name: `\u{1F4C1} ${project.name} [${sourceLabel}]`
1864
+ });
1490
1865
  }
1491
1866
  }
1492
- } catch {
1493
- }
1494
- return tasks;
1495
- }
1496
- function searchKnowledge(query) {
1497
- const config = loadMCPConfig();
1498
- const projects = getExposedProjects();
1499
- const results = [];
1500
- const queryLower = query.toLowerCase();
1501
- for (const project of projects) {
1502
- const permissions = getProjectPermissions(config, project.name);
1503
- if (!permissions.knowledge || !project.knowledgePath) continue;
1504
- try {
1505
- const files = fs13.readdirSync(project.knowledgePath);
1506
- for (const file of files) {
1507
- if (!file.endsWith(".md")) continue;
1508
- const filePath = path13.join(project.knowledgePath, file);
1509
- const content = fs13.readFileSync(filePath, "utf-8");
1510
- const lines = content.split("\n");
1511
- const matches = [];
1512
- for (const line of lines) {
1513
- if (line.toLowerCase().includes(queryLower)) {
1514
- matches.push(line.trim());
1515
- }
1516
- }
1517
- if (matches.length > 0) {
1518
- results.push({
1519
- project: project.name,
1520
- file,
1521
- matches: matches.slice(0, 5)
1522
- // Limit to 5 matches per file
1523
- });
1524
- }
1867
+ } else {
1868
+ const projectNames = linkedProjects;
1869
+ const rrceHome = customGlobalPath || getRRCEHome();
1870
+ for (const projectName of projectNames) {
1871
+ const folderPath = path5.join(rrceHome, "workspaces", projectName);
1872
+ if (fs5.existsSync(folderPath)) {
1873
+ referenceFolderPaths.push(folderPath);
1874
+ workspace.folders.push({
1875
+ path: folderPath,
1876
+ name: `\u{1F4C1} ${projectName} [global]`
1877
+ });
1525
1878
  }
1526
- } catch {
1527
1879
  }
1528
1880
  }
1529
- return results;
1530
- }
1531
-
1532
- // src/mcp/prompts.ts
1533
- var AGENT_PROMPTS = [
1534
- {
1535
- name: "init",
1536
- description: "Initialize project context by analyzing codebase structure, tech stack, and conventions",
1537
- file: "init.md",
1538
- arguments: [
1539
- { name: "PROJECT_NAME", description: "Project name (optional, auto-detected if omitted)", required: false }
1540
- ]
1541
- },
1542
- {
1543
- name: "research",
1544
- description: "Research and clarify requirements for a new task",
1545
- file: "research_discussion.md",
1546
- arguments: [
1547
- { name: "REQUEST", description: "Description of the task or feature to research", required: true },
1548
- { name: "TASK_SLUG", description: "Kebab-case identifier for the task", required: true },
1549
- { name: "TITLE", description: "Human-readable title for the task", required: false }
1550
- ]
1551
- },
1552
- {
1553
- name: "plan",
1554
- description: "Create an actionable execution plan from research findings",
1555
- file: "planning_orchestrator.md",
1556
- arguments: [
1557
- { name: "TASK_SLUG", description: "Task slug to create plan for", required: true }
1558
- ]
1559
- },
1560
- {
1561
- name: "execute",
1562
- description: "Implement the planned work with code and tests",
1563
- file: "executor.md",
1564
- arguments: [
1565
- { name: "TASK_SLUG", description: "Task slug to execute", required: true },
1566
- { name: "BRANCH", description: "Git branch reference (optional)", required: false }
1567
- ]
1568
- },
1569
- {
1570
- name: "docs",
1571
- description: "Generate documentation for completed work",
1572
- file: "documentation.md",
1573
- arguments: [
1574
- { name: "DOC_TYPE", description: "Type of documentation (api, architecture, runbook, changelog)", required: true },
1575
- { name: "TASK_SLUG", description: "Task slug if documenting specific task", required: false }
1576
- ]
1577
- },
1578
- {
1579
- name: "sync",
1580
- description: "Reconcile knowledge base with actual codebase state",
1581
- file: "sync.md",
1582
- arguments: [
1583
- { name: "SCOPE", description: "Specific path or module to sync (optional)", required: false }
1584
- ]
1881
+ if (referenceFolderPaths.length > 0) {
1882
+ const readonlyPatterns = {};
1883
+ for (const folderPath of referenceFolderPaths) {
1884
+ readonlyPatterns[`${folderPath}/**`] = true;
1885
+ }
1886
+ const existingReadonly = workspace.settings["files.readonlyInclude"] || {};
1887
+ const cleanedReadonly = {};
1888
+ for (const [pattern, value] of Object.entries(existingReadonly)) {
1889
+ if (!pattern.includes(".rrce-workflow") && !pattern.includes("rrce-workflow/workspaces")) {
1890
+ cleanedReadonly[pattern] = value;
1891
+ }
1892
+ }
1893
+ workspace.settings["files.readonlyInclude"] = {
1894
+ ...cleanedReadonly,
1895
+ ...readonlyPatterns
1896
+ };
1585
1897
  }
1586
- ];
1587
-
1588
- // src/mcp/server.ts
1589
- var serverState = { running: false };
1590
- var mcpServer = null;
1591
- async function startMCPServer() {
1592
- const config = loadMCPConfig();
1593
- mcpServer = new Server(
1594
- { name: "rrce-mcp-hub", version: "1.0.0" },
1595
- { capabilities: { resources: {}, tools: {}, prompts: {} } }
1596
- );
1597
- registerResourceHandlers(mcpServer);
1598
- registerToolHandlers(mcpServer);
1599
- registerPromptHandlers(mcpServer);
1600
- const transport = new StdioServerTransport();
1601
- await mcpServer.connect(transport);
1602
- serverState = { running: true, port: config.server.port, pid: process.pid };
1603
- console.error(`RRCE MCP Hub started (pid: ${process.pid})`);
1604
- console.error(`Exposed projects: ${getExposedProjects().map((p) => p.name).join(", ")}`);
1605
- return { port: config.server.port, pid: process.pid };
1898
+ fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
1606
1899
  }
1607
- function registerResourceHandlers(server) {
1608
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
1609
- const projects = getExposedProjects();
1610
- const resources = [];
1611
- resources.push({
1612
- uri: "rrce://projects",
1613
- name: "Project List",
1614
- description: "List of all RRCE projects exposed via MCP",
1615
- mimeType: "application/json"
1616
- });
1617
- for (const project of projects) {
1618
- const config = loadMCPConfig();
1619
- const permissions = getProjectPermissions(config, project.name);
1620
- if (permissions.knowledge) {
1621
- resources.push({
1622
- uri: `rrce://projects/${project.name}/context`,
1623
- name: `${project.name} - Project Context`,
1624
- description: `Project context and architecture for ${project.name}`,
1625
- mimeType: "text/markdown"
1626
- });
1627
- }
1628
- if (permissions.tasks) {
1629
- resources.push({
1630
- uri: `rrce://projects/${project.name}/tasks`,
1631
- name: `${project.name} - Tasks`,
1632
- description: `Task list and status for ${project.name}`,
1633
- mimeType: "application/json"
1900
+
1901
+ // src/commands/wizard/setup-flow.ts
1902
+ init_detection();
1903
+ init_tui_utils();
1904
+ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
1905
+ const s = spinner();
1906
+ const config = await group(
1907
+ {
1908
+ storageMode: () => select2({
1909
+ message: "Where should workflow data be stored?",
1910
+ options: [
1911
+ { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
1912
+ { value: "workspace", label: "Workspace (.rrce-workflow/)", hint: "Self-contained, version with repo" }
1913
+ ],
1914
+ initialValue: "global"
1915
+ }),
1916
+ tools: () => multiselect({
1917
+ message: "Which AI tools do you use?",
1918
+ options: [
1919
+ { value: "copilot", label: "GitHub Copilot", hint: "VSCode" },
1920
+ { value: "antigravity", label: "Antigravity IDE" }
1921
+ ],
1922
+ required: false
1923
+ }),
1924
+ linkedProjects: () => {
1925
+ if (existingProjects.length === 0) {
1926
+ return Promise.resolve([]);
1927
+ }
1928
+ return multiselect({
1929
+ message: "Link knowledge from other projects?",
1930
+ options: existingProjects.map((project) => ({
1931
+ value: `${project.name}:${project.source}`,
1932
+ // Unique key
1933
+ label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
1934
+ hint: pc3.dim(
1935
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
1936
+ )
1937
+ })),
1938
+ required: false
1634
1939
  });
1940
+ },
1941
+ addToGitignore: () => confirm({
1942
+ message: "Add generated folders to .gitignore? (as comments - uncomment if needed)",
1943
+ initialValue: true
1944
+ }),
1945
+ confirm: () => confirm({
1946
+ message: "Create configuration?",
1947
+ initialValue: true
1948
+ })
1949
+ },
1950
+ {
1951
+ onCancel: () => {
1952
+ cancel("Setup process cancelled.");
1953
+ process.exit(0);
1635
1954
  }
1636
1955
  }
1637
- return { resources };
1638
- });
1639
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1640
- const { uri } = request.params;
1641
- if (uri === "rrce://projects") {
1642
- const projects = getExposedProjects();
1643
- return {
1644
- contents: [{
1645
- uri,
1646
- mimeType: "application/json",
1647
- text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1648
- }]
1649
- };
1956
+ );
1957
+ if (!config.confirm) {
1958
+ outro("Setup cancelled by user.");
1959
+ process.exit(0);
1960
+ }
1961
+ let customGlobalPath;
1962
+ if (config.storageMode === "global") {
1963
+ customGlobalPath = await resolveGlobalPath();
1964
+ if (!customGlobalPath) {
1965
+ cancel("Setup cancelled - no writable global path available.");
1966
+ process.exit(1);
1650
1967
  }
1651
- const projectMatch = uri.match(/^rrce:\/\/projects\/([^/]+)\/(.+)$/);
1652
- if (projectMatch) {
1653
- const [, projectName, resourceType] = projectMatch;
1654
- const content = resourceType === "context" ? getProjectContext(projectName) : JSON.stringify(getProjectTasks(projectName), null, 2);
1655
- if (content === null) throw new Error(`Resource not found: ${uri}`);
1656
- return {
1657
- contents: [{
1658
- uri,
1659
- mimeType: resourceType === "tasks" ? "application/json" : "text/markdown",
1660
- text: content
1661
- }]
1662
- };
1968
+ }
1969
+ s.start("Generating configuration");
1970
+ try {
1971
+ await generateConfiguration({
1972
+ storageMode: config.storageMode,
1973
+ globalPath: customGlobalPath,
1974
+ tools: config.tools,
1975
+ linkedProjects: config.linkedProjects,
1976
+ addToGitignore: config.addToGitignore
1977
+ }, workspacePath, workspaceName, existingProjects);
1978
+ s.stop("Configuration generated");
1979
+ const dataPaths = getDataPaths(
1980
+ config.storageMode,
1981
+ workspaceName,
1982
+ workspacePath,
1983
+ customGlobalPath
1984
+ );
1985
+ const summary = [
1986
+ `Storage: ${config.storageMode}`
1987
+ ];
1988
+ if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
1989
+ summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
1663
1990
  }
1664
- throw new Error(`Unknown resource: ${uri}`);
1665
- });
1666
- }
1667
- function registerToolHandlers(server) {
1668
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
1669
- tools: [
1670
- {
1671
- name: "search_knowledge",
1672
- description: "Search across all exposed project knowledge bases",
1673
- inputSchema: {
1674
- type: "object",
1675
- properties: { query: { type: "string", description: "Search query to find in knowledge files" } },
1676
- required: ["query"]
1677
- }
1678
- },
1679
- {
1680
- name: "list_projects",
1681
- description: "List all projects exposed via MCP",
1682
- inputSchema: { type: "object", properties: {} }
1683
- },
1684
- {
1685
- name: "get_project_context",
1686
- description: "Get the project context/architecture for a specific project",
1687
- inputSchema: {
1688
- type: "object",
1689
- properties: { project: { type: "string", description: "Name of the project to get context for" } },
1690
- required: ["project"]
1691
- }
1692
- }
1693
- ]
1694
- }));
1695
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
1696
- const { name, arguments: args } = request.params;
1697
- switch (name) {
1698
- case "search_knowledge": {
1699
- const results = searchKnowledge(args.query);
1700
- return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
1701
- }
1702
- case "list_projects": {
1703
- const projects = getExposedProjects();
1704
- return {
1705
- content: [{
1706
- type: "text",
1707
- text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1708
- }]
1709
- };
1710
- }
1711
- case "get_project_context": {
1712
- const context = getProjectContext(args.project);
1713
- if (!context) {
1714
- return { content: [{ type: "text", text: `No project context found for "${args.project}"` }], isError: true };
1715
- }
1716
- return { content: [{ type: "text", text: context }] };
1717
- }
1718
- default:
1719
- throw new Error(`Unknown tool: ${name}`);
1991
+ if (dataPaths.length > 0) {
1992
+ summary.push(`Data paths:`);
1993
+ dataPaths.forEach((p) => summary.push(` - ${p}`));
1720
1994
  }
1721
- });
1722
- }
1723
- function registerPromptHandlers(server) {
1724
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({
1725
- prompts: AGENT_PROMPTS.map((p) => ({
1726
- name: p.name,
1727
- description: p.description,
1728
- arguments: p.arguments.map((a) => ({ name: a.name, description: a.description, required: a.required }))
1729
- }))
1730
- }));
1731
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1732
- const { name, arguments: args } = request.params;
1733
- const promptDef = AGENT_PROMPTS.find((p) => p.name === name);
1734
- if (!promptDef) throw new Error(`Unknown prompt: ${name}`);
1735
- const promptsDir = getAgentCorePromptsDir();
1736
- const promptPath = path14.join(promptsDir, promptDef.file);
1737
- if (!fs14.existsSync(promptPath)) throw new Error(`Prompt file not found: ${promptDef.file}`);
1738
- let promptContent = fs14.readFileSync(promptPath, "utf-8");
1739
- if (args) {
1740
- for (const [key, value] of Object.entries(args)) {
1741
- promptContent = promptContent.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(value));
1742
- }
1995
+ const selectedTools = config.tools;
1996
+ if (selectedTools.length > 0) {
1997
+ summary.push(`Tools: ${selectedTools.join(", ")}`);
1743
1998
  }
1744
- return {
1745
- description: promptDef.description,
1746
- messages: [{ role: "user", content: { type: "text", text: promptContent } }]
1747
- };
1748
- });
1999
+ const linkedProjects = config.linkedProjects;
2000
+ if (linkedProjects.length > 0) {
2001
+ summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
2002
+ summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
2003
+ }
2004
+ note2(summary.join("\n"), "Setup Summary");
2005
+ if (linkedProjects.length > 0) {
2006
+ outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
2007
+ } else {
2008
+ outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
2009
+ }
2010
+ } catch (error) {
2011
+ s.stop("Error occurred");
2012
+ cancel(`Failed to setup: ${error instanceof Error ? error.message : String(error)}`);
2013
+ process.exit(1);
2014
+ }
1749
2015
  }
1750
- function stopMCPServer() {
1751
- if (mcpServer) {
1752
- mcpServer.close();
1753
- mcpServer = null;
2016
+ async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
2017
+ const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
2018
+ for (const dataPath of dataPaths) {
2019
+ ensureDir(dataPath);
2020
+ ensureDir(path8.join(dataPath, "knowledge"));
2021
+ ensureDir(path8.join(dataPath, "refs"));
2022
+ ensureDir(path8.join(dataPath, "tasks"));
2023
+ ensureDir(path8.join(dataPath, "templates"));
2024
+ }
2025
+ const agentCoreDir = getAgentCoreDir();
2026
+ syncMetadataToAll(agentCoreDir, dataPaths);
2027
+ copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
2028
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2029
+ if (config.tools.includes("copilot")) {
2030
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
2031
+ ensureDir(copilotPath);
2032
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
2033
+ }
2034
+ if (config.tools.includes("antigravity")) {
2035
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
2036
+ ensureDir(antigravityPath);
2037
+ copyPromptsToDir(prompts, antigravityPath, ".md");
2038
+ }
2039
+ const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
2040
+ ensureDir(path8.dirname(workspaceConfigPath));
2041
+ let configContent = `# RRCE-Workflow Configuration
2042
+ version: 1
2043
+
2044
+ storage:
2045
+ mode: ${config.storageMode}`;
2046
+ if (config.globalPath && config.globalPath !== getDefaultRRCEHome()) {
2047
+ configContent += `
2048
+ globalPath: "${config.globalPath}"`;
2049
+ }
2050
+ configContent += `
2051
+
2052
+ project:
2053
+ name: "${workspaceName}"
2054
+
2055
+ tools:
2056
+ copilot: ${config.tools.includes("copilot")}
2057
+ antigravity: ${config.tools.includes("antigravity")}
2058
+ `;
2059
+ if (config.linkedProjects.length > 0) {
2060
+ configContent += `
2061
+ linked_projects:
2062
+ `;
2063
+ config.linkedProjects.forEach((name) => {
2064
+ configContent += ` - ${name}
2065
+ `;
2066
+ });
2067
+ }
2068
+ fs7.writeFileSync(workspaceConfigPath, configContent);
2069
+ if (config.addToGitignore) {
2070
+ updateGitignore(workspacePath, config.storageMode, config.tools);
2071
+ }
2072
+ if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
2073
+ const selectedProjects = allProjects.filter(
2074
+ (p) => config.linkedProjects.includes(`${p.name}:${p.source}`)
2075
+ );
2076
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, config.globalPath);
1754
2077
  }
1755
- serverState = { running: false };
1756
- console.error("RRCE MCP Hub stopped");
1757
2078
  }
1758
- function getMCPServerStatus() {
1759
- return { ...serverState };
2079
+ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
2080
+ const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
2081
+ const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
2082
+ switch (mode) {
2083
+ case "global":
2084
+ return [globalPath];
2085
+ case "workspace":
2086
+ return [workspacePath];
2087
+ default:
2088
+ return [globalPath];
2089
+ }
1760
2090
  }
1761
-
1762
- // src/mcp/index.ts
1763
- async function runMCP(subcommand2) {
1764
- if (subcommand2) {
1765
- switch (subcommand2) {
1766
- case "start":
1767
- await startMCPServer();
1768
- await new Promise(() => {
1769
- });
1770
- return;
1771
- case "stop":
1772
- await handleStopServer();
1773
- return;
1774
- case "status":
1775
- await handleShowStatus();
1776
- return;
1777
- case "help":
1778
- showHelp();
1779
- return;
1780
- }
2091
+ function updateGitignore(workspacePath, storageMode, tools) {
2092
+ const gitignorePath = path8.join(workspacePath, ".gitignore");
2093
+ const entries = [];
2094
+ if (storageMode === "workspace") {
2095
+ entries.push(".rrce-workflow/");
1781
2096
  }
1782
- intro3(pc9.bgCyan(pc9.black(" RRCE MCP Hub ")));
1783
- const globalPathCheck = await ensureMCPGlobalPath();
1784
- if (!globalPathCheck.configured) {
1785
- const configured = await handleConfigureGlobalPath();
1786
- if (!configured) {
1787
- outro7(pc9.yellow("MCP requires a global storage path. Setup cancelled."));
1788
- return;
1789
- }
2097
+ if (tools.includes("copilot")) {
2098
+ entries.push(".github/agents/");
1790
2099
  }
1791
- let running = true;
1792
- while (running) {
1793
- const action = await select5({
1794
- message: "What would you like to do?",
1795
- options: [
1796
- { value: "status", label: "\u{1F4CB} View project status", hint: "See which projects are exposed" },
1797
- { value: "configure", label: "\u2699\uFE0F Configure projects", hint: "Choose which projects to expose" },
1798
- { value: "start", label: "\u25B6\uFE0F Start MCP server", hint: "Start the MCP server" },
1799
- { value: "stop", label: "\u23F9\uFE0F Stop MCP server", hint: "Stop the running server" },
1800
- { value: "help", label: "\u2753 Help", hint: "Learn about MCP Hub" },
1801
- { value: "exit", label: "\u21A9 Exit", hint: "Return to shell" }
1802
- ]
1803
- });
1804
- if (isCancel8(action)) {
1805
- cancel7("MCP Hub closed.");
1806
- return;
1807
- }
1808
- switch (action) {
1809
- case "status":
1810
- await handleShowStatus();
1811
- break;
1812
- case "configure":
1813
- await handleConfigure();
1814
- break;
1815
- case "start":
1816
- await handleStartServer();
1817
- running = false;
1818
- break;
1819
- case "stop":
1820
- await handleStopServer();
1821
- break;
1822
- case "help":
1823
- showHelp();
1824
- break;
1825
- case "exit":
1826
- running = false;
1827
- break;
1828
- }
2100
+ if (tools.includes("antigravity")) {
2101
+ entries.push(".agent/");
1829
2102
  }
1830
- outro7(pc9.green("MCP Hub closed."));
1831
- }
1832
- async function handleConfigureGlobalPath() {
1833
- const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
1834
- const fs15 = await import("fs");
1835
- const path15 = await import("path");
1836
- note8(
1837
- `MCP Hub requires a ${pc9.bold("global storage path")} to store its configuration
1838
- and coordinate across projects.
1839
-
1840
- Your current setup uses ${pc9.cyan("workspace")} mode, which stores data
1841
- locally in each project. MCP needs a central location.`,
1842
- "Global Path Required"
1843
- );
1844
- const resolvedPath = await resolveGlobalPath2();
1845
- if (!resolvedPath) {
2103
+ if (entries.length === 0) {
1846
2104
  return false;
1847
2105
  }
1848
2106
  try {
1849
- if (!fs15.existsSync(resolvedPath)) {
1850
- fs15.mkdirSync(resolvedPath, { recursive: true });
2107
+ let content = "";
2108
+ if (fs7.existsSync(gitignorePath)) {
2109
+ content = fs7.readFileSync(gitignorePath, "utf-8");
1851
2110
  }
1852
- const config = loadMCPConfig();
1853
- saveMCPConfig(config);
1854
- note8(
1855
- `${pc9.green("\u2713")} Global path configured: ${pc9.cyan(resolvedPath)}
1856
-
1857
- MCP config will be stored at:
1858
- ${path15.join(resolvedPath, "mcp.yaml")}`,
1859
- "Configuration Saved"
1860
- );
2111
+ const lines = content.split("\n").map((line) => line.trim());
2112
+ const newEntries = [];
2113
+ for (const entry of entries) {
2114
+ const entryWithoutSlash = entry.replace(/\/$/, "");
2115
+ const commentedEntry = `# ${entry}`;
2116
+ const commentedEntryNoSlash = `# ${entryWithoutSlash}`;
2117
+ if (!lines.some(
2118
+ (line) => line === entry || line === entryWithoutSlash || line === commentedEntry || line === commentedEntryNoSlash
2119
+ )) {
2120
+ newEntries.push(entry);
2121
+ }
2122
+ }
2123
+ if (newEntries.length === 0) {
2124
+ return false;
2125
+ }
2126
+ let newContent = content;
2127
+ if (!newContent.endsWith("\n") && newContent !== "") {
2128
+ newContent += "\n";
2129
+ }
2130
+ if (newContent === "" || !content.includes("# rrce-workflow")) {
2131
+ newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
2132
+ }
2133
+ newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
2134
+ fs7.writeFileSync(gitignorePath, newContent);
1861
2135
  return true;
1862
- } catch (error) {
1863
- note8(
1864
- `${pc9.red("\u2717")} Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,
1865
- "Error"
1866
- );
2136
+ } catch {
1867
2137
  return false;
1868
2138
  }
1869
2139
  }
1870
- async function handleShowStatus() {
1871
- const s = spinner6();
1872
- s.start("Loading projects...");
1873
- const config = loadMCPConfig();
1874
- const projects = scanForProjects();
1875
- s.stop("Projects loaded");
2140
+
2141
+ // src/commands/wizard/link-flow.ts
2142
+ init_paths();
2143
+ import { multiselect as multiselect2, spinner as spinner2, note as note3, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
2144
+ import pc4 from "picocolors";
2145
+ import * as fs8 from "fs";
2146
+ init_detection();
2147
+ async function runLinkProjectsFlow(workspacePath, workspaceName) {
2148
+ const projects = scanForProjects({
2149
+ excludeWorkspace: workspaceName,
2150
+ workspacePath
2151
+ });
1876
2152
  if (projects.length === 0) {
1877
- note8('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
2153
+ outro2(pc4.yellow("No other projects found. Try setting up another project first."));
1878
2154
  return;
1879
2155
  }
1880
- const lines = [
1881
- `${pc9.bold("Project Status")}`,
1882
- ""
1883
- ];
1884
- for (const project of projects) {
1885
- const projectConfig = config.projects.find((p) => p.name === project.name);
1886
- const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1887
- const status = isExposed ? pc9.green("\u2713 exposed") : pc9.dim("\u25CB hidden");
1888
- const source = pc9.dim(`(${project.source})`);
1889
- lines.push(` ${status} ${project.name} ${source}`);
2156
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2157
+ const linkedProjects = await multiselect2({
2158
+ message: "Select projects to link:",
2159
+ options: projects.map((project) => ({
2160
+ value: `${project.name}:${project.source}`,
2161
+ // Unique key
2162
+ label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
2163
+ hint: pc4.dim(
2164
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
2165
+ )
2166
+ })),
2167
+ required: true
2168
+ });
2169
+ if (isCancel3(linkedProjects)) {
2170
+ cancel2("Cancelled.");
2171
+ process.exit(0);
1890
2172
  }
1891
- lines.push("");
1892
- lines.push(pc9.dim(`Config: ${getMCPConfigPath()}`));
1893
- const serverStatus = getMCPServerStatus();
1894
- if (serverStatus.running) {
1895
- lines.push(pc9.green(`Server: running on port ${serverStatus.port}`));
2173
+ const selectedKeys = linkedProjects;
2174
+ if (selectedKeys.length === 0) {
2175
+ outro2("No projects selected.");
2176
+ return;
2177
+ }
2178
+ const selectedProjects = projects.filter(
2179
+ (p) => selectedKeys.includes(`${p.name}:${p.source}`)
2180
+ );
2181
+ const s = spinner2();
2182
+ s.start("Linking projects");
2183
+ const configFilePath = getConfigPath(workspacePath);
2184
+ let configContent = fs8.readFileSync(configFilePath, "utf-8");
2185
+ if (configContent.includes("linked_projects:")) {
2186
+ const lines = configContent.split("\n");
2187
+ const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
2188
+ if (linkedIndex !== -1) {
2189
+ let insertIndex = linkedIndex + 1;
2190
+ while (insertIndex < lines.length && lines[insertIndex]?.startsWith(" - ")) {
2191
+ insertIndex++;
2192
+ }
2193
+ for (const project of selectedProjects) {
2194
+ const entry = ` - ${project.name}:${project.source}`;
2195
+ if (!configContent.includes(entry)) {
2196
+ lines.splice(insertIndex, 0, entry);
2197
+ insertIndex++;
2198
+ }
2199
+ }
2200
+ configContent = lines.join("\n");
2201
+ }
1896
2202
  } else {
1897
- lines.push(pc9.dim("Server: not running"));
2203
+ configContent += `
2204
+ linked_projects:
2205
+ `;
2206
+ selectedProjects.forEach((project) => {
2207
+ configContent += ` - ${project.name}:${project.source}
2208
+ `;
2209
+ });
1898
2210
  }
1899
- note8(lines.join("\n"), "MCP Hub Status");
2211
+ fs8.writeFileSync(configFilePath, configContent);
2212
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
2213
+ s.stop("Projects linked");
2214
+ const workspaceFile = `${workspaceName}.code-workspace`;
2215
+ const summary = [
2216
+ `Linked projects:`,
2217
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
2218
+ ``,
2219
+ `Workspace file: ${pc4.cyan(workspaceFile)}`
2220
+ ];
2221
+ note3(summary.join("\n"), "Link Summary");
2222
+ outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
1900
2223
  }
1901
- async function handleConfigure() {
1902
- const s = spinner6();
1903
- s.start("Scanning for projects...");
1904
- const config = loadMCPConfig();
1905
- const projects = scanForProjects();
1906
- s.stop("Projects found");
1907
- if (projects.length === 0) {
1908
- note8('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1909
- return;
1910
- }
1911
- const options = projects.map((project) => {
1912
- const projectConfig = config.projects.find((p) => p.name === project.name);
1913
- const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1914
- return {
1915
- value: project.name,
1916
- label: `${project.name} ${pc9.dim(`(${project.source})`)}`,
1917
- hint: project.dataPath
1918
- };
1919
- });
1920
- const currentlyExposed = projects.filter((p) => {
1921
- const cfg = config.projects.find((c) => c.name === p.name);
1922
- return cfg?.expose ?? config.defaults.includeNew;
1923
- }).map((p) => p.name);
1924
- const selected = await multiselect3({
1925
- message: "Select projects to expose via MCP:",
1926
- options,
1927
- initialValues: currentlyExposed,
1928
- required: false
1929
- });
1930
- if (isCancel8(selected)) {
2224
+
2225
+ // src/commands/wizard/sync-flow.ts
2226
+ init_paths();
2227
+ import { confirm as confirm2, spinner as spinner3, note as note4, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
2228
+ import pc5 from "picocolors";
2229
+ import * as fs9 from "fs";
2230
+ import * as path9 from "path";
2231
+ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
2232
+ const localPath = getLocalWorkspacePath(workspacePath);
2233
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2234
+ const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
2235
+ const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
2236
+ const existingDirs = subdirs.filter(
2237
+ (dir) => fs9.existsSync(path9.join(localPath, dir))
2238
+ );
2239
+ if (existingDirs.length === 0) {
2240
+ outro3(pc5.yellow("No data found in workspace storage to sync."));
1931
2241
  return;
1932
2242
  }
1933
- const selectedNames = selected;
1934
- for (const project of projects) {
1935
- const shouldExpose = selectedNames.includes(project.name);
1936
- setProjectConfig(config, project.name, shouldExpose);
1937
- }
1938
- saveMCPConfig(config);
1939
- const exposedCount = selectedNames.length;
1940
- note8(
1941
- `${pc9.green("\u2713")} Configuration saved!
2243
+ note4(
2244
+ `The following will be copied to global storage:
2245
+ ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
1942
2246
 
1943
- Exposed projects: ${exposedCount}
1944
- Hidden projects: ${projects.length - exposedCount}`,
1945
- "Configuration Updated"
2247
+ Destination: ${pc5.cyan(globalPath)}`,
2248
+ "Sync Preview"
1946
2249
  );
2250
+ const shouldSync = await confirm2({
2251
+ message: "Proceed with sync to global storage?",
2252
+ initialValue: true
2253
+ });
2254
+ if (isCancel4(shouldSync) || !shouldSync) {
2255
+ outro3("Sync cancelled.");
2256
+ return;
2257
+ }
2258
+ const s = spinner3();
2259
+ s.start("Syncing to global storage");
2260
+ try {
2261
+ ensureDir(globalPath);
2262
+ for (const dir of existingDirs) {
2263
+ const srcDir = path9.join(localPath, dir);
2264
+ const destDir = path9.join(globalPath, dir);
2265
+ ensureDir(destDir);
2266
+ copyDirRecursive(srcDir, destDir);
2267
+ }
2268
+ s.stop("Sync complete");
2269
+ const summary = [
2270
+ `Synced directories:`,
2271
+ ...existingDirs.map((d) => ` \u2713 ${d}/`),
2272
+ ``,
2273
+ `Global path: ${pc5.cyan(globalPath)}`,
2274
+ ``,
2275
+ `Other projects can now link this knowledge!`
2276
+ ];
2277
+ note4(summary.join("\n"), "Sync Summary");
2278
+ outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
2279
+ } catch (error) {
2280
+ s.stop("Error occurred");
2281
+ cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
2282
+ process.exit(1);
2283
+ }
1947
2284
  }
1948
- async function handleStartServer() {
1949
- const s = spinner6();
1950
- s.start("Starting MCP server...");
2285
+
2286
+ // src/commands/wizard/update-flow.ts
2287
+ init_paths();
2288
+ init_prompts();
2289
+ import { confirm as confirm3, spinner as spinner4, note as note5, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
2290
+ import pc6 from "picocolors";
2291
+ import * as fs10 from "fs";
2292
+ import * as path10 from "path";
2293
+ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
2294
+ const s = spinner4();
2295
+ s.start("Checking for updates");
1951
2296
  try {
1952
- const result = await startMCPServer();
1953
- s.stop(pc9.green(`MCP server started on port ${result.port}`));
1954
- note8(
1955
- `The MCP server is now running.
2297
+ const agentCoreDir = getAgentCoreDir();
2298
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2299
+ const mode = currentStorageMode || "global";
2300
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2301
+ const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
2302
+ s.stop("Updates found");
2303
+ note5(
2304
+ `The following will be updated from the package:
2305
+ \u2022 prompts/ (${prompts.length} agent prompts)
2306
+ \u2022 templates/ (output templates)
1956
2307
 
1957
- To connect from Claude Desktop, add to your config:
1958
- ` + pc9.cyan(`~/.config/claude/claude_desktop_config.json`),
1959
- "Server Started"
2308
+ Target locations:
2309
+ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
2310
+ "Update Preview"
1960
2311
  );
2312
+ const shouldUpdate = await confirm3({
2313
+ message: "Proceed with update?",
2314
+ initialValue: true
2315
+ });
2316
+ if (isCancel5(shouldUpdate) || !shouldUpdate) {
2317
+ outro4("Update cancelled.");
2318
+ return;
2319
+ }
2320
+ s.start("Updating from package");
2321
+ for (const dataPath of dataPaths) {
2322
+ copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
2323
+ }
2324
+ const configFilePath = getConfigPath(workspacePath);
2325
+ const configContent = fs10.readFileSync(configFilePath, "utf-8");
2326
+ if (configContent.includes("copilot: true")) {
2327
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
2328
+ ensureDir(copilotPath);
2329
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
2330
+ }
2331
+ if (configContent.includes("antigravity: true")) {
2332
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
2333
+ ensureDir(antigravityPath);
2334
+ copyPromptsToDir(prompts, antigravityPath, ".md");
2335
+ }
2336
+ s.stop("Update complete");
2337
+ const summary = [
2338
+ `Updated:`,
2339
+ ` \u2713 ${prompts.length} agent prompts`,
2340
+ ` \u2713 Output templates`,
2341
+ ``,
2342
+ `Your configuration and knowledge files were preserved.`
2343
+ ];
2344
+ note5(summary.join("\n"), "Update Summary");
2345
+ outro4(pc6.green("\u2713 Successfully updated from package!"));
1961
2346
  } catch (error) {
1962
- s.stop(pc9.red("Failed to start server"));
1963
- note8(
1964
- `Error: ${error instanceof Error ? error.message : String(error)}`,
1965
- "Server Error"
1966
- );
2347
+ s.stop("Error occurred");
2348
+ cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
2349
+ process.exit(1);
1967
2350
  }
1968
2351
  }
1969
- async function handleStopServer() {
1970
- const status = getMCPServerStatus();
1971
- if (!status.running) {
1972
- note8("MCP server is not running.", "Status");
1973
- return;
1974
- }
1975
- const confirmed = await confirm4({
1976
- message: "Stop the MCP server?",
1977
- initialValue: true
1978
- });
1979
- if (isCancel8(confirmed) || !confirmed) {
1980
- return;
2352
+ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
2353
+ const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
2354
+ const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
2355
+ switch (mode) {
2356
+ case "global":
2357
+ return [globalPath];
2358
+ case "workspace":
2359
+ return [workspacePath];
2360
+ default:
2361
+ return [globalPath];
1981
2362
  }
1982
- stopMCPServer();
1983
- note8(pc9.green("MCP server stopped."), "Server Stopped");
1984
2363
  }
1985
- function showHelp() {
1986
- const help = `
1987
- ${pc9.bold("RRCE MCP Hub")} - Cross-project AI assistant server
1988
-
1989
- ${pc9.bold("ABOUT")}
1990
- MCP (Model Context Protocol) allows AI assistants like Claude to
1991
- access your project knowledge in real-time. The RRCE MCP Hub
1992
- provides a central server that exposes selected projects.
1993
-
1994
- ${pc9.bold("MENU OPTIONS")}
1995
- ${pc9.cyan("View project status")} See which projects are exposed
1996
- ${pc9.cyan("Configure projects")} Choose which projects to expose
1997
- ${pc9.cyan("Start MCP server")} Start the server for AI access
1998
- ${pc9.cyan("Stop MCP server")} Stop the running server
1999
2364
 
2000
- ${pc9.bold("DIRECT COMMANDS")}
2001
- ${pc9.dim("rrce-workflow mcp start")} Start server directly
2002
- ${pc9.dim("rrce-workflow mcp stop")} Stop server directly
2003
- ${pc9.dim("rrce-workflow mcp status")} Show status directly
2004
- ${pc9.dim("rrce-workflow mcp help")} Show this help
2005
-
2006
- ${pc9.bold("CLAUDE DESKTOP SETUP")}
2007
- Add to ${pc9.cyan("~/.config/claude/claude_desktop_config.json")}:
2008
- ${pc9.dim(`{
2009
- "mcpServers": {
2010
- "rrce": {
2011
- "command": "npx",
2012
- "args": ["rrce-workflow", "mcp", "start"]
2365
+ // src/commands/wizard/index.ts
2366
+ async function runWizard() {
2367
+ intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
2368
+ const s = spinner5();
2369
+ s.start("Detecting environment");
2370
+ const workspacePath = detectWorkspaceRoot();
2371
+ const workspaceName = getWorkspaceName(workspacePath);
2372
+ const gitUser = getGitUser();
2373
+ await new Promise((r) => setTimeout(r, 800));
2374
+ s.stop("Environment detected");
2375
+ note6(
2376
+ `Git User: ${pc7.bold(gitUser || "(not found)")}
2377
+ Workspace: ${pc7.bold(workspaceName)}`,
2378
+ "Context"
2379
+ );
2380
+ const detectedProjects = scanForProjects({
2381
+ excludeWorkspace: workspaceName,
2382
+ workspacePath
2383
+ });
2384
+ const configFilePath = getConfigPath(workspacePath);
2385
+ const isAlreadyConfigured = fs11.existsSync(configFilePath);
2386
+ let currentStorageMode = null;
2387
+ if (isAlreadyConfigured) {
2388
+ try {
2389
+ const configContent = fs11.readFileSync(configFilePath, "utf-8");
2390
+ const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
2391
+ currentStorageMode = modeMatch?.[1] ?? null;
2392
+ } catch {
2013
2393
  }
2014
2394
  }
2015
- }`)}
2016
-
2017
- ${pc9.bold("RESOURCES EXPOSED")}
2018
- ${pc9.cyan("rrce://projects")} List all exposed projects
2019
- ${pc9.cyan("rrce://projects/{name}/context")} Get project context
2020
- ${pc9.cyan("rrce://projects/{name}/tasks")} Get project tasks
2395
+ const localDataPath = getLocalWorkspacePath(workspacePath);
2396
+ const hasLocalData = fs11.existsSync(localDataPath);
2397
+ if (isAlreadyConfigured) {
2398
+ const menuOptions = [];
2399
+ if (detectedProjects.length > 0) {
2400
+ menuOptions.push({
2401
+ value: "link",
2402
+ label: "Link other project knowledge",
2403
+ hint: `${detectedProjects.length} projects detected (global + sibling)`
2404
+ });
2405
+ }
2406
+ if (currentStorageMode === "workspace" && hasLocalData) {
2407
+ menuOptions.push({
2408
+ value: "sync-global",
2409
+ label: "Sync to global storage",
2410
+ hint: "Share knowledge with other projects"
2411
+ });
2412
+ }
2413
+ menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
2414
+ menuOptions.push({ value: "exit", label: "Exit" });
2415
+ const action = await select3({
2416
+ message: "This workspace is already configured. What would you like to do?",
2417
+ options: menuOptions
2418
+ });
2419
+ if (isCancel6(action) || action === "exit") {
2420
+ outro5("Exited.");
2421
+ process.exit(0);
2422
+ }
2423
+ if (action === "link") {
2424
+ await runLinkProjectsFlow(workspacePath, workspaceName);
2425
+ return;
2426
+ }
2427
+ if (action === "sync-global") {
2428
+ await runSyncToGlobalFlow(workspacePath, workspaceName);
2429
+ return;
2430
+ }
2431
+ if (action === "update") {
2432
+ await runUpdateFlow(workspacePath, workspaceName, currentStorageMode);
2433
+ return;
2434
+ }
2435
+ }
2436
+ await runSetupFlow(workspacePath, workspaceName, detectedProjects);
2437
+ }
2021
2438
 
2022
- ${pc9.bold("PROMPTS (Agent Commands)")}
2023
- ${pc9.cyan("init")} Initialize project context
2024
- ${pc9.cyan("research")} Research requirements for a task
2025
- ${pc9.cyan("plan")} Create execution plan
2026
- ${pc9.cyan("execute")} Implement planned work
2027
- ${pc9.cyan("docs")} Generate documentation
2028
- ${pc9.cyan("sync")} Sync knowledge with codebase
2029
- `;
2030
- note8(help.trim(), "Help");
2439
+ // src/commands/selector.ts
2440
+ init_prompts();
2441
+ import { intro as intro3, select as select5, note as note8, cancel as cancel7, isCancel as isCancel8, outro as outro7 } from "@clack/prompts";
2442
+ import pc9 from "picocolors";
2443
+ import * as path15 from "path";
2444
+ async function runSelector() {
2445
+ const workspaceName = path15.basename(process.cwd());
2446
+ intro3(pc9.cyan(pc9.inverse(` RRCE-Workflow | ${workspaceName} `)));
2447
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2448
+ if (prompts.length === 0) {
2449
+ cancel7("No agents found. Run `rrce-workflow` to set up.");
2450
+ process.exit(0);
2451
+ }
2452
+ const selection = await select5({
2453
+ message: "Select an agent:",
2454
+ options: [
2455
+ {
2456
+ value: "mcp",
2457
+ label: "Manage MCP Hub",
2458
+ hint: "Configure & Start MCP Server"
2459
+ },
2460
+ ...prompts.map((p) => ({
2461
+ value: p,
2462
+ label: p.frontmatter.name,
2463
+ hint: p.frontmatter.description
2464
+ }))
2465
+ ]
2466
+ });
2467
+ if (isCancel8(selection)) {
2468
+ cancel7("Selection cancelled.");
2469
+ process.exit(0);
2470
+ }
2471
+ if (selection === "mcp") {
2472
+ const { runMCP: runMCP2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
2473
+ await runMCP2();
2474
+ return;
2475
+ }
2476
+ const prompt = selection;
2477
+ note8(
2478
+ `Use this agent in your IDE by invoking:
2479
+ ${pc9.bold(pc9.cyan(`@${prompt.frontmatter.name}`))}`,
2480
+ "Agent Selected"
2481
+ );
2482
+ outro7("Done");
2031
2483
  }
2032
2484
 
2033
2485
  // src/index.ts
2486
+ init_mcp();
2034
2487
  var command = process.argv[2];
2035
2488
  var subcommand = process.argv[3];
2036
2489
  if (!command || command === "wizard") {