rrce-workflow 0.2.23 → 0.2.24

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 +1960 -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,1908 @@ 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 getProjectContext(projectName) {
869
+ const config = loadMCPConfig();
870
+ if (!isProjectExposed(config, projectName)) {
871
+ return null;
872
+ }
873
+ const permissions = getProjectPermissions(config, projectName);
874
+ if (!permissions.knowledge) {
875
+ return null;
876
+ }
877
+ const projects = scanForProjects();
878
+ const project = projects.find((p) => p.name === projectName);
879
+ if (!project?.knowledgePath) {
880
+ return null;
881
+ }
882
+ const contextPath = path13.join(project.knowledgePath, "project-context.md");
883
+ if (!fs14.existsSync(contextPath)) {
884
+ return null;
885
+ }
886
+ return fs14.readFileSync(contextPath, "utf-8");
887
+ }
888
+ function getProjectTasks(projectName) {
889
+ const config = loadMCPConfig();
890
+ if (!isProjectExposed(config, projectName)) {
891
+ return [];
892
+ }
893
+ const permissions = getProjectPermissions(config, projectName);
894
+ if (!permissions.tasks) {
895
+ return [];
896
+ }
897
+ const projects = scanForProjects();
898
+ const project = projects.find((p) => p.name === projectName);
899
+ if (!project?.tasksPath || !fs14.existsSync(project.tasksPath)) {
900
+ return [];
901
+ }
902
+ const tasks = [];
903
+ try {
904
+ const taskDirs = fs14.readdirSync(project.tasksPath, { withFileTypes: true });
905
+ for (const dir of taskDirs) {
906
+ if (!dir.isDirectory()) continue;
907
+ const metaPath = path13.join(project.tasksPath, dir.name, "meta.json");
908
+ if (fs14.existsSync(metaPath)) {
909
+ try {
910
+ const meta = JSON.parse(fs14.readFileSync(metaPath, "utf-8"));
911
+ tasks.push(meta);
912
+ } catch {
913
+ }
914
+ }
915
+ }
916
+ } catch {
917
+ }
918
+ return tasks;
919
+ }
920
+ function searchKnowledge(query) {
921
+ const config = loadMCPConfig();
922
+ const projects = getExposedProjects();
923
+ const results = [];
924
+ const queryLower = query.toLowerCase();
925
+ for (const project of projects) {
926
+ const permissions = getProjectPermissions(config, project.name);
927
+ if (!permissions.knowledge || !project.knowledgePath) continue;
601
928
  try {
602
- const content = fs5.readFileSync(workspaceFilePath, "utf-8");
603
- workspace = JSON.parse(content);
929
+ const files = fs14.readdirSync(project.knowledgePath);
930
+ for (const file of files) {
931
+ if (!file.endsWith(".md")) continue;
932
+ const filePath = path13.join(project.knowledgePath, file);
933
+ const content = fs14.readFileSync(filePath, "utf-8");
934
+ const lines = content.split("\n");
935
+ const matches = [];
936
+ for (const line of lines) {
937
+ if (line.toLowerCase().includes(queryLower)) {
938
+ matches.push(line.trim());
939
+ }
940
+ }
941
+ if (matches.length > 0) {
942
+ results.push({
943
+ project: project.name,
944
+ file,
945
+ matches: matches.slice(0, 5)
946
+ // Limit to 5 matches per file
947
+ });
948
+ }
949
+ }
604
950
  } catch {
605
- workspace = { folders: [], settings: {} };
606
951
  }
607
- } else {
608
- workspace = { folders: [], settings: {} };
609
952
  }
610
- if (!workspace.settings) {
611
- workspace.settings = {};
953
+ return results;
954
+ }
955
+ var init_resources = __esm({
956
+ "src/mcp/resources.ts"() {
957
+ "use strict";
958
+ init_config();
959
+ init_detection();
612
960
  }
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)`
961
+ });
962
+
963
+ // src/mcp/prompts.ts
964
+ var AGENT_PROMPTS;
965
+ var init_prompts2 = __esm({
966
+ "src/mcp/prompts.ts"() {
967
+ "use strict";
968
+ AGENT_PROMPTS = [
969
+ {
970
+ name: "init",
971
+ description: "Initialize project context by analyzing codebase structure, tech stack, and conventions",
972
+ file: "init.md",
973
+ arguments: [
974
+ { name: "PROJECT_NAME", description: "Project name (optional, auto-detected if omitted)", required: false }
975
+ ]
976
+ },
977
+ {
978
+ name: "research",
979
+ description: "Research and clarify requirements for a new task",
980
+ file: "research_discussion.md",
981
+ arguments: [
982
+ { name: "REQUEST", description: "Description of the task or feature to research", required: true },
983
+ { name: "TASK_SLUG", description: "Kebab-case identifier for the task", required: true },
984
+ { name: "TITLE", description: "Human-readable title for the task", required: false }
985
+ ]
986
+ },
987
+ {
988
+ name: "plan",
989
+ description: "Create an actionable execution plan from research findings",
990
+ file: "planning_orchestrator.md",
991
+ arguments: [
992
+ { name: "TASK_SLUG", description: "Task slug to create plan for", required: true }
993
+ ]
994
+ },
995
+ {
996
+ name: "execute",
997
+ description: "Implement the planned work with code and tests",
998
+ file: "executor.md",
999
+ arguments: [
1000
+ { name: "TASK_SLUG", description: "Task slug to execute", required: true },
1001
+ { name: "BRANCH", description: "Git branch reference (optional)", required: false }
1002
+ ]
1003
+ },
1004
+ {
1005
+ name: "docs",
1006
+ description: "Generate documentation for completed work",
1007
+ file: "documentation.md",
1008
+ arguments: [
1009
+ { name: "DOC_TYPE", description: "Type of documentation (api, architecture, runbook, changelog)", required: true },
1010
+ { name: "TASK_SLUG", description: "Task slug if documenting specific task", required: false }
1011
+ ]
1012
+ },
1013
+ {
1014
+ name: "sync",
1015
+ description: "Reconcile knowledge base with actual codebase state",
1016
+ file: "sync.md",
1017
+ arguments: [
1018
+ { name: "SCOPE", description: "Specific path or module to sync (optional)", required: false }
1019
+ ]
1020
+ }
1021
+ ];
1022
+ }
1023
+ });
1024
+
1025
+ // src/mcp/server.ts
1026
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1027
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1028
+ import {
1029
+ CallToolRequestSchema,
1030
+ ListResourcesRequestSchema,
1031
+ ListToolsRequestSchema,
1032
+ ReadResourceRequestSchema,
1033
+ ListPromptsRequestSchema,
1034
+ GetPromptRequestSchema
1035
+ } from "@modelcontextprotocol/sdk/types.js";
1036
+ import * as fs15 from "fs";
1037
+ import * as path14 from "path";
1038
+ async function startMCPServer() {
1039
+ try {
1040
+ logger.info("Starting MCP Server...");
1041
+ process.on("uncaughtException", (error) => {
1042
+ logger.error("Uncaught Exception", error);
1043
+ console.error("Uncaught Exception:", error);
621
1044
  });
622
- } else {
623
- workspace.folders[mainFolderIndex] = {
624
- path: ".",
625
- name: `\u{1F3E0} ${workspaceName} (workspace)`
1045
+ process.on("unhandledRejection", (reason) => {
1046
+ logger.error("Unhandled Rejection", reason);
1047
+ console.error("Unhandled Rejection:", reason);
1048
+ });
1049
+ const config = loadMCPConfig();
1050
+ mcpServer = new Server(
1051
+ { name: "rrce-mcp-hub", version: "1.0.0" },
1052
+ { capabilities: { resources: {}, tools: {}, prompts: {} } }
1053
+ );
1054
+ mcpServer.onerror = (error) => {
1055
+ logger.error("MCP Server Error", error);
626
1056
  };
1057
+ registerResourceHandlers(mcpServer);
1058
+ registerToolHandlers(mcpServer);
1059
+ registerPromptHandlers(mcpServer);
1060
+ const transport = new StdioServerTransport();
1061
+ await mcpServer.connect(transport);
1062
+ serverState = { running: true, port: config.server.port, pid: process.pid };
1063
+ const exposed = getExposedProjects().map((p) => p.name).join(", ");
1064
+ logger.info(`RRCE MCP Hub started (pid: ${process.pid})`, { exposedProjects: exposed });
1065
+ console.error(`RRCE MCP Hub started (pid: ${process.pid})`);
1066
+ console.error(`Exposed projects: ${exposed}`);
1067
+ return { port: config.server.port, pid: process.pid };
1068
+ } catch (error) {
1069
+ logger.error("Failed to start MCP server", error);
1070
+ throw error;
627
1071
  }
628
- const referenceFolderPaths = [];
629
- const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
630
- if (isDetectedProjects) {
631
- const projects = linkedProjects;
1072
+ }
1073
+ function registerResourceHandlers(server) {
1074
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1075
+ logger.debug("Listing resources");
1076
+ const projects = getExposedProjects();
1077
+ const resources = [];
1078
+ resources.push({
1079
+ uri: "rrce://projects",
1080
+ name: "Project List",
1081
+ description: "List of all RRCE projects exposed via MCP",
1082
+ mimeType: "application/json"
1083
+ });
632
1084
  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}]`
1085
+ const config = loadMCPConfig();
1086
+ const permissions = getProjectPermissions(config, project.name);
1087
+ if (permissions.knowledge) {
1088
+ resources.push({
1089
+ uri: `rrce://projects/${project.name}/context`,
1090
+ name: `${project.name} - Project Context`,
1091
+ description: `Project context and architecture for ${project.name}`,
1092
+ mimeType: "text/markdown"
640
1093
  });
641
1094
  }
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]`
1095
+ if (permissions.tasks) {
1096
+ resources.push({
1097
+ uri: `rrce://projects/${project.name}/tasks`,
1098
+ name: `${project.name} - Tasks`,
1099
+ description: `Task list and status for ${project.name}`,
1100
+ mimeType: "application/json"
653
1101
  });
654
1102
  }
655
1103
  }
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;
1104
+ return { resources };
1105
+ });
1106
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1107
+ const { uri } = request.params;
1108
+ logger.info(`Reading resource: ${uri}`);
1109
+ try {
1110
+ if (uri === "rrce://projects") {
1111
+ const projects = getExposedProjects();
1112
+ return {
1113
+ contents: [{
1114
+ uri,
1115
+ mimeType: "application/json",
1116
+ text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1117
+ }]
1118
+ };
1119
+ }
1120
+ const projectMatch = uri.match(/^rrce:\/\/projects\/([^/]+)\/(.+)$/);
1121
+ if (projectMatch) {
1122
+ const [, projectName, resourceType] = projectMatch;
1123
+ const content = resourceType === "context" ? getProjectContext(projectName) : JSON.stringify(getProjectTasks(projectName), null, 2);
1124
+ if (content === null) throw new Error(`Resource not found: ${uri}`);
1125
+ return {
1126
+ contents: [{
1127
+ uri,
1128
+ mimeType: resourceType === "tasks" ? "application/json" : "text/markdown",
1129
+ text: content
1130
+ }]
1131
+ };
667
1132
  }
1133
+ throw new Error(`Unknown resource: ${uri}`);
1134
+ } catch (error) {
1135
+ logger.error(`Failed to read resource: ${uri}`, error);
1136
+ throw error;
668
1137
  }
669
- workspace.settings["files.readonlyInclude"] = {
670
- ...cleanedReadonly,
671
- ...readonlyPatterns
672
- };
673
- }
674
- fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
1138
+ });
675
1139
  }
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([]);
1140
+ function registerToolHandlers(server) {
1141
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1142
+ tools: [
1143
+ {
1144
+ name: "search_knowledge",
1145
+ description: "Search across all exposed project knowledge bases",
1146
+ inputSchema: {
1147
+ type: "object",
1148
+ properties: { query: { type: "string", description: "Search query to find in knowledge files" } },
1149
+ required: ["query"]
702
1150
  }
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
1151
  },
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);
1152
+ {
1153
+ name: "list_projects",
1154
+ description: "List all projects exposed via MCP",
1155
+ inputSchema: { type: "object", properties: {} }
1156
+ },
1157
+ {
1158
+ name: "get_project_context",
1159
+ description: "Get the project context/architecture for a specific project",
1160
+ inputSchema: {
1161
+ type: "object",
1162
+ properties: { project: { type: "string", description: "Name of the project to get context for" } },
1163
+ required: ["project"]
1164
+ }
729
1165
  }
1166
+ ]
1167
+ }));
1168
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1169
+ const { name, arguments: args } = request.params;
1170
+ logger.info(`Calling tool: ${name}`, args);
1171
+ try {
1172
+ switch (name) {
1173
+ case "search_knowledge": {
1174
+ const results = searchKnowledge(args.query);
1175
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
1176
+ }
1177
+ case "list_projects": {
1178
+ const projects = getExposedProjects();
1179
+ return {
1180
+ content: [{
1181
+ type: "text",
1182
+ text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
1183
+ }]
1184
+ };
1185
+ }
1186
+ case "get_project_context": {
1187
+ const context = getProjectContext(args.project);
1188
+ if (!context) {
1189
+ const msg = `No project context found for "${args.project}"`;
1190
+ logger.warn(msg);
1191
+ return { content: [{ type: "text", text: msg }], isError: true };
1192
+ }
1193
+ return { content: [{ type: "text", text: context }] };
1194
+ }
1195
+ default:
1196
+ throw new Error(`Unknown tool: ${name}`);
1197
+ }
1198
+ } catch (error) {
1199
+ logger.error(`Tool execution failed: ${name}`, error);
1200
+ throw error;
730
1201
  }
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.`));
1202
+ });
1203
+ }
1204
+ function registerPromptHandlers(server) {
1205
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
1206
+ prompts: AGENT_PROMPTS.map((p) => ({
1207
+ name: p.name,
1208
+ description: p.description,
1209
+ arguments: p.arguments.map((a) => ({ name: a.name, description: a.description, required: a.required }))
1210
+ }))
1211
+ }));
1212
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1213
+ const { name, arguments: args } = request.params;
1214
+ logger.info(`Getting prompt: ${name}`, args);
1215
+ try {
1216
+ const promptDef = AGENT_PROMPTS.find((p) => p.name === name);
1217
+ if (!promptDef) throw new Error(`Unknown prompt: ${name}`);
1218
+ const promptsDir = getAgentCorePromptsDir();
1219
+ const promptPath = path14.join(promptsDir, promptDef.file);
1220
+ if (!fs15.existsSync(promptPath)) throw new Error(`Prompt file not found: ${promptDef.file}`);
1221
+ let promptContent = fs15.readFileSync(promptPath, "utf-8");
1222
+ if (args) {
1223
+ for (const [key, value] of Object.entries(args)) {
1224
+ promptContent = promptContent.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(value));
1225
+ }
1226
+ }
1227
+ return {
1228
+ description: promptDef.description,
1229
+ messages: [{ role: "user", content: { type: "text", text: promptContent } }]
1230
+ };
1231
+ } catch (error) {
1232
+ logger.error(`Failed to get prompt: ${name}`, error);
1233
+ throw error;
784
1234
  }
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
- }
1235
+ });
790
1236
  }
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");
1237
+ function stopMCPServer() {
1238
+ if (mcpServer) {
1239
+ logger.info("Stopping MCP Server...");
1240
+ mcpServer.close();
1241
+ mcpServer = null;
813
1242
  }
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}"`;
1243
+ serverState = { running: false };
1244
+ logger.info("RRCE MCP Hub stopped");
1245
+ console.error("RRCE MCP Hub stopped");
1246
+ }
1247
+ function getMCPServerStatus() {
1248
+ return { ...serverState };
1249
+ }
1250
+ var serverState, mcpServer;
1251
+ var init_server = __esm({
1252
+ "src/mcp/server.ts"() {
1253
+ "use strict";
1254
+ init_logger();
1255
+ init_config();
1256
+ init_resources();
1257
+ init_prompts2();
1258
+ init_prompts();
1259
+ serverState = { running: false };
1260
+ mcpServer = null;
824
1261
  }
825
- configContent += `
826
-
827
- project:
828
- name: "${workspaceName}"
1262
+ });
829
1263
 
830
- tools:
831
- copilot: ${config.tools.includes("copilot")}
832
- antigravity: ${config.tools.includes("antigravity")}
833
- `;
834
- if (config.linkedProjects.length > 0) {
835
- configContent += `
836
- linked_projects:
837
- `;
838
- config.linkedProjects.forEach((name) => {
839
- configContent += ` - ${name}
840
- `;
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);
852
- }
1264
+ // src/mcp/install.ts
1265
+ import * as fs16 from "fs";
1266
+ import * as path15 from "path";
1267
+ import * as os from "os";
1268
+ function checkInstallStatus() {
1269
+ return {
1270
+ antigravity: checkConfigFile(ANTIGRAVITY_CONFIG),
1271
+ claude: checkConfigFile(CLAUDE_CONFIG)
1272
+ };
853
1273
  }
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];
1274
+ function checkConfigFile(configPath) {
1275
+ if (!fs16.existsSync(configPath)) return false;
1276
+ try {
1277
+ const content = JSON.parse(fs16.readFileSync(configPath, "utf-8"));
1278
+ return !!content.mcpServers?.["rrce"];
1279
+ } catch {
1280
+ return false;
864
1281
  }
865
1282
  }
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/");
1283
+ function installToConfig(target) {
1284
+ const configPath = target === "antigravity" ? ANTIGRAVITY_CONFIG : CLAUDE_CONFIG;
1285
+ const dir = path15.dirname(configPath);
1286
+ if (!fs16.existsSync(dir)) {
1287
+ fs16.mkdirSync(dir, { recursive: true });
871
1288
  }
872
- if (tools.includes("copilot")) {
873
- entries.push(".github/agents/");
874
- }
875
- if (tools.includes("antigravity")) {
876
- entries.push(".agent/");
1289
+ let config = { mcpServers: {} };
1290
+ if (fs16.existsSync(configPath)) {
1291
+ try {
1292
+ config = JSON.parse(fs16.readFileSync(configPath, "utf-8"));
1293
+ } catch {
1294
+ }
877
1295
  }
878
- if (entries.length === 0) {
1296
+ if (!config.mcpServers) config.mcpServers = {};
1297
+ config.mcpServers["rrce"] = {
1298
+ command: "npx",
1299
+ args: ["-y", "rrce-workflow", "mcp", "start"]
1300
+ // -y to avoid interactive prompts
1301
+ };
1302
+ try {
1303
+ fs16.writeFileSync(configPath, JSON.stringify(config, null, 2));
1304
+ return true;
1305
+ } catch {
879
1306
  return false;
880
1307
  }
881
- try {
882
- let content = "";
883
- if (fs7.existsSync(gitignorePath)) {
884
- content = fs7.readFileSync(gitignorePath, "utf-8");
1308
+ }
1309
+ var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG;
1310
+ var init_install = __esm({
1311
+ "src/mcp/install.ts"() {
1312
+ "use strict";
1313
+ ANTIGRAVITY_CONFIG = path15.join(os.homedir(), ".gemini/antigravity/mcp_config.json");
1314
+ CLAUDE_CONFIG = path15.join(os.homedir(), ".config/claude/claude_desktop_config.json");
1315
+ }
1316
+ });
1317
+
1318
+ // src/mcp/index.ts
1319
+ var mcp_exports = {};
1320
+ __export(mcp_exports, {
1321
+ runMCP: () => runMCP
1322
+ });
1323
+ 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";
1324
+ import pc8 from "picocolors";
1325
+ async function runMCP(subcommand2) {
1326
+ if (subcommand2) {
1327
+ switch (subcommand2) {
1328
+ case "start":
1329
+ await startMCPServer();
1330
+ await new Promise(() => {
1331
+ });
1332
+ return;
1333
+ case "stop":
1334
+ await handleStopServer();
1335
+ return;
1336
+ case "status":
1337
+ await handleShowStatus();
1338
+ return;
1339
+ case "help":
1340
+ showHelp();
1341
+ return;
885
1342
  }
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
- }
1343
+ }
1344
+ intro2(pc8.bgCyan(pc8.black(" RRCE MCP Hub ")));
1345
+ const globalPathCheck = await ensureMCPGlobalPath();
1346
+ if (!globalPathCheck.configured) {
1347
+ const configured = await handleConfigureGlobalPath();
1348
+ if (!configured) {
1349
+ outro6(pc8.yellow("MCP requires a global storage path. Setup cancelled."));
1350
+ return;
897
1351
  }
898
- if (newEntries.length === 0) {
899
- return false;
1352
+ }
1353
+ const status = checkInstallStatus();
1354
+ if (!status.antigravity && !status.claude) {
1355
+ const shouldInstall = await confirm4({
1356
+ message: "MCP server is not installed in your IDEs. Install now?",
1357
+ initialValue: true
1358
+ });
1359
+ if (shouldInstall && !isCancel7(shouldInstall)) {
1360
+ await handleInstallWizard();
900
1361
  }
901
- let newContent = content;
902
- if (!newContent.endsWith("\n") && newContent !== "") {
903
- newContent += "\n";
1362
+ }
1363
+ let running = true;
1364
+ while (running) {
1365
+ const serverStatus = getMCPServerStatus();
1366
+ const serverLabel = serverStatus.running ? pc8.green("Running") : pc8.dim("Stopped");
1367
+ const action = await select4({
1368
+ message: "What would you like to do?",
1369
+ options: [
1370
+ { value: "start", label: `\u25B6\uFE0F Start MCP server`, hint: `Current status: ${serverLabel}` },
1371
+ { value: "configure", label: "\u2699\uFE0F Configure projects", hint: "Choose which projects to expose" },
1372
+ { value: "install", label: "\u{1F4E5} Install to IDE", hint: "Add to Antigravity or Claude Desktop" },
1373
+ { value: "status", label: "\u{1F4CB} View status", hint: "See details" },
1374
+ { value: "help", label: "\u2753 Help", hint: "Learn about MCP Hub" },
1375
+ { value: "exit", label: "\u21A9 Exit", hint: "Return to shell" }
1376
+ ]
1377
+ });
1378
+ if (isCancel7(action)) {
1379
+ cancel6("MCP Hub closed.");
1380
+ return;
904
1381
  }
905
- if (newContent === "" || !content.includes("# rrce-workflow")) {
906
- newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
1382
+ switch (action) {
1383
+ case "start":
1384
+ await handleStartServer();
1385
+ break;
1386
+ case "configure":
1387
+ await handleConfigure();
1388
+ break;
1389
+ case "install":
1390
+ await handleInstallWizard();
1391
+ break;
1392
+ case "status":
1393
+ await handleShowStatus();
1394
+ break;
1395
+ case "help":
1396
+ showHelp();
1397
+ break;
1398
+ case "exit":
1399
+ running = false;
1400
+ break;
907
1401
  }
908
- newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
909
- fs7.writeFileSync(gitignorePath, newContent);
910
- return true;
911
- } catch {
912
- return false;
913
1402
  }
1403
+ outro6(pc8.green("MCP Hub closed."));
914
1404
  }
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
1405
+ async function handleInstallWizard() {
1406
+ const status = checkInstallStatus();
1407
+ const options = [
1408
+ { value: "antigravity", label: "Antigravity IDE", hint: status.antigravity ? "Installed" : "Not installed" },
1409
+ { value: "claude", label: "Claude Desktop", hint: status.claude ? "Installed" : "Not installed" }
1410
+ ];
1411
+ const selected = await multiselect3({
1412
+ message: "Select where to install RRCE MCP Server:",
1413
+ options,
1414
+ initialValues: [
1415
+ ...status.antigravity ? ["antigravity"] : [],
1416
+ ...status.claude ? ["claude"] : []
1417
+ ],
1418
+ required: false
925
1419
  });
926
- if (projects.length === 0) {
927
- outro2(pc4.yellow("No other projects found. Try setting up another project first."));
928
- return;
1420
+ if (isCancel7(selected)) return;
1421
+ const targets = selected;
1422
+ const results = [];
1423
+ for (const target of targets) {
1424
+ const success = installToConfig(target);
1425
+ results.push(`${target}: ${success ? pc8.green("Success") : pc8.red("Failed")}`);
929
1426
  }
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);
1427
+ if (results.length > 0) {
1428
+ note7(results.join("\n"), "Installation Results");
946
1429
  }
947
- const selectedKeys = linkedProjects;
948
- if (selectedKeys.length === 0) {
949
- outro2("No projects selected.");
950
- return;
1430
+ }
1431
+ async function handleStartServer() {
1432
+ const fs17 = await import("fs");
1433
+ const { getLogFilePath: getLogFilePath2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1434
+ const config = loadMCPConfig();
1435
+ const exposedCount = config.projects.filter((p) => p.expose).length;
1436
+ if (exposedCount === 0 && !config.defaults.includeNew) {
1437
+ const shouldConfig = await confirm4({
1438
+ message: "No projects are currently exposed. Configure now?",
1439
+ initialValue: true
1440
+ });
1441
+ if (shouldConfig && !isCancel7(shouldConfig)) {
1442
+ await handleConfigure();
1443
+ }
951
1444
  }
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");
1445
+ const portInput = await text2({
1446
+ message: "Select port for MCP Server",
1447
+ initialValue: config.server.port.toString(),
1448
+ placeholder: "3200",
1449
+ validate(value) {
1450
+ if (isNaN(Number(value))) return "Port must be a number";
975
1451
  }
976
- } else {
977
- configContent += `
978
- linked_projects:
979
- `;
980
- selectedProjects.forEach((project) => {
981
- configContent += ` - ${project.name}:${project.source}
982
- `;
1452
+ });
1453
+ if (isCancel7(portInput)) return;
1454
+ const newPort = parseInt(portInput, 10);
1455
+ if (newPort !== config.server.port) {
1456
+ config.server.port = newPort;
1457
+ saveMCPConfig(config);
1458
+ }
1459
+ console.clear();
1460
+ const logPath = getLogFilePath2();
1461
+ const renderHeader = () => {
1462
+ console.clear();
1463
+ 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"));
1464
+ console.log(pc8.cyan("\u2551 RRCE MCP Hub Running \u2551"));
1465
+ 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"));
1466
+ console.log(pc8.dim("Server is running in Stdio mode (JSON-RPC)."));
1467
+ console.log(pc8.dim(`Port: ${newPort} | PID: ${process.pid}`));
1468
+ console.log(pc8.dim(`Logging to: ${logPath}`));
1469
+ console.log(pc8.dim("---------------------------------------------"));
1470
+ };
1471
+ const renderFooter = () => {
1472
+ console.log(pc8.dim("---------------------------------------------"));
1473
+ console.log(pc8.bgBlue(pc8.white(pc8.bold(" COMMANDS "))));
1474
+ console.log(`${pc8.bold("q")}: Stop & Quit ${pc8.bold("g")}: Configure Projects ${pc8.bold("p")}: Change Port`);
1475
+ };
1476
+ renderHeader();
1477
+ renderFooter();
1478
+ try {
1479
+ await startMCPServer();
1480
+ let lastSize = 0;
1481
+ if (fs17.existsSync(logPath)) {
1482
+ const stats = fs17.statSync(logPath);
1483
+ lastSize = stats.size;
1484
+ }
1485
+ let isRunning = true;
1486
+ if (process.stdin.setRawMode) {
1487
+ process.stdin.setRawMode(true);
1488
+ process.stdin.resume();
1489
+ process.stdin.setEncoding("utf8");
1490
+ }
1491
+ return new Promise((resolve) => {
1492
+ const onKey = async (key) => {
1493
+ if (key === "" || key.toLowerCase() === "q") {
1494
+ cleanup();
1495
+ resolve();
1496
+ }
1497
+ if (key.toLowerCase() === "p" || key.toLowerCase() === "g") {
1498
+ cleanup();
1499
+ resolve();
1500
+ }
1501
+ };
1502
+ process.stdin.on("data", onKey);
1503
+ const interval = setInterval(() => {
1504
+ if (!isRunning) return;
1505
+ if (fs17.existsSync(logPath)) {
1506
+ const stats = fs17.statSync(logPath);
1507
+ if (stats.size > lastSize) {
1508
+ const stream = fs17.createReadStream(logPath, {
1509
+ start: lastSize,
1510
+ end: stats.size,
1511
+ encoding: "utf-8"
1512
+ });
1513
+ stream.on("data", (chunk) => {
1514
+ process.stderr.write(chunk);
1515
+ });
1516
+ lastSize = stats.size;
1517
+ }
1518
+ }
1519
+ }, 500);
1520
+ const cleanup = () => {
1521
+ isRunning = false;
1522
+ clearInterval(interval);
1523
+ if (process.stdin.setRawMode) {
1524
+ process.stdin.setRawMode(false);
1525
+ }
1526
+ process.stdin.removeListener("data", onKey);
1527
+ process.stdin.pause();
1528
+ stopMCPServer();
1529
+ console.log("");
1530
+ };
983
1531
  });
1532
+ } catch (error) {
1533
+ if (process.stdin.setRawMode) {
1534
+ process.stdin.setRawMode(false);
1535
+ }
1536
+ console.error(pc8.red("\nFailed to start server:"));
1537
+ console.error(error);
984
1538
  }
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
1539
  }
1540
+ async function handleConfigureGlobalPath() {
1541
+ const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
1542
+ const fs17 = await import("fs");
1543
+ const path17 = await import("path");
1544
+ note7(
1545
+ `MCP Hub requires a ${pc8.bold("global storage path")} to store its configuration
1546
+ and coordinate across projects.
998
1547
 
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))
1012
- );
1013
- if (existingDirs.length === 0) {
1014
- outro3(pc5.yellow("No data found in workspace storage to sync."));
1015
- return;
1016
- }
1017
- note4(
1018
- `The following will be copied to global storage:
1019
- ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
1020
-
1021
- Destination: ${pc5.cyan(globalPath)}`,
1022
- "Sync Preview"
1548
+ Your current setup uses ${pc8.cyan("workspace")} mode, which stores data
1549
+ locally in each project. MCP needs a central location.`,
1550
+ "Global Path Required"
1023
1551
  );
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.");
1030
- return;
1552
+ const resolvedPath = await resolveGlobalPath2();
1553
+ if (!resolvedPath) {
1554
+ return false;
1031
1555
  }
1032
- const s = spinner3();
1033
- s.start("Syncing to global storage");
1034
1556
  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);
1557
+ if (!fs17.existsSync(resolvedPath)) {
1558
+ fs17.mkdirSync(resolvedPath, { recursive: true });
1041
1559
  }
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!"));
1560
+ const config = loadMCPConfig();
1561
+ saveMCPConfig(config);
1562
+ note7(
1563
+ `${pc8.green("\u2713")} Global path configured: ${pc8.cyan(resolvedPath)}
1564
+
1565
+ MCP config will be stored at:
1566
+ ${path17.join(resolvedPath, "mcp.yaml")}`,
1567
+ "Configuration Saved"
1568
+ );
1569
+ return true;
1053
1570
  } catch (error) {
1054
- s.stop("Error occurred");
1055
- cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
1056
- process.exit(1);
1057
- }
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"
1571
+ note7(
1572
+ `${pc8.red("\u2717")} Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,
1573
+ "Error"
1084
1574
  );
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);
1575
+ return false;
1123
1576
  }
1124
1577
  }
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];
1578
+ async function handleShowStatus() {
1579
+ const s = spinner6();
1580
+ s.start("Loading projects...");
1581
+ const config = loadMCPConfig();
1582
+ const projects = scanForProjects();
1583
+ s.stop("Projects loaded");
1584
+ if (projects.length === 0) {
1585
+ note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1586
+ return;
1135
1587
  }
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
1156
- });
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
- }
1588
+ const lines = [
1589
+ `${pc8.bold("Project Status")}`,
1590
+ ""
1591
+ ];
1592
+ for (const project of projects) {
1593
+ const projectConfig = config.projects.find((p) => p.name === project.name);
1594
+ const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1595
+ const status = isExposed ? pc8.green("\u2713 exposed") : pc8.dim("\u25CB hidden");
1596
+ const source = pc8.dim(`(${project.source})`);
1597
+ lines.push(` ${status} ${project.name} ${source}`);
1167
1598
  }
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
- }
1599
+ lines.push("");
1600
+ lines.push(pc8.dim(`Config: ${getMCPConfigPath()}`));
1601
+ const serverStatus = getMCPServerStatus();
1602
+ if (serverStatus.running) {
1603
+ lines.push(pc8.green(`Server: running on port ${serverStatus.port}`));
1604
+ } else {
1605
+ lines.push(pc8.dim("Server: not running"));
1208
1606
  }
1209
- await runSetupFlow(workspacePath, workspaceName, detectedProjects);
1607
+ note7(lines.join("\n"), "MCP Hub Status");
1210
1608
  }
1211
-
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);
1609
+ async function handleConfigure() {
1610
+ const { logger: logger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
1611
+ const s = spinner6();
1612
+ s.start("Scanning for projects...");
1613
+ const config = loadMCPConfig();
1614
+ const projects = scanForProjects();
1615
+ logger2.info("Configure: Loaded config", { projects: config.projects, defaultMode: config.defaults.includeNew });
1616
+ s.stop("Projects found");
1617
+ if (projects.length === 0) {
1618
+ note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1619
+ return;
1223
1620
  }
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
- }))
1621
+ const options = projects.map((project) => {
1622
+ const projectConfig = config.projects.find((p) => p.name === project.name);
1623
+ const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
1624
+ return {
1625
+ value: project.name,
1626
+ label: `${project.name} ${pc8.dim(`(${project.source})`)}`,
1627
+ hint: project.dataPath
1628
+ };
1231
1629
  });
1232
- if (isCancel7(selection)) {
1233
- cancel6("Selection cancelled.");
1234
- process.exit(0);
1630
+ const currentlyExposed = projects.filter((p) => {
1631
+ const cfg = config.projects.find((c) => c.name === p.name);
1632
+ return cfg?.expose ?? config.defaults.includeNew;
1633
+ }).map((p) => p.name);
1634
+ const selected = await multiselect3({
1635
+ message: "Select projects to expose via MCP:",
1636
+ options,
1637
+ initialValues: currentlyExposed,
1638
+ required: false
1639
+ });
1640
+ if (isCancel7(selected)) {
1641
+ return;
1235
1642
  }
1236
- const prompt = selection;
1643
+ const selectedNames = selected;
1644
+ logger2.info("Configure: User selected projects", selectedNames);
1645
+ for (const project of projects) {
1646
+ const shouldExpose = selectedNames.includes(project.name);
1647
+ setProjectConfig(config, project.name, shouldExpose);
1648
+ }
1649
+ saveMCPConfig(config);
1650
+ logger2.info("Configure: Config saved", config);
1651
+ const exposedCount = selectedNames.length;
1237
1652
  note7(
1238
- `Use this agent in your IDE by invoking:
1239
- ${pc8.bold(pc8.cyan(`@${prompt.frontmatter.name}`))}`,
1240
- "Agent Selected"
1653
+ `${pc8.green("\u2713")} Configuration saved!
1654
+
1655
+ Exposed projects: ${exposedCount}
1656
+ Hidden projects: ${projects.length - exposedCount}`,
1657
+ "Configuration Updated"
1241
1658
  );
1242
- outro6("Done");
1243
1659
  }
1660
+ async function handleStopServer() {
1661
+ const status = getMCPServerStatus();
1662
+ if (!status.running) {
1663
+ note7("MCP server is not running.", "Status");
1664
+ return;
1665
+ }
1666
+ const confirmed = await confirm4({
1667
+ message: "Stop the MCP server?",
1668
+ initialValue: true
1669
+ });
1670
+ if (isCancel7(confirmed) || !confirmed) {
1671
+ return;
1672
+ }
1673
+ stopMCPServer();
1674
+ note7(pc8.green("MCP server stopped."), "Server Stopped");
1675
+ }
1676
+ function showHelp() {
1677
+ const help = `
1678
+ ${pc8.bold("RRCE MCP Hub")} - Cross-project AI assistant server
1244
1679
 
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";
1680
+ ${pc8.bold("ABOUT")}
1681
+ MCP (Model Context Protocol) allows AI assistants like Claude to
1682
+ access your project knowledge in real-time. The RRCE MCP Hub
1683
+ provides a central server that exposes selected projects.
1248
1684
 
1249
- // src/mcp/config.ts
1250
- init_paths();
1251
- import * as fs12 from "fs";
1252
- import * as path12 from "path";
1685
+ ${pc8.bold("MENU OPTIONS")}
1686
+ ${pc8.cyan("View project status")} See which projects are exposed
1687
+ ${pc8.cyan("Configure projects")} Choose which projects to expose
1688
+ ${pc8.cyan("Start MCP server")} Start the server for AI access
1689
+ ${pc8.cyan("Stop MCP server")} Stop the running server
1253
1690
 
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
1691
+ ${pc8.bold("DIRECT COMMANDS")}
1692
+ ${pc8.dim("rrce-workflow mcp start")} Start server directly
1693
+ ${pc8.dim("rrce-workflow mcp stop")} Stop server directly
1694
+ ${pc8.dim("rrce-workflow mcp status")} Show status directly
1695
+ ${pc8.dim("rrce-workflow mcp help")} Show this help
1696
+
1697
+ ${pc8.bold("CLAUDE DESKTOP SETUP")}
1698
+ Add to ${pc8.cyan("~/.config/claude/claude_desktop_config.json")}:
1699
+ ${pc8.dim(`{
1700
+ "mcpServers": {
1701
+ "rrce": {
1702
+ "command": "npx",
1703
+ "args": ["rrce-workflow", "mcp", "start"]
1267
1704
  }
1268
1705
  }
1269
- };
1270
- var DEFAULT_PERMISSIONS = {
1271
- knowledge: true,
1272
- tasks: true,
1273
- refs: true
1274
- };
1706
+ }`)}
1275
1707
 
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
- }
1297
- }
1298
- }
1299
- return {
1300
- configured: true,
1301
- path: rrceHome
1302
- };
1708
+ ${pc8.bold("RESOURCES EXPOSED")}
1709
+ ${pc8.cyan("rrce://projects")} List all exposed projects
1710
+ ${pc8.cyan("rrce://projects/{name}/context")} Get project context
1711
+ ${pc8.cyan("rrce://projects/{name}/tasks")} Get project tasks
1712
+
1713
+ ${pc8.bold("PROMPTS (Agent Commands)")}
1714
+ ${pc8.cyan("init")} Initialize project context
1715
+ ${pc8.cyan("research")} Research requirements for a task
1716
+ ${pc8.cyan("plan")} Create execution plan
1717
+ ${pc8.cyan("execute")} Implement planned work
1718
+ ${pc8.cyan("docs")} Generate documentation
1719
+ ${pc8.cyan("sync")} Sync knowledge with codebase
1720
+ `;
1721
+ note7(help.trim(), "Help");
1303
1722
  }
1304
- function loadMCPConfig() {
1305
- const configPath = getMCPConfigPath();
1306
- if (!fs12.existsSync(configPath)) {
1307
- return { ...DEFAULT_MCP_CONFIG };
1723
+ var init_mcp = __esm({
1724
+ "src/mcp/index.ts"() {
1725
+ "use strict";
1726
+ init_config();
1727
+ init_detection();
1728
+ init_server();
1729
+ init_install();
1308
1730
  }
1731
+ });
1732
+
1733
+ // src/commands/wizard/index.ts
1734
+ import { intro, select as select3, spinner as spinner5, note as note6, outro as outro5, isCancel as isCancel6 } from "@clack/prompts";
1735
+ import pc7 from "picocolors";
1736
+ import * as fs11 from "fs";
1737
+
1738
+ // src/lib/git.ts
1739
+ import { execSync } from "child_process";
1740
+ function getGitUser() {
1309
1741
  try {
1310
- const content = fs12.readFileSync(configPath, "utf-8");
1311
- return parseMCPConfig(content);
1742
+ const result = execSync("git config user.name", { encoding: "utf-8" });
1743
+ return result.trim() || null;
1312
1744
  } 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
- }
1745
+ return null;
1349
1746
  }
1350
- return config;
1351
1747
  }
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
1748
 
1366
- server:
1367
- port: ${config.server.port}
1368
- autoStart: ${config.server.autoStart}
1749
+ // src/commands/wizard/index.ts
1750
+ init_paths();
1751
+ init_detection();
1369
1752
 
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}
1753
+ // src/commands/wizard/setup-flow.ts
1754
+ init_paths();
1755
+ init_prompts();
1756
+ import { group, select as select2, multiselect, confirm, spinner, note as note2, outro, cancel } from "@clack/prompts";
1757
+ import pc3 from "picocolors";
1758
+ import * as fs7 from "fs";
1759
+ import * as path8 from "path";
1376
1760
 
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
- }
1761
+ // src/commands/wizard/utils.ts
1762
+ init_paths();
1763
+ import * as fs4 from "fs";
1764
+ import * as path4 from "path";
1765
+ function copyPromptsToDir(prompts, targetDir, extension) {
1766
+ for (const prompt of prompts) {
1767
+ const baseName = path4.basename(prompt.filePath, ".md");
1768
+ const targetName = baseName + extension;
1769
+ const targetPath = path4.join(targetDir, targetName);
1770
+ const content = fs4.readFileSync(prompt.filePath, "utf-8");
1771
+ fs4.writeFileSync(targetPath, content);
1391
1772
  }
1392
- return content;
1393
1773
  }
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 };
1774
+ function copyDirRecursive(src, dest) {
1775
+ const entries = fs4.readdirSync(src, { withFileTypes: true });
1776
+ for (const entry of entries) {
1777
+ const srcPath = path4.join(src, entry.name);
1778
+ const destPath = path4.join(dest, entry.name);
1779
+ if (entry.isDirectory()) {
1780
+ ensureDir(destPath);
1781
+ copyDirRecursive(srcPath, destPath);
1782
+ } else {
1783
+ fs4.copyFileSync(srcPath, destPath);
1400
1784
  }
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
1785
  }
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
1786
  }
1421
1787
 
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 [];
1788
+ // src/commands/wizard/vscode.ts
1789
+ init_paths();
1790
+ init_detection();
1791
+ import * as fs5 from "fs";
1792
+ import * as path5 from "path";
1793
+ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
1794
+ const workspaceFilePath = path5.join(workspacePath, `${workspaceName}.code-workspace`);
1795
+ let workspace;
1796
+ if (fs5.existsSync(workspaceFilePath)) {
1797
+ try {
1798
+ const content = fs5.readFileSync(workspaceFilePath, "utf-8");
1799
+ workspace = JSON.parse(content);
1800
+ } catch {
1801
+ workspace = { folders: [], settings: {} };
1802
+ }
1803
+ } else {
1804
+ workspace = { folders: [], settings: {} };
1468
1805
  }
1469
- const permissions = getProjectPermissions(config, projectName);
1470
- if (!permissions.tasks) {
1471
- return [];
1806
+ if (!workspace.settings) {
1807
+ workspace.settings = {};
1472
1808
  }
1473
- const projects = scanForProjects();
1474
- const project = projects.find((p) => p.name === projectName);
1475
- if (!project?.tasksPath || !fs13.existsSync(project.tasksPath)) {
1476
- return [];
1809
+ workspace.folders = workspace.folders.filter(
1810
+ (f) => f.path === "." || !f.name?.startsWith("\u{1F4C1}") && !f.name?.startsWith("\u{1F4DA}") && !f.name?.startsWith("\u{1F4CE}") && !f.name?.startsWith("\u{1F4CB}")
1811
+ );
1812
+ const mainFolderIndex = workspace.folders.findIndex((f) => f.path === ".");
1813
+ if (mainFolderIndex === -1) {
1814
+ workspace.folders.unshift({
1815
+ path: ".",
1816
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
1817
+ });
1818
+ } else {
1819
+ workspace.folders[mainFolderIndex] = {
1820
+ path: ".",
1821
+ name: `\u{1F3E0} ${workspaceName} (workspace)`
1822
+ };
1477
1823
  }
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
- }
1824
+ const referenceFolderPaths = [];
1825
+ const isDetectedProjects = linkedProjects.length > 0 && typeof linkedProjects[0] === "object";
1826
+ if (isDetectedProjects) {
1827
+ const projects = linkedProjects;
1828
+ for (const project of projects) {
1829
+ const sourceLabel = project.source === "global" ? "global" : "local";
1830
+ const folderPath = project.dataPath;
1831
+ if (fs5.existsSync(folderPath)) {
1832
+ referenceFolderPaths.push(folderPath);
1833
+ workspace.folders.push({
1834
+ path: folderPath,
1835
+ name: `\u{1F4C1} ${project.name} [${sourceLabel}]`
1836
+ });
1490
1837
  }
1491
1838
  }
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
- }
1839
+ } else {
1840
+ const projectNames = linkedProjects;
1841
+ const rrceHome = customGlobalPath || getRRCEHome();
1842
+ for (const projectName of projectNames) {
1843
+ const folderPath = path5.join(rrceHome, "workspaces", projectName);
1844
+ if (fs5.existsSync(folderPath)) {
1845
+ referenceFolderPaths.push(folderPath);
1846
+ workspace.folders.push({
1847
+ path: folderPath,
1848
+ name: `\u{1F4C1} ${projectName} [global]`
1849
+ });
1525
1850
  }
1526
- } catch {
1527
1851
  }
1528
1852
  }
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
- ]
1853
+ if (referenceFolderPaths.length > 0) {
1854
+ const readonlyPatterns = {};
1855
+ for (const folderPath of referenceFolderPaths) {
1856
+ readonlyPatterns[`${folderPath}/**`] = true;
1857
+ }
1858
+ const existingReadonly = workspace.settings["files.readonlyInclude"] || {};
1859
+ const cleanedReadonly = {};
1860
+ for (const [pattern, value] of Object.entries(existingReadonly)) {
1861
+ if (!pattern.includes(".rrce-workflow") && !pattern.includes("rrce-workflow/workspaces")) {
1862
+ cleanedReadonly[pattern] = value;
1863
+ }
1864
+ }
1865
+ workspace.settings["files.readonlyInclude"] = {
1866
+ ...cleanedReadonly,
1867
+ ...readonlyPatterns
1868
+ };
1585
1869
  }
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 };
1870
+ fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
1606
1871
  }
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"
1872
+
1873
+ // src/commands/wizard/setup-flow.ts
1874
+ init_detection();
1875
+ init_tui_utils();
1876
+ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
1877
+ const s = spinner();
1878
+ const config = await group(
1879
+ {
1880
+ storageMode: () => select2({
1881
+ message: "Where should workflow data be stored?",
1882
+ options: [
1883
+ { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
1884
+ { value: "workspace", label: "Workspace (.rrce-workflow/)", hint: "Self-contained, version with repo" }
1885
+ ],
1886
+ initialValue: "global"
1887
+ }),
1888
+ tools: () => multiselect({
1889
+ message: "Which AI tools do you use?",
1890
+ options: [
1891
+ { value: "copilot", label: "GitHub Copilot", hint: "VSCode" },
1892
+ { value: "antigravity", label: "Antigravity IDE" }
1893
+ ],
1894
+ required: false
1895
+ }),
1896
+ linkedProjects: () => {
1897
+ if (existingProjects.length === 0) {
1898
+ return Promise.resolve([]);
1899
+ }
1900
+ return multiselect({
1901
+ message: "Link knowledge from other projects?",
1902
+ options: existingProjects.map((project) => ({
1903
+ value: `${project.name}:${project.source}`,
1904
+ // Unique key
1905
+ label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
1906
+ hint: pc3.dim(
1907
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
1908
+ )
1909
+ })),
1910
+ required: false
1634
1911
  });
1912
+ },
1913
+ addToGitignore: () => confirm({
1914
+ message: "Add generated folders to .gitignore? (as comments - uncomment if needed)",
1915
+ initialValue: true
1916
+ }),
1917
+ confirm: () => confirm({
1918
+ message: "Create configuration?",
1919
+ initialValue: true
1920
+ })
1921
+ },
1922
+ {
1923
+ onCancel: () => {
1924
+ cancel("Setup process cancelled.");
1925
+ process.exit(0);
1635
1926
  }
1636
1927
  }
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
- };
1928
+ );
1929
+ if (!config.confirm) {
1930
+ outro("Setup cancelled by user.");
1931
+ process.exit(0);
1932
+ }
1933
+ let customGlobalPath;
1934
+ if (config.storageMode === "global") {
1935
+ customGlobalPath = await resolveGlobalPath();
1936
+ if (!customGlobalPath) {
1937
+ cancel("Setup cancelled - no writable global path available.");
1938
+ process.exit(1);
1650
1939
  }
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
- };
1940
+ }
1941
+ s.start("Generating configuration");
1942
+ try {
1943
+ await generateConfiguration({
1944
+ storageMode: config.storageMode,
1945
+ globalPath: customGlobalPath,
1946
+ tools: config.tools,
1947
+ linkedProjects: config.linkedProjects,
1948
+ addToGitignore: config.addToGitignore
1949
+ }, workspacePath, workspaceName, existingProjects);
1950
+ s.stop("Configuration generated");
1951
+ const dataPaths = getDataPaths(
1952
+ config.storageMode,
1953
+ workspaceName,
1954
+ workspacePath,
1955
+ customGlobalPath
1956
+ );
1957
+ const summary = [
1958
+ `Storage: ${config.storageMode}`
1959
+ ];
1960
+ if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
1961
+ summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
1663
1962
  }
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}`);
1963
+ if (dataPaths.length > 0) {
1964
+ summary.push(`Data paths:`);
1965
+ dataPaths.forEach((p) => summary.push(` - ${p}`));
1720
1966
  }
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
- }
1967
+ const selectedTools = config.tools;
1968
+ if (selectedTools.length > 0) {
1969
+ summary.push(`Tools: ${selectedTools.join(", ")}`);
1743
1970
  }
1744
- return {
1745
- description: promptDef.description,
1746
- messages: [{ role: "user", content: { type: "text", text: promptContent } }]
1747
- };
1748
- });
1971
+ const linkedProjects = config.linkedProjects;
1972
+ if (linkedProjects.length > 0) {
1973
+ summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
1974
+ summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
1975
+ }
1976
+ note2(summary.join("\n"), "Setup Summary");
1977
+ if (linkedProjects.length > 0) {
1978
+ outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
1979
+ } else {
1980
+ outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
1981
+ }
1982
+ } catch (error) {
1983
+ s.stop("Error occurred");
1984
+ cancel(`Failed to setup: ${error instanceof Error ? error.message : String(error)}`);
1985
+ process.exit(1);
1986
+ }
1749
1987
  }
1750
- function stopMCPServer() {
1751
- if (mcpServer) {
1752
- mcpServer.close();
1753
- mcpServer = null;
1988
+ async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
1989
+ const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
1990
+ for (const dataPath of dataPaths) {
1991
+ ensureDir(dataPath);
1992
+ ensureDir(path8.join(dataPath, "knowledge"));
1993
+ ensureDir(path8.join(dataPath, "refs"));
1994
+ ensureDir(path8.join(dataPath, "tasks"));
1995
+ ensureDir(path8.join(dataPath, "templates"));
1996
+ }
1997
+ const agentCoreDir = getAgentCoreDir();
1998
+ syncMetadataToAll(agentCoreDir, dataPaths);
1999
+ copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
2000
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2001
+ if (config.tools.includes("copilot")) {
2002
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
2003
+ ensureDir(copilotPath);
2004
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
2005
+ }
2006
+ if (config.tools.includes("antigravity")) {
2007
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
2008
+ ensureDir(antigravityPath);
2009
+ copyPromptsToDir(prompts, antigravityPath, ".md");
2010
+ }
2011
+ const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
2012
+ ensureDir(path8.dirname(workspaceConfigPath));
2013
+ let configContent = `# RRCE-Workflow Configuration
2014
+ version: 1
2015
+
2016
+ storage:
2017
+ mode: ${config.storageMode}`;
2018
+ if (config.globalPath && config.globalPath !== getDefaultRRCEHome()) {
2019
+ configContent += `
2020
+ globalPath: "${config.globalPath}"`;
2021
+ }
2022
+ configContent += `
2023
+
2024
+ project:
2025
+ name: "${workspaceName}"
2026
+
2027
+ tools:
2028
+ copilot: ${config.tools.includes("copilot")}
2029
+ antigravity: ${config.tools.includes("antigravity")}
2030
+ `;
2031
+ if (config.linkedProjects.length > 0) {
2032
+ configContent += `
2033
+ linked_projects:
2034
+ `;
2035
+ config.linkedProjects.forEach((name) => {
2036
+ configContent += ` - ${name}
2037
+ `;
2038
+ });
2039
+ }
2040
+ fs7.writeFileSync(workspaceConfigPath, configContent);
2041
+ if (config.addToGitignore) {
2042
+ updateGitignore(workspacePath, config.storageMode, config.tools);
2043
+ }
2044
+ if (config.tools.includes("copilot") || config.linkedProjects.length > 0) {
2045
+ const selectedProjects = allProjects.filter(
2046
+ (p) => config.linkedProjects.includes(`${p.name}:${p.source}`)
2047
+ );
2048
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, config.globalPath);
1754
2049
  }
1755
- serverState = { running: false };
1756
- console.error("RRCE MCP Hub stopped");
1757
2050
  }
1758
- function getMCPServerStatus() {
1759
- return { ...serverState };
2051
+ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
2052
+ const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
2053
+ const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
2054
+ switch (mode) {
2055
+ case "global":
2056
+ return [globalPath];
2057
+ case "workspace":
2058
+ return [workspacePath];
2059
+ default:
2060
+ return [globalPath];
2061
+ }
1760
2062
  }
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
- }
2063
+ function updateGitignore(workspacePath, storageMode, tools) {
2064
+ const gitignorePath = path8.join(workspacePath, ".gitignore");
2065
+ const entries = [];
2066
+ if (storageMode === "workspace") {
2067
+ entries.push(".rrce-workflow/");
1781
2068
  }
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
- }
2069
+ if (tools.includes("copilot")) {
2070
+ entries.push(".github/agents/");
1790
2071
  }
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
- }
2072
+ if (tools.includes("antigravity")) {
2073
+ entries.push(".agent/");
1829
2074
  }
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) {
2075
+ if (entries.length === 0) {
1846
2076
  return false;
1847
2077
  }
1848
2078
  try {
1849
- if (!fs15.existsSync(resolvedPath)) {
1850
- fs15.mkdirSync(resolvedPath, { recursive: true });
2079
+ let content = "";
2080
+ if (fs7.existsSync(gitignorePath)) {
2081
+ content = fs7.readFileSync(gitignorePath, "utf-8");
1851
2082
  }
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
- );
2083
+ const lines = content.split("\n").map((line) => line.trim());
2084
+ const newEntries = [];
2085
+ for (const entry of entries) {
2086
+ const entryWithoutSlash = entry.replace(/\/$/, "");
2087
+ const commentedEntry = `# ${entry}`;
2088
+ const commentedEntryNoSlash = `# ${entryWithoutSlash}`;
2089
+ if (!lines.some(
2090
+ (line) => line === entry || line === entryWithoutSlash || line === commentedEntry || line === commentedEntryNoSlash
2091
+ )) {
2092
+ newEntries.push(entry);
2093
+ }
2094
+ }
2095
+ if (newEntries.length === 0) {
2096
+ return false;
2097
+ }
2098
+ let newContent = content;
2099
+ if (!newContent.endsWith("\n") && newContent !== "") {
2100
+ newContent += "\n";
2101
+ }
2102
+ if (newContent === "" || !content.includes("# rrce-workflow")) {
2103
+ newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
2104
+ }
2105
+ newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
2106
+ fs7.writeFileSync(gitignorePath, newContent);
1861
2107
  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
- );
2108
+ } catch {
1867
2109
  return false;
1868
2110
  }
1869
2111
  }
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");
2112
+
2113
+ // src/commands/wizard/link-flow.ts
2114
+ init_paths();
2115
+ import { multiselect as multiselect2, spinner as spinner2, note as note3, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
2116
+ import pc4 from "picocolors";
2117
+ import * as fs8 from "fs";
2118
+ init_detection();
2119
+ async function runLinkProjectsFlow(workspacePath, workspaceName) {
2120
+ const projects = scanForProjects({
2121
+ excludeWorkspace: workspaceName,
2122
+ workspacePath
2123
+ });
1876
2124
  if (projects.length === 0) {
1877
- note8('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
2125
+ outro2(pc4.yellow("No other projects found. Try setting up another project first."));
1878
2126
  return;
1879
2127
  }
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}`);
2128
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2129
+ const linkedProjects = await multiselect2({
2130
+ message: "Select projects to link:",
2131
+ options: projects.map((project) => ({
2132
+ value: `${project.name}:${project.source}`,
2133
+ // Unique key
2134
+ label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
2135
+ hint: pc4.dim(
2136
+ project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
2137
+ )
2138
+ })),
2139
+ required: true
2140
+ });
2141
+ if (isCancel3(linkedProjects)) {
2142
+ cancel2("Cancelled.");
2143
+ process.exit(0);
1890
2144
  }
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}`));
2145
+ const selectedKeys = linkedProjects;
2146
+ if (selectedKeys.length === 0) {
2147
+ outro2("No projects selected.");
2148
+ return;
2149
+ }
2150
+ const selectedProjects = projects.filter(
2151
+ (p) => selectedKeys.includes(`${p.name}:${p.source}`)
2152
+ );
2153
+ const s = spinner2();
2154
+ s.start("Linking projects");
2155
+ const configFilePath = getConfigPath(workspacePath);
2156
+ let configContent = fs8.readFileSync(configFilePath, "utf-8");
2157
+ if (configContent.includes("linked_projects:")) {
2158
+ const lines = configContent.split("\n");
2159
+ const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
2160
+ if (linkedIndex !== -1) {
2161
+ let insertIndex = linkedIndex + 1;
2162
+ while (insertIndex < lines.length && lines[insertIndex]?.startsWith(" - ")) {
2163
+ insertIndex++;
2164
+ }
2165
+ for (const project of selectedProjects) {
2166
+ const entry = ` - ${project.name}:${project.source}`;
2167
+ if (!configContent.includes(entry)) {
2168
+ lines.splice(insertIndex, 0, entry);
2169
+ insertIndex++;
2170
+ }
2171
+ }
2172
+ configContent = lines.join("\n");
2173
+ }
1896
2174
  } else {
1897
- lines.push(pc9.dim("Server: not running"));
2175
+ configContent += `
2176
+ linked_projects:
2177
+ `;
2178
+ selectedProjects.forEach((project) => {
2179
+ configContent += ` - ${project.name}:${project.source}
2180
+ `;
2181
+ });
1898
2182
  }
1899
- note8(lines.join("\n"), "MCP Hub Status");
2183
+ fs8.writeFileSync(configFilePath, configContent);
2184
+ generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
2185
+ s.stop("Projects linked");
2186
+ const workspaceFile = `${workspaceName}.code-workspace`;
2187
+ const summary = [
2188
+ `Linked projects:`,
2189
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
2190
+ ``,
2191
+ `Workspace file: ${pc4.cyan(workspaceFile)}`
2192
+ ];
2193
+ note3(summary.join("\n"), "Link Summary");
2194
+ outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
1900
2195
  }
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)) {
2196
+
2197
+ // src/commands/wizard/sync-flow.ts
2198
+ init_paths();
2199
+ import { confirm as confirm2, spinner as spinner3, note as note4, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
2200
+ import pc5 from "picocolors";
2201
+ import * as fs9 from "fs";
2202
+ import * as path9 from "path";
2203
+ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
2204
+ const localPath = getLocalWorkspacePath(workspacePath);
2205
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2206
+ const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
2207
+ const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
2208
+ const existingDirs = subdirs.filter(
2209
+ (dir) => fs9.existsSync(path9.join(localPath, dir))
2210
+ );
2211
+ if (existingDirs.length === 0) {
2212
+ outro3(pc5.yellow("No data found in workspace storage to sync."));
1931
2213
  return;
1932
2214
  }
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!
2215
+ note4(
2216
+ `The following will be copied to global storage:
2217
+ ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
1942
2218
 
1943
- Exposed projects: ${exposedCount}
1944
- Hidden projects: ${projects.length - exposedCount}`,
1945
- "Configuration Updated"
2219
+ Destination: ${pc5.cyan(globalPath)}`,
2220
+ "Sync Preview"
1946
2221
  );
2222
+ const shouldSync = await confirm2({
2223
+ message: "Proceed with sync to global storage?",
2224
+ initialValue: true
2225
+ });
2226
+ if (isCancel4(shouldSync) || !shouldSync) {
2227
+ outro3("Sync cancelled.");
2228
+ return;
2229
+ }
2230
+ const s = spinner3();
2231
+ s.start("Syncing to global storage");
2232
+ try {
2233
+ ensureDir(globalPath);
2234
+ for (const dir of existingDirs) {
2235
+ const srcDir = path9.join(localPath, dir);
2236
+ const destDir = path9.join(globalPath, dir);
2237
+ ensureDir(destDir);
2238
+ copyDirRecursive(srcDir, destDir);
2239
+ }
2240
+ s.stop("Sync complete");
2241
+ const summary = [
2242
+ `Synced directories:`,
2243
+ ...existingDirs.map((d) => ` \u2713 ${d}/`),
2244
+ ``,
2245
+ `Global path: ${pc5.cyan(globalPath)}`,
2246
+ ``,
2247
+ `Other projects can now link this knowledge!`
2248
+ ];
2249
+ note4(summary.join("\n"), "Sync Summary");
2250
+ outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
2251
+ } catch (error) {
2252
+ s.stop("Error occurred");
2253
+ cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
2254
+ process.exit(1);
2255
+ }
1947
2256
  }
1948
- async function handleStartServer() {
1949
- const s = spinner6();
1950
- s.start("Starting MCP server...");
2257
+
2258
+ // src/commands/wizard/update-flow.ts
2259
+ init_paths();
2260
+ init_prompts();
2261
+ import { confirm as confirm3, spinner as spinner4, note as note5, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
2262
+ import pc6 from "picocolors";
2263
+ import * as fs10 from "fs";
2264
+ import * as path10 from "path";
2265
+ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
2266
+ const s = spinner4();
2267
+ s.start("Checking for updates");
1951
2268
  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.
2269
+ const agentCoreDir = getAgentCoreDir();
2270
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2271
+ const mode = currentStorageMode || "global";
2272
+ const customGlobalPath = getEffectiveRRCEHome(workspacePath);
2273
+ const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
2274
+ s.stop("Updates found");
2275
+ note5(
2276
+ `The following will be updated from the package:
2277
+ \u2022 prompts/ (${prompts.length} agent prompts)
2278
+ \u2022 templates/ (output templates)
1956
2279
 
1957
- To connect from Claude Desktop, add to your config:
1958
- ` + pc9.cyan(`~/.config/claude/claude_desktop_config.json`),
1959
- "Server Started"
2280
+ Target locations:
2281
+ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
2282
+ "Update Preview"
1960
2283
  );
2284
+ const shouldUpdate = await confirm3({
2285
+ message: "Proceed with update?",
2286
+ initialValue: true
2287
+ });
2288
+ if (isCancel5(shouldUpdate) || !shouldUpdate) {
2289
+ outro4("Update cancelled.");
2290
+ return;
2291
+ }
2292
+ s.start("Updating from package");
2293
+ for (const dataPath of dataPaths) {
2294
+ copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
2295
+ }
2296
+ const configFilePath = getConfigPath(workspacePath);
2297
+ const configContent = fs10.readFileSync(configFilePath, "utf-8");
2298
+ if (configContent.includes("copilot: true")) {
2299
+ const copilotPath = getAgentPromptPath(workspacePath, "copilot");
2300
+ ensureDir(copilotPath);
2301
+ copyPromptsToDir(prompts, copilotPath, ".agent.md");
2302
+ }
2303
+ if (configContent.includes("antigravity: true")) {
2304
+ const antigravityPath = getAgentPromptPath(workspacePath, "antigravity");
2305
+ ensureDir(antigravityPath);
2306
+ copyPromptsToDir(prompts, antigravityPath, ".md");
2307
+ }
2308
+ s.stop("Update complete");
2309
+ const summary = [
2310
+ `Updated:`,
2311
+ ` \u2713 ${prompts.length} agent prompts`,
2312
+ ` \u2713 Output templates`,
2313
+ ``,
2314
+ `Your configuration and knowledge files were preserved.`
2315
+ ];
2316
+ note5(summary.join("\n"), "Update Summary");
2317
+ outro4(pc6.green("\u2713 Successfully updated from package!"));
1961
2318
  } 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
- );
2319
+ s.stop("Error occurred");
2320
+ cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
2321
+ process.exit(1);
1967
2322
  }
1968
2323
  }
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;
2324
+ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
2325
+ const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
2326
+ const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
2327
+ switch (mode) {
2328
+ case "global":
2329
+ return [globalPath];
2330
+ case "workspace":
2331
+ return [workspacePath];
2332
+ default:
2333
+ return [globalPath];
1981
2334
  }
1982
- stopMCPServer();
1983
- note8(pc9.green("MCP server stopped."), "Server Stopped");
1984
2335
  }
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
2336
 
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"]
2337
+ // src/commands/wizard/index.ts
2338
+ async function runWizard() {
2339
+ intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
2340
+ const s = spinner5();
2341
+ s.start("Detecting environment");
2342
+ const workspacePath = detectWorkspaceRoot();
2343
+ const workspaceName = getWorkspaceName(workspacePath);
2344
+ const gitUser = getGitUser();
2345
+ await new Promise((r) => setTimeout(r, 800));
2346
+ s.stop("Environment detected");
2347
+ note6(
2348
+ `Git User: ${pc7.bold(gitUser || "(not found)")}
2349
+ Workspace: ${pc7.bold(workspaceName)}`,
2350
+ "Context"
2351
+ );
2352
+ const detectedProjects = scanForProjects({
2353
+ excludeWorkspace: workspaceName,
2354
+ workspacePath
2355
+ });
2356
+ const configFilePath = getConfigPath(workspacePath);
2357
+ const isAlreadyConfigured = fs11.existsSync(configFilePath);
2358
+ let currentStorageMode = null;
2359
+ if (isAlreadyConfigured) {
2360
+ try {
2361
+ const configContent = fs11.readFileSync(configFilePath, "utf-8");
2362
+ const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
2363
+ currentStorageMode = modeMatch?.[1] ?? null;
2364
+ } catch {
2013
2365
  }
2014
2366
  }
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
2367
+ const localDataPath = getLocalWorkspacePath(workspacePath);
2368
+ const hasLocalData = fs11.existsSync(localDataPath);
2369
+ if (isAlreadyConfigured) {
2370
+ const menuOptions = [];
2371
+ if (detectedProjects.length > 0) {
2372
+ menuOptions.push({
2373
+ value: "link",
2374
+ label: "Link other project knowledge",
2375
+ hint: `${detectedProjects.length} projects detected (global + sibling)`
2376
+ });
2377
+ }
2378
+ if (currentStorageMode === "workspace" && hasLocalData) {
2379
+ menuOptions.push({
2380
+ value: "sync-global",
2381
+ label: "Sync to global storage",
2382
+ hint: "Share knowledge with other projects"
2383
+ });
2384
+ }
2385
+ menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
2386
+ menuOptions.push({ value: "exit", label: "Exit" });
2387
+ const action = await select3({
2388
+ message: "This workspace is already configured. What would you like to do?",
2389
+ options: menuOptions
2390
+ });
2391
+ if (isCancel6(action) || action === "exit") {
2392
+ outro5("Exited.");
2393
+ process.exit(0);
2394
+ }
2395
+ if (action === "link") {
2396
+ await runLinkProjectsFlow(workspacePath, workspaceName);
2397
+ return;
2398
+ }
2399
+ if (action === "sync-global") {
2400
+ await runSyncToGlobalFlow(workspacePath, workspaceName);
2401
+ return;
2402
+ }
2403
+ if (action === "update") {
2404
+ await runUpdateFlow(workspacePath, workspaceName, currentStorageMode);
2405
+ return;
2406
+ }
2407
+ }
2408
+ await runSetupFlow(workspacePath, workspaceName, detectedProjects);
2409
+ }
2021
2410
 
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");
2411
+ // src/commands/selector.ts
2412
+ init_prompts();
2413
+ import { intro as intro3, select as select5, note as note8, cancel as cancel7, isCancel as isCancel8, outro as outro7 } from "@clack/prompts";
2414
+ import pc9 from "picocolors";
2415
+ import * as path16 from "path";
2416
+ async function runSelector() {
2417
+ const workspaceName = path16.basename(process.cwd());
2418
+ intro3(pc9.cyan(pc9.inverse(` RRCE-Workflow | ${workspaceName} `)));
2419
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
2420
+ if (prompts.length === 0) {
2421
+ cancel7("No agents found. Run `rrce-workflow` to set up.");
2422
+ process.exit(0);
2423
+ }
2424
+ const selection = await select5({
2425
+ message: "Select an agent:",
2426
+ options: [
2427
+ {
2428
+ value: "mcp",
2429
+ label: "Manage MCP Hub",
2430
+ hint: "Configure & Start MCP Server"
2431
+ },
2432
+ ...prompts.map((p) => ({
2433
+ value: p,
2434
+ label: p.frontmatter.name,
2435
+ hint: p.frontmatter.description
2436
+ }))
2437
+ ]
2438
+ });
2439
+ if (isCancel8(selection)) {
2440
+ cancel7("Selection cancelled.");
2441
+ process.exit(0);
2442
+ }
2443
+ if (selection === "mcp") {
2444
+ const { runMCP: runMCP2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
2445
+ await runMCP2();
2446
+ return;
2447
+ }
2448
+ const prompt = selection;
2449
+ note8(
2450
+ `Use this agent in your IDE by invoking:
2451
+ ${pc9.bold(pc9.cyan(`@${prompt.frontmatter.name}`))}`,
2452
+ "Agent Selected"
2453
+ );
2454
+ outro7("Done");
2031
2455
  }
2032
2456
 
2033
2457
  // src/index.ts
2458
+ init_mcp();
2034
2459
  var command = process.argv[2];
2035
2460
  var subcommand = process.argv[3];
2036
2461
  if (!command || command === "wizard") {