rrce-workflow 0.2.22 → 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 (3) hide show
  1. package/README.md +144 -0
  2. package/dist/index.js +1609 -344
  3. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -1,24 +1,16 @@
1
- // src/commands/wizard/index.ts
2
- import { intro, select as select2, spinner as spinner5, note as note5, outro as outro5, isCancel as isCancel5 } from "@clack/prompts";
3
- import pc6 from "picocolors";
4
- import * as fs11 from "fs";
5
-
6
- // src/lib/git.ts
7
- import { execSync } from "child_process";
8
- function getGitUser() {
9
- try {
10
- const result = execSync("git config user.name", { encoding: "utf-8" });
11
- return result.trim() || null;
12
- } catch {
13
- return null;
14
- }
15
- }
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
16
10
 
17
11
  // src/lib/paths.ts
18
12
  import * as fs from "fs";
19
13
  import * as path from "path";
20
- var RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
21
- var RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
22
14
  function detectWorkspaceRoot() {
23
15
  if (RRCE_WORKSPACE) {
24
16
  return RRCE_WORKSPACE;
@@ -132,30 +124,18 @@ function getEffectiveRRCEHome(workspaceRoot) {
132
124
  }
133
125
  return getDefaultRRCEHome();
134
126
  }
127
+ var RRCE_HOME, RRCE_WORKSPACE;
128
+ var init_paths = __esm({
129
+ "src/lib/paths.ts"() {
130
+ "use strict";
131
+ RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
132
+ RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
133
+ }
134
+ });
135
135
 
136
136
  // src/lib/detection.ts
137
137
  import * as fs2 from "fs";
138
138
  import * as path2 from "path";
139
- var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
140
- "node_modules",
141
- ".git",
142
- ".cache",
143
- ".npm",
144
- ".yarn",
145
- ".pnpm",
146
- ".local",
147
- ".config",
148
- ".vscode",
149
- ".vscode-server",
150
- "Library",
151
- "Applications",
152
- ".Trash",
153
- "snap",
154
- ".cargo",
155
- ".rustup",
156
- ".go",
157
- ".docker"
158
- ]);
159
139
  function scanForProjects(options = {}) {
160
140
  const { excludeWorkspace, workspacePath } = options;
161
141
  const projects = [];
@@ -277,84 +257,1509 @@ function parseWorkspaceConfig(configPath) {
277
257
  return null;
278
258
  }
279
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
+
369
+ // src/lib/autocomplete-prompt.ts
370
+ import * as fs6 from "fs";
371
+ import * as path6 from "path";
372
+ import * as readline from "readline";
373
+ import pc from "picocolors";
374
+ function directoryPrompt(opts) {
375
+ return new Promise((resolve) => {
376
+ process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
377
+ `);
378
+ process.stdout.write(`${pc.cyan("\u2502")} `);
379
+ const rl = readline.createInterface({
380
+ input: process.stdin,
381
+ output: process.stdout,
382
+ completer: completeDirectory,
383
+ terminal: true
384
+ });
385
+ if (opts.defaultValue) {
386
+ rl.write(opts.defaultValue);
387
+ }
388
+ rl.on("line", (input) => {
389
+ const value = input.trim();
390
+ const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
391
+ if (opts.validate) {
392
+ const error = opts.validate(expandedPath);
393
+ if (error) {
394
+ process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
395
+ `);
396
+ process.stdout.write(`${pc.cyan("\u2502")} `);
397
+ rl.write(value);
398
+ return;
399
+ }
400
+ }
401
+ rl.close();
402
+ process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
403
+ `);
404
+ resolve(expandedPath);
405
+ });
406
+ rl.on("close", () => {
407
+ });
408
+ rl.on("SIGINT", () => {
409
+ rl.close();
410
+ process.stdout.write("\n");
411
+ resolve(/* @__PURE__ */ Symbol("cancel"));
412
+ });
413
+ });
414
+ }
415
+ function completeDirectory(line) {
416
+ const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
417
+ try {
418
+ let dirToScan;
419
+ let prefix;
420
+ let basePath;
421
+ if (expanded === "" || expanded === "/") {
422
+ dirToScan = expanded || "/";
423
+ prefix = "";
424
+ basePath = expanded;
425
+ } else if (expanded.endsWith("/")) {
426
+ dirToScan = expanded;
427
+ prefix = "";
428
+ basePath = expanded;
429
+ } else {
430
+ dirToScan = path6.dirname(expanded);
431
+ prefix = path6.basename(expanded).toLowerCase();
432
+ basePath = dirToScan === "/" ? "/" : dirToScan + "/";
433
+ }
434
+ if (!fs6.existsSync(dirToScan)) {
435
+ return [[], line];
436
+ }
437
+ const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
438
+ if (!entry.isDirectory()) return false;
439
+ if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
440
+ return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
441
+ }).map((entry) => {
442
+ const fullPath = path6.join(dirToScan, entry.name);
443
+ const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
444
+ return displayPath + "/";
445
+ }).sort();
446
+ if (entries.length === 1) {
447
+ return [entries, line];
448
+ }
449
+ if (entries.length > 1) {
450
+ const commonPrefix = getCommonPrefix(entries);
451
+ if (commonPrefix.length > line.length) {
452
+ return [[commonPrefix], line];
453
+ }
454
+ }
455
+ return [entries, line];
456
+ } catch {
457
+ return [[], line];
458
+ }
459
+ }
460
+ function getCommonPrefix(strings) {
461
+ if (strings.length === 0) return "";
462
+ if (strings.length === 1) return strings[0] || "";
463
+ let prefix = strings[0] || "";
464
+ for (let i = 1; i < strings.length; i++) {
465
+ const str = strings[i] || "";
466
+ while (prefix.length > 0 && !str.startsWith(prefix)) {
467
+ prefix = prefix.slice(0, -1);
468
+ }
469
+ }
470
+ return prefix;
471
+ }
472
+ function isCancelled(value) {
473
+ return typeof value === "symbol";
474
+ }
475
+ var init_autocomplete_prompt = __esm({
476
+ "src/lib/autocomplete-prompt.ts"() {
477
+ "use strict";
478
+ }
479
+ });
480
+
481
+ // src/lib/tui-utils.ts
482
+ var tui_utils_exports = {};
483
+ __export(tui_utils_exports, {
484
+ resolveGlobalPath: () => resolveGlobalPath
485
+ });
486
+ import { select, note, isCancel } from "@clack/prompts";
487
+ import pc2 from "picocolors";
488
+ import * as path7 from "path";
489
+ async function resolveGlobalPath() {
490
+ const defaultPath = getDefaultRRCEHome();
491
+ const isDefaultWritable = checkWriteAccess(defaultPath);
492
+ const options = [
493
+ {
494
+ value: "default",
495
+ label: `Default (${defaultPath})`,
496
+ hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
497
+ },
498
+ {
499
+ value: "custom",
500
+ label: "Custom path",
501
+ hint: "Specify your own directory"
502
+ }
503
+ ];
504
+ const choice = await select({
505
+ message: "Global storage location:",
506
+ options,
507
+ initialValue: isDefaultWritable ? "default" : "custom"
508
+ });
509
+ if (isCancel(choice)) {
510
+ return void 0;
511
+ }
512
+ if (choice === "default") {
513
+ if (!isDefaultWritable) {
514
+ note(
515
+ `${pc2.yellow("\u26A0")} Cannot write to default path:
516
+ ${pc2.dim(defaultPath)}
517
+
518
+ This can happen when running via npx/bunx in restricted environments.
519
+ Please choose a custom path instead.`,
520
+ "Write Access Issue"
521
+ );
522
+ return resolveGlobalPath();
523
+ }
524
+ return defaultPath;
525
+ }
526
+ const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
527
+ const customPath = await directoryPrompt({
528
+ message: "Enter custom global path (Tab to autocomplete):",
529
+ defaultValue: suggestedPath,
530
+ validate: (value) => {
531
+ if (!value.trim()) {
532
+ return "Path cannot be empty";
533
+ }
534
+ if (!checkWriteAccess(value)) {
535
+ return `Cannot write to ${value}. Please choose a writable path.`;
536
+ }
537
+ return void 0;
538
+ }
539
+ });
540
+ if (isCancelled(customPath)) {
541
+ return void 0;
542
+ }
543
+ let expandedPath = customPath;
544
+ if (!expandedPath.endsWith(".rrce-workflow")) {
545
+ expandedPath = path7.join(expandedPath, ".rrce-workflow");
546
+ }
547
+ return expandedPath;
548
+ }
549
+ var init_tui_utils = __esm({
550
+ "src/lib/tui-utils.ts"() {
551
+ "use strict";
552
+ init_paths();
553
+ init_autocomplete_prompt();
554
+ }
555
+ });
556
+
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
+ });
584
+
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
+ }
620
+ try {
621
+ const content = fs12.readFileSync(configPath, "utf-8");
622
+ return parseMCPConfig(content);
623
+ } catch {
624
+ return { ...DEFAULT_MCP_CONFIG };
625
+ }
626
+ }
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 });
632
+ }
633
+ const content = serializeMCPConfig(config);
634
+ fs12.writeFileSync(configPath, content);
635
+ }
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
+ }
684
+ }
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);
690
+ }
691
+ currentProject = {
692
+ name: projectNameMatch[1].trim(),
693
+ expose: true,
694
+ permissions: { ...DEFAULT_PERMISSIONS }
695
+ };
696
+ inPermissions = false;
697
+ continue;
698
+ }
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";
715
+ }
716
+ }
717
+ }
718
+ }
719
+ if (currentProject && currentProject.name) {
720
+ config.projects.push(currentProject);
721
+ }
722
+ return config;
723
+ }
724
+ function serializeMCPConfig(config) {
725
+ let content = `# RRCE MCP Hub Configuration
726
+ # Manages which projects are exposed via MCP
727
+
728
+ server:
729
+ port: ${config.server.port}
730
+ autoStart: ${config.server.autoStart}
731
+
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}
738
+
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
+ `;
752
+ }
753
+ }
754
+ return content;
755
+ }
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 };
762
+ }
763
+ } else {
764
+ config.projects.push({
765
+ name,
766
+ expose,
767
+ permissions: permissions ? { ...DEFAULT_PERMISSIONS, ...permissions } : { ...DEFAULT_PERMISSIONS }
768
+ });
769
+ }
770
+ return config;
771
+ }
772
+ function isProjectExposed(config, name) {
773
+ const project = config.projects.find((p) => p.name === name);
774
+ if (project) {
775
+ return project.expose;
776
+ }
777
+ return config.defaults.includeNew;
778
+ }
779
+ function getProjectPermissions(config, name) {
780
+ const project = config.projects.find((p) => p.name === name);
781
+ return project?.permissions ?? config.defaults.permissions;
782
+ }
783
+ var init_config = __esm({
784
+ "src/mcp/config.ts"() {
785
+ "use strict";
786
+ init_paths();
787
+ init_types();
788
+ }
789
+ });
790
+
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;
928
+ try {
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
+ }
950
+ } catch {
951
+ }
952
+ }
953
+ return results;
954
+ }
955
+ var init_resources = __esm({
956
+ "src/mcp/resources.ts"() {
957
+ "use strict";
958
+ init_config();
959
+ init_detection();
960
+ }
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);
1044
+ });
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);
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;
1071
+ }
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
+ });
1084
+ for (const project of projects) {
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"
1093
+ });
1094
+ }
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"
1101
+ });
1102
+ }
1103
+ }
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
+ };
1132
+ }
1133
+ throw new Error(`Unknown resource: ${uri}`);
1134
+ } catch (error) {
1135
+ logger.error(`Failed to read resource: ${uri}`, error);
1136
+ throw error;
1137
+ }
1138
+ });
1139
+ }
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"]
1150
+ }
1151
+ },
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
+ }
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;
1201
+ }
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;
1234
+ }
1235
+ });
1236
+ }
1237
+ function stopMCPServer() {
1238
+ if (mcpServer) {
1239
+ logger.info("Stopping MCP Server...");
1240
+ mcpServer.close();
1241
+ mcpServer = null;
1242
+ }
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;
1261
+ }
1262
+ });
1263
+
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
+ };
1273
+ }
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;
1281
+ }
1282
+ }
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 });
1288
+ }
1289
+ let config = { mcpServers: {} };
1290
+ if (fs16.existsSync(configPath)) {
1291
+ try {
1292
+ config = JSON.parse(fs16.readFileSync(configPath, "utf-8"));
1293
+ } catch {
1294
+ }
1295
+ }
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 {
1306
+ return false;
1307
+ }
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;
1342
+ }
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;
1351
+ }
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();
1361
+ }
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;
1381
+ }
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;
1401
+ }
1402
+ }
1403
+ outro6(pc8.green("MCP Hub closed."));
1404
+ }
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
1419
+ });
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")}`);
1426
+ }
1427
+ if (results.length > 0) {
1428
+ note7(results.join("\n"), "Installation Results");
1429
+ }
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
+ }
1444
+ }
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";
1451
+ }
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
+ };
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);
1538
+ }
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.
1547
+
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"
1551
+ );
1552
+ const resolvedPath = await resolveGlobalPath2();
1553
+ if (!resolvedPath) {
1554
+ return false;
1555
+ }
1556
+ try {
1557
+ if (!fs17.existsSync(resolvedPath)) {
1558
+ fs17.mkdirSync(resolvedPath, { recursive: true });
1559
+ }
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;
1570
+ } catch (error) {
1571
+ note7(
1572
+ `${pc8.red("\u2717")} Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,
1573
+ "Error"
1574
+ );
1575
+ return false;
1576
+ }
1577
+ }
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;
1587
+ }
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}`);
1598
+ }
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"));
1606
+ }
1607
+ note7(lines.join("\n"), "MCP Hub Status");
1608
+ }
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;
1620
+ }
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
+ };
1629
+ });
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;
1642
+ }
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;
1652
+ note7(
1653
+ `${pc8.green("\u2713")} Configuration saved!
1654
+
1655
+ Exposed projects: ${exposedCount}
1656
+ Hidden projects: ${projects.length - exposedCount}`,
1657
+ "Configuration Updated"
1658
+ );
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
280
1679
 
281
- // src/commands/wizard/setup-flow.ts
282
- import { group, select, multiselect, confirm, spinner, note, outro, cancel, isCancel } from "@clack/prompts";
283
- import pc2 from "picocolors";
284
- import * as fs7 from "fs";
285
- import * as path7 from "path";
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.
286
1684
 
287
- // src/lib/prompts.ts
288
- import * as fs3 from "fs";
289
- import * as path3 from "path";
290
- import { fileURLToPath } from "url";
291
- import matter from "gray-matter";
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
292
1690
 
293
- // src/types/prompt.ts
294
- import { z } from "zod";
295
- var PromptArgSchema = z.object({
296
- name: z.string(),
297
- default: z.string().optional(),
298
- prompt: z.string().optional()
299
- });
300
- var AutoIdentitySchema = z.object({
301
- user: z.string(),
302
- model: z.string()
303
- });
304
- var PromptFrontmatterSchema = z.object({
305
- name: z.string(),
306
- description: z.string(),
307
- "argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
308
- tools: z.array(z.string()).optional(),
309
- "required-args": z.array(PromptArgSchema).optional(),
310
- "optional-args": z.array(PromptArgSchema).optional(),
311
- "auto-identity": AutoIdentitySchema.optional()
312
- });
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
313
1696
 
314
- // src/lib/prompts.ts
315
- var __filename = fileURLToPath(import.meta.url);
316
- var __dirname = path3.dirname(__filename);
317
- function parsePromptFile(filePath) {
318
- try {
319
- const fileContent = fs3.readFileSync(filePath, "utf-8");
320
- const { data, content } = matter(fileContent);
321
- const parsed = PromptFrontmatterSchema.safeParse(data);
322
- if (!parsed.success) {
323
- console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
324
- return null;
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"]
325
1704
  }
326
- return {
327
- frontmatter: parsed.data,
328
- content: content.trim(),
329
- filePath
330
- };
331
- } catch (error) {
332
- console.error(`Error reading prompt file ${filePath}:`, error);
333
- return null;
334
1705
  }
1706
+ }`)}
1707
+
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");
335
1722
  }
336
- function loadPromptsFromDir(dirPath) {
337
- if (!fs3.existsSync(dirPath)) {
338
- return [];
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();
339
1730
  }
340
- const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
341
- const prompts = [];
342
- for (const file of files) {
343
- const prompt = parsePromptFile(path3.join(dirPath, file));
344
- if (prompt) {
345
- prompts.push(prompt);
346
- }
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() {
1741
+ try {
1742
+ const result = execSync("git config user.name", { encoding: "utf-8" });
1743
+ return result.trim() || null;
1744
+ } catch {
1745
+ return null;
347
1746
  }
348
- return prompts;
349
- }
350
- function getAgentCoreDir() {
351
- return path3.join(__dirname, "..", "agent-core");
352
- }
353
- function getAgentCorePromptsDir() {
354
- return path3.join(getAgentCoreDir(), "prompts");
355
1747
  }
356
1748
 
1749
+ // src/commands/wizard/index.ts
1750
+ init_paths();
1751
+ init_detection();
1752
+
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";
1760
+
357
1761
  // src/commands/wizard/utils.ts
1762
+ init_paths();
358
1763
  import * as fs4 from "fs";
359
1764
  import * as path4 from "path";
360
1765
  function copyPromptsToDir(prompts, targetDir, extension) {
@@ -381,6 +1786,8 @@ function copyDirRecursive(src, dest) {
381
1786
  }
382
1787
 
383
1788
  // src/commands/wizard/vscode.ts
1789
+ init_paths();
1790
+ init_detection();
384
1791
  import * as fs5 from "fs";
385
1792
  import * as path5 from "path";
386
1793
  function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
@@ -463,119 +1870,14 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
463
1870
  fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
464
1871
  }
465
1872
 
466
- // src/lib/autocomplete-prompt.ts
467
- import * as fs6 from "fs";
468
- import * as path6 from "path";
469
- import * as readline from "readline";
470
- import pc from "picocolors";
471
- function directoryPrompt(opts) {
472
- return new Promise((resolve) => {
473
- process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
474
- `);
475
- process.stdout.write(`${pc.cyan("\u2502")} `);
476
- const rl = readline.createInterface({
477
- input: process.stdin,
478
- output: process.stdout,
479
- completer: completeDirectory,
480
- terminal: true
481
- });
482
- if (opts.defaultValue) {
483
- rl.write(opts.defaultValue);
484
- }
485
- rl.on("line", (input) => {
486
- const value = input.trim();
487
- const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
488
- if (opts.validate) {
489
- const error = opts.validate(expandedPath);
490
- if (error) {
491
- process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
492
- `);
493
- process.stdout.write(`${pc.cyan("\u2502")} `);
494
- rl.write(value);
495
- return;
496
- }
497
- }
498
- rl.close();
499
- process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
500
- `);
501
- resolve(expandedPath);
502
- });
503
- rl.on("close", () => {
504
- });
505
- rl.on("SIGINT", () => {
506
- rl.close();
507
- process.stdout.write("\n");
508
- resolve(/* @__PURE__ */ Symbol("cancel"));
509
- });
510
- });
511
- }
512
- function completeDirectory(line) {
513
- const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
514
- try {
515
- let dirToScan;
516
- let prefix;
517
- let basePath;
518
- if (expanded === "" || expanded === "/") {
519
- dirToScan = expanded || "/";
520
- prefix = "";
521
- basePath = expanded;
522
- } else if (expanded.endsWith("/")) {
523
- dirToScan = expanded;
524
- prefix = "";
525
- basePath = expanded;
526
- } else {
527
- dirToScan = path6.dirname(expanded);
528
- prefix = path6.basename(expanded).toLowerCase();
529
- basePath = dirToScan === "/" ? "/" : dirToScan + "/";
530
- }
531
- if (!fs6.existsSync(dirToScan)) {
532
- return [[], line];
533
- }
534
- const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
535
- if (!entry.isDirectory()) return false;
536
- if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
537
- return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
538
- }).map((entry) => {
539
- const fullPath = path6.join(dirToScan, entry.name);
540
- const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
541
- return displayPath + "/";
542
- }).sort();
543
- if (entries.length === 1) {
544
- return [entries, line];
545
- }
546
- if (entries.length > 1) {
547
- const commonPrefix = getCommonPrefix(entries);
548
- if (commonPrefix.length > line.length) {
549
- return [[commonPrefix], line];
550
- }
551
- }
552
- return [entries, line];
553
- } catch {
554
- return [[], line];
555
- }
556
- }
557
- function getCommonPrefix(strings) {
558
- if (strings.length === 0) return "";
559
- if (strings.length === 1) return strings[0] || "";
560
- let prefix = strings[0] || "";
561
- for (let i = 1; i < strings.length; i++) {
562
- const str = strings[i] || "";
563
- while (prefix.length > 0 && !str.startsWith(prefix)) {
564
- prefix = prefix.slice(0, -1);
565
- }
566
- }
567
- return prefix;
568
- }
569
- function isCancelled(value) {
570
- return typeof value === "symbol";
571
- }
572
-
573
1873
  // src/commands/wizard/setup-flow.ts
1874
+ init_detection();
1875
+ init_tui_utils();
574
1876
  async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
575
1877
  const s = spinner();
576
1878
  const config = await group(
577
1879
  {
578
- storageMode: () => select({
1880
+ storageMode: () => select2({
579
1881
  message: "Where should workflow data be stored?",
580
1882
  options: [
581
1883
  { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
@@ -600,8 +1902,8 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
600
1902
  options: existingProjects.map((project) => ({
601
1903
  value: `${project.name}:${project.source}`,
602
1904
  // Unique key
603
- label: `${project.name} ${pc2.dim(`(${project.source})`)}`,
604
- hint: pc2.dim(
1905
+ label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
1906
+ hint: pc3.dim(
605
1907
  project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
606
1908
  )
607
1909
  })),
@@ -656,7 +1958,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
656
1958
  `Storage: ${config.storageMode}`
657
1959
  ];
658
1960
  if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
659
- summary.push(`Global path: ${pc2.cyan(customGlobalPath)}`);
1961
+ summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
660
1962
  }
661
1963
  if (dataPaths.length > 0) {
662
1964
  summary.push(`Data paths:`);
@@ -669,13 +1971,13 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
669
1971
  const linkedProjects = config.linkedProjects;
670
1972
  if (linkedProjects.length > 0) {
671
1973
  summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
672
- summary.push(`Workspace file: ${pc2.cyan(`${workspaceName}.code-workspace`)}`);
1974
+ summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
673
1975
  }
674
- note(summary.join("\n"), "Setup Summary");
1976
+ note2(summary.join("\n"), "Setup Summary");
675
1977
  if (linkedProjects.length > 0) {
676
- outro(pc2.green(`\u2713 Setup complete! Open ${pc2.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
1978
+ outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
677
1979
  } else {
678
- outro(pc2.green(`\u2713 Setup complete! Your agents are ready to use.`));
1980
+ outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
679
1981
  }
680
1982
  } catch (error) {
681
1983
  s.stop("Error occurred");
@@ -683,77 +1985,18 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
683
1985
  process.exit(1);
684
1986
  }
685
1987
  }
686
- async function resolveGlobalPath() {
687
- const defaultPath = getDefaultRRCEHome();
688
- const isDefaultWritable = checkWriteAccess(defaultPath);
689
- const options = [];
690
- options.push({
691
- value: "default",
692
- label: `Default (${defaultPath})`,
693
- hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
694
- });
695
- options.push({
696
- value: "custom",
697
- label: "Custom path",
698
- hint: "Specify your own directory"
699
- });
700
- const choice = await select({
701
- message: "Global storage location:",
702
- options,
703
- initialValue: isDefaultWritable ? "default" : "custom"
704
- });
705
- if (isCancel(choice)) {
706
- return void 0;
707
- }
708
- if (choice === "default") {
709
- if (!isDefaultWritable) {
710
- note(
711
- `${pc2.yellow("\u26A0")} Cannot write to default path:
712
- ${pc2.dim(defaultPath)}
713
-
714
- This can happen when running via npx/bunx in restricted environments.
715
- Please choose a custom path instead.`,
716
- "Write Access Issue"
717
- );
718
- return resolveGlobalPath();
719
- }
720
- return defaultPath;
721
- }
722
- const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
723
- const customPath = await directoryPrompt({
724
- message: "Enter custom global path (Tab to autocomplete):",
725
- defaultValue: suggestedPath,
726
- validate: (value) => {
727
- if (!value.trim()) {
728
- return "Path cannot be empty";
729
- }
730
- if (!checkWriteAccess(value)) {
731
- return `Cannot write to ${value}. Please choose a writable path.`;
732
- }
733
- return void 0;
734
- }
735
- });
736
- if (isCancelled(customPath)) {
737
- return void 0;
738
- }
739
- let expandedPath = customPath;
740
- if (!expandedPath.endsWith(".rrce-workflow")) {
741
- expandedPath = path7.join(expandedPath, ".rrce-workflow");
742
- }
743
- return expandedPath;
744
- }
745
1988
  async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
746
1989
  const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
747
1990
  for (const dataPath of dataPaths) {
748
1991
  ensureDir(dataPath);
749
- ensureDir(path7.join(dataPath, "knowledge"));
750
- ensureDir(path7.join(dataPath, "refs"));
751
- ensureDir(path7.join(dataPath, "tasks"));
752
- ensureDir(path7.join(dataPath, "templates"));
1992
+ ensureDir(path8.join(dataPath, "knowledge"));
1993
+ ensureDir(path8.join(dataPath, "refs"));
1994
+ ensureDir(path8.join(dataPath, "tasks"));
1995
+ ensureDir(path8.join(dataPath, "templates"));
753
1996
  }
754
1997
  const agentCoreDir = getAgentCoreDir();
755
1998
  syncMetadataToAll(agentCoreDir, dataPaths);
756
- copyDirToAllStoragePaths(path7.join(agentCoreDir, "templates"), "templates", dataPaths);
1999
+ copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
757
2000
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
758
2001
  if (config.tools.includes("copilot")) {
759
2002
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
@@ -765,8 +2008,8 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
765
2008
  ensureDir(antigravityPath);
766
2009
  copyPromptsToDir(prompts, antigravityPath, ".md");
767
2010
  }
768
- const workspaceConfigPath = path7.join(workspacePath, ".rrce-workflow", "config.yaml");
769
- ensureDir(path7.dirname(workspaceConfigPath));
2011
+ const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
2012
+ ensureDir(path8.dirname(workspaceConfigPath));
770
2013
  let configContent = `# RRCE-Workflow Configuration
771
2014
  version: 1
772
2015
 
@@ -806,8 +2049,8 @@ linked_projects:
806
2049
  }
807
2050
  }
808
2051
  function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
809
- const globalPath = path7.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
810
- const workspacePath = path7.join(workspaceRoot, ".rrce-workflow");
2052
+ const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
2053
+ const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
811
2054
  switch (mode) {
812
2055
  case "global":
813
2056
  return [globalPath];
@@ -818,7 +2061,7 @@ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
818
2061
  }
819
2062
  }
820
2063
  function updateGitignore(workspacePath, storageMode, tools) {
821
- const gitignorePath = path7.join(workspacePath, ".gitignore");
2064
+ const gitignorePath = path8.join(workspacePath, ".gitignore");
822
2065
  const entries = [];
823
2066
  if (storageMode === "workspace") {
824
2067
  entries.push(".rrce-workflow/");
@@ -868,16 +2111,18 @@ function updateGitignore(workspacePath, storageMode, tools) {
868
2111
  }
869
2112
 
870
2113
  // src/commands/wizard/link-flow.ts
871
- import { multiselect as multiselect2, spinner as spinner2, note as note2, outro as outro2, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
872
- import pc3 from "picocolors";
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";
873
2117
  import * as fs8 from "fs";
2118
+ init_detection();
874
2119
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
875
2120
  const projects = scanForProjects({
876
2121
  excludeWorkspace: workspaceName,
877
2122
  workspacePath
878
2123
  });
879
2124
  if (projects.length === 0) {
880
- outro2(pc3.yellow("No other projects found. Try setting up another project first."));
2125
+ outro2(pc4.yellow("No other projects found. Try setting up another project first."));
881
2126
  return;
882
2127
  }
883
2128
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
@@ -886,14 +2131,14 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
886
2131
  options: projects.map((project) => ({
887
2132
  value: `${project.name}:${project.source}`,
888
2133
  // Unique key
889
- label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
890
- hint: pc3.dim(
2134
+ label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
2135
+ hint: pc4.dim(
891
2136
  project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
892
2137
  )
893
2138
  })),
894
2139
  required: true
895
2140
  });
896
- if (isCancel2(linkedProjects)) {
2141
+ if (isCancel3(linkedProjects)) {
897
2142
  cancel2("Cancelled.");
898
2143
  process.exit(0);
899
2144
  }
@@ -941,43 +2186,44 @@ linked_projects:
941
2186
  const workspaceFile = `${workspaceName}.code-workspace`;
942
2187
  const summary = [
943
2188
  `Linked projects:`,
944
- ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc3.dim(`(${p.source})`)}`),
2189
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
945
2190
  ``,
946
- `Workspace file: ${pc3.cyan(workspaceFile)}`
2191
+ `Workspace file: ${pc4.cyan(workspaceFile)}`
947
2192
  ];
948
- note2(summary.join("\n"), "Link Summary");
949
- outro2(pc3.green(`\u2713 Projects linked! Open ${pc3.bold(workspaceFile)} in VSCode to access linked data.`));
2193
+ note3(summary.join("\n"), "Link Summary");
2194
+ outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
950
2195
  }
951
2196
 
952
2197
  // src/commands/wizard/sync-flow.ts
953
- import { confirm as confirm2, spinner as spinner3, note as note3, outro as outro3, cancel as cancel3, isCancel as isCancel3 } from "@clack/prompts";
954
- import pc4 from "picocolors";
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";
955
2201
  import * as fs9 from "fs";
956
- import * as path8 from "path";
2202
+ import * as path9 from "path";
957
2203
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
958
2204
  const localPath = getLocalWorkspacePath(workspacePath);
959
2205
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
960
- const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
2206
+ const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
961
2207
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
962
2208
  const existingDirs = subdirs.filter(
963
- (dir) => fs9.existsSync(path8.join(localPath, dir))
2209
+ (dir) => fs9.existsSync(path9.join(localPath, dir))
964
2210
  );
965
2211
  if (existingDirs.length === 0) {
966
- outro3(pc4.yellow("No data found in workspace storage to sync."));
2212
+ outro3(pc5.yellow("No data found in workspace storage to sync."));
967
2213
  return;
968
2214
  }
969
- note3(
2215
+ note4(
970
2216
  `The following will be copied to global storage:
971
2217
  ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
972
2218
 
973
- Destination: ${pc4.cyan(globalPath)}`,
2219
+ Destination: ${pc5.cyan(globalPath)}`,
974
2220
  "Sync Preview"
975
2221
  );
976
2222
  const shouldSync = await confirm2({
977
2223
  message: "Proceed with sync to global storage?",
978
2224
  initialValue: true
979
2225
  });
980
- if (isCancel3(shouldSync) || !shouldSync) {
2226
+ if (isCancel4(shouldSync) || !shouldSync) {
981
2227
  outro3("Sync cancelled.");
982
2228
  return;
983
2229
  }
@@ -986,8 +2232,8 @@ Destination: ${pc4.cyan(globalPath)}`,
986
2232
  try {
987
2233
  ensureDir(globalPath);
988
2234
  for (const dir of existingDirs) {
989
- const srcDir = path8.join(localPath, dir);
990
- const destDir = path8.join(globalPath, dir);
2235
+ const srcDir = path9.join(localPath, dir);
2236
+ const destDir = path9.join(globalPath, dir);
991
2237
  ensureDir(destDir);
992
2238
  copyDirRecursive(srcDir, destDir);
993
2239
  }
@@ -996,12 +2242,12 @@ Destination: ${pc4.cyan(globalPath)}`,
996
2242
  `Synced directories:`,
997
2243
  ...existingDirs.map((d) => ` \u2713 ${d}/`),
998
2244
  ``,
999
- `Global path: ${pc4.cyan(globalPath)}`,
2245
+ `Global path: ${pc5.cyan(globalPath)}`,
1000
2246
  ``,
1001
2247
  `Other projects can now link this knowledge!`
1002
2248
  ];
1003
- note3(summary.join("\n"), "Sync Summary");
1004
- outro3(pc4.green("\u2713 Workspace knowledge synced to global storage!"));
2249
+ note4(summary.join("\n"), "Sync Summary");
2250
+ outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
1005
2251
  } catch (error) {
1006
2252
  s.stop("Error occurred");
1007
2253
  cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
@@ -1010,10 +2256,12 @@ Destination: ${pc4.cyan(globalPath)}`,
1010
2256
  }
1011
2257
 
1012
2258
  // src/commands/wizard/update-flow.ts
1013
- import { confirm as confirm3, spinner as spinner4, note as note4, outro as outro4, cancel as cancel4, isCancel as isCancel4 } from "@clack/prompts";
1014
- import pc5 from "picocolors";
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";
1015
2263
  import * as fs10 from "fs";
1016
- import * as path9 from "path";
2264
+ import * as path10 from "path";
1017
2265
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
1018
2266
  const s = spinner4();
1019
2267
  s.start("Checking for updates");
@@ -1024,7 +2272,7 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
1024
2272
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
1025
2273
  const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
1026
2274
  s.stop("Updates found");
1027
- note4(
2275
+ note5(
1028
2276
  `The following will be updated from the package:
1029
2277
  \u2022 prompts/ (${prompts.length} agent prompts)
1030
2278
  \u2022 templates/ (output templates)
@@ -1037,13 +2285,13 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1037
2285
  message: "Proceed with update?",
1038
2286
  initialValue: true
1039
2287
  });
1040
- if (isCancel4(shouldUpdate) || !shouldUpdate) {
2288
+ if (isCancel5(shouldUpdate) || !shouldUpdate) {
1041
2289
  outro4("Update cancelled.");
1042
2290
  return;
1043
2291
  }
1044
2292
  s.start("Updating from package");
1045
2293
  for (const dataPath of dataPaths) {
1046
- copyDirToAllStoragePaths(path9.join(agentCoreDir, "templates"), "templates", [dataPath]);
2294
+ copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
1047
2295
  }
1048
2296
  const configFilePath = getConfigPath(workspacePath);
1049
2297
  const configContent = fs10.readFileSync(configFilePath, "utf-8");
@@ -1065,8 +2313,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1065
2313
  ``,
1066
2314
  `Your configuration and knowledge files were preserved.`
1067
2315
  ];
1068
- note4(summary.join("\n"), "Update Summary");
1069
- outro4(pc5.green("\u2713 Successfully updated from package!"));
2316
+ note5(summary.join("\n"), "Update Summary");
2317
+ outro4(pc6.green("\u2713 Successfully updated from package!"));
1070
2318
  } catch (error) {
1071
2319
  s.stop("Error occurred");
1072
2320
  cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
@@ -1074,8 +2322,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1074
2322
  }
1075
2323
  }
1076
2324
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
1077
- const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
1078
- const workspacePath = path9.join(workspaceRoot, ".rrce-workflow");
2325
+ const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
2326
+ const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
1079
2327
  switch (mode) {
1080
2328
  case "global":
1081
2329
  return [globalPath];
@@ -1088,7 +2336,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
1088
2336
 
1089
2337
  // src/commands/wizard/index.ts
1090
2338
  async function runWizard() {
1091
- intro(pc6.cyan(pc6.inverse(" RRCE-Workflow Setup ")));
2339
+ intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
1092
2340
  const s = spinner5();
1093
2341
  s.start("Detecting environment");
1094
2342
  const workspacePath = detectWorkspaceRoot();
@@ -1096,9 +2344,9 @@ async function runWizard() {
1096
2344
  const gitUser = getGitUser();
1097
2345
  await new Promise((r) => setTimeout(r, 800));
1098
2346
  s.stop("Environment detected");
1099
- note5(
1100
- `Git User: ${pc6.bold(gitUser || "(not found)")}
1101
- Workspace: ${pc6.bold(workspaceName)}`,
2347
+ note6(
2348
+ `Git User: ${pc7.bold(gitUser || "(not found)")}
2349
+ Workspace: ${pc7.bold(workspaceName)}`,
1102
2350
  "Context"
1103
2351
  );
1104
2352
  const detectedProjects = scanForProjects({
@@ -1136,11 +2384,11 @@ Workspace: ${pc6.bold(workspaceName)}`,
1136
2384
  }
1137
2385
  menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
1138
2386
  menuOptions.push({ value: "exit", label: "Exit" });
1139
- const action = await select2({
2387
+ const action = await select3({
1140
2388
  message: "This workspace is already configured. What would you like to do?",
1141
2389
  options: menuOptions
1142
2390
  });
1143
- if (isCancel5(action) || action === "exit") {
2391
+ if (isCancel6(action) || action === "exit") {
1144
2392
  outro5("Exited.");
1145
2393
  process.exit(0);
1146
2394
  }
@@ -1161,42 +2409,59 @@ Workspace: ${pc6.bold(workspaceName)}`,
1161
2409
  }
1162
2410
 
1163
2411
  // src/commands/selector.ts
1164
- import { intro as intro2, select as select3, note as note6, cancel as cancel6, isCancel as isCancel6, outro as outro6 } from "@clack/prompts";
1165
- import pc7 from "picocolors";
1166
- import * as path10 from "path";
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";
1167
2416
  async function runSelector() {
1168
- const workspaceName = path10.basename(process.cwd());
1169
- intro2(pc7.cyan(pc7.inverse(` RRCE-Workflow | ${workspaceName} `)));
2417
+ const workspaceName = path16.basename(process.cwd());
2418
+ intro3(pc9.cyan(pc9.inverse(` RRCE-Workflow | ${workspaceName} `)));
1170
2419
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1171
2420
  if (prompts.length === 0) {
1172
- cancel6("No agents found. Run `rrce-workflow` to set up.");
2421
+ cancel7("No agents found. Run `rrce-workflow` to set up.");
1173
2422
  process.exit(0);
1174
2423
  }
1175
- const selection = await select3({
2424
+ const selection = await select5({
1176
2425
  message: "Select an agent:",
1177
- options: prompts.map((p) => ({
1178
- value: p,
1179
- label: p.frontmatter.name,
1180
- hint: p.frontmatter.description
1181
- }))
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
+ ]
1182
2438
  });
1183
- if (isCancel6(selection)) {
1184
- cancel6("Selection cancelled.");
2439
+ if (isCancel8(selection)) {
2440
+ cancel7("Selection cancelled.");
1185
2441
  process.exit(0);
1186
2442
  }
2443
+ if (selection === "mcp") {
2444
+ const { runMCP: runMCP2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
2445
+ await runMCP2();
2446
+ return;
2447
+ }
1187
2448
  const prompt = selection;
1188
- note6(
2449
+ note8(
1189
2450
  `Use this agent in your IDE by invoking:
1190
- ${pc7.bold(pc7.cyan(`@${prompt.frontmatter.name}`))}`,
2451
+ ${pc9.bold(pc9.cyan(`@${prompt.frontmatter.name}`))}`,
1191
2452
  "Agent Selected"
1192
2453
  );
1193
- outro6("Done");
2454
+ outro7("Done");
1194
2455
  }
1195
2456
 
1196
2457
  // src/index.ts
2458
+ init_mcp();
1197
2459
  var command = process.argv[2];
2460
+ var subcommand = process.argv[3];
1198
2461
  if (!command || command === "wizard") {
1199
2462
  runWizard();
2463
+ } else if (command === "mcp") {
2464
+ runMCP(subcommand);
1200
2465
  } else {
1201
2466
  runSelector();
1202
2467
  }