rrce-workflow 0.2.21 → 0.2.23

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 +1103 -259
  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,8 +124,224 @@ 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
+
136
+ // src/lib/autocomplete-prompt.ts
137
+ import * as fs6 from "fs";
138
+ import * as path6 from "path";
139
+ import * as readline from "readline";
140
+ import pc from "picocolors";
141
+ function directoryPrompt(opts) {
142
+ return new Promise((resolve) => {
143
+ process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
144
+ `);
145
+ process.stdout.write(`${pc.cyan("\u2502")} `);
146
+ const rl = readline.createInterface({
147
+ input: process.stdin,
148
+ output: process.stdout,
149
+ completer: completeDirectory,
150
+ terminal: true
151
+ });
152
+ if (opts.defaultValue) {
153
+ rl.write(opts.defaultValue);
154
+ }
155
+ rl.on("line", (input) => {
156
+ const value = input.trim();
157
+ const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
158
+ if (opts.validate) {
159
+ const error = opts.validate(expandedPath);
160
+ if (error) {
161
+ process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
162
+ `);
163
+ process.stdout.write(`${pc.cyan("\u2502")} `);
164
+ rl.write(value);
165
+ return;
166
+ }
167
+ }
168
+ rl.close();
169
+ process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
170
+ `);
171
+ resolve(expandedPath);
172
+ });
173
+ rl.on("close", () => {
174
+ });
175
+ rl.on("SIGINT", () => {
176
+ rl.close();
177
+ process.stdout.write("\n");
178
+ resolve(/* @__PURE__ */ Symbol("cancel"));
179
+ });
180
+ });
181
+ }
182
+ function completeDirectory(line) {
183
+ const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
184
+ try {
185
+ let dirToScan;
186
+ let prefix;
187
+ let basePath;
188
+ if (expanded === "" || expanded === "/") {
189
+ dirToScan = expanded || "/";
190
+ prefix = "";
191
+ basePath = expanded;
192
+ } else if (expanded.endsWith("/")) {
193
+ dirToScan = expanded;
194
+ prefix = "";
195
+ basePath = expanded;
196
+ } else {
197
+ dirToScan = path6.dirname(expanded);
198
+ prefix = path6.basename(expanded).toLowerCase();
199
+ basePath = dirToScan === "/" ? "/" : dirToScan + "/";
200
+ }
201
+ if (!fs6.existsSync(dirToScan)) {
202
+ return [[], line];
203
+ }
204
+ const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
205
+ if (!entry.isDirectory()) return false;
206
+ if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
207
+ return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
208
+ }).map((entry) => {
209
+ const fullPath = path6.join(dirToScan, entry.name);
210
+ const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
211
+ return displayPath + "/";
212
+ }).sort();
213
+ if (entries.length === 1) {
214
+ return [entries, line];
215
+ }
216
+ if (entries.length > 1) {
217
+ const commonPrefix = getCommonPrefix(entries);
218
+ if (commonPrefix.length > line.length) {
219
+ return [[commonPrefix], line];
220
+ }
221
+ }
222
+ return [entries, line];
223
+ } catch {
224
+ return [[], line];
225
+ }
226
+ }
227
+ function getCommonPrefix(strings) {
228
+ if (strings.length === 0) return "";
229
+ if (strings.length === 1) return strings[0] || "";
230
+ let prefix = strings[0] || "";
231
+ for (let i = 1; i < strings.length; i++) {
232
+ const str = strings[i] || "";
233
+ while (prefix.length > 0 && !str.startsWith(prefix)) {
234
+ prefix = prefix.slice(0, -1);
235
+ }
236
+ }
237
+ return prefix;
238
+ }
239
+ function isCancelled(value) {
240
+ return typeof value === "symbol";
241
+ }
242
+ var init_autocomplete_prompt = __esm({
243
+ "src/lib/autocomplete-prompt.ts"() {
244
+ "use strict";
245
+ }
246
+ });
247
+
248
+ // src/lib/tui-utils.ts
249
+ var tui_utils_exports = {};
250
+ __export(tui_utils_exports, {
251
+ resolveGlobalPath: () => resolveGlobalPath
252
+ });
253
+ import { select, note, isCancel } from "@clack/prompts";
254
+ import pc2 from "picocolors";
255
+ import * as path7 from "path";
256
+ async function resolveGlobalPath() {
257
+ const defaultPath = getDefaultRRCEHome();
258
+ const isDefaultWritable = checkWriteAccess(defaultPath);
259
+ const options = [
260
+ {
261
+ value: "default",
262
+ label: `Default (${defaultPath})`,
263
+ hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
264
+ },
265
+ {
266
+ value: "custom",
267
+ label: "Custom path",
268
+ hint: "Specify your own directory"
269
+ }
270
+ ];
271
+ const choice = await select({
272
+ message: "Global storage location:",
273
+ options,
274
+ initialValue: isDefaultWritable ? "default" : "custom"
275
+ });
276
+ if (isCancel(choice)) {
277
+ return void 0;
278
+ }
279
+ if (choice === "default") {
280
+ if (!isDefaultWritable) {
281
+ note(
282
+ `${pc2.yellow("\u26A0")} Cannot write to default path:
283
+ ${pc2.dim(defaultPath)}
284
+
285
+ This can happen when running via npx/bunx in restricted environments.
286
+ Please choose a custom path instead.`,
287
+ "Write Access Issue"
288
+ );
289
+ return resolveGlobalPath();
290
+ }
291
+ return defaultPath;
292
+ }
293
+ const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
294
+ const customPath = await directoryPrompt({
295
+ message: "Enter custom global path (Tab to autocomplete):",
296
+ defaultValue: suggestedPath,
297
+ validate: (value) => {
298
+ if (!value.trim()) {
299
+ return "Path cannot be empty";
300
+ }
301
+ if (!checkWriteAccess(value)) {
302
+ return `Cannot write to ${value}. Please choose a writable path.`;
303
+ }
304
+ return void 0;
305
+ }
306
+ });
307
+ if (isCancelled(customPath)) {
308
+ return void 0;
309
+ }
310
+ let expandedPath = customPath;
311
+ if (!expandedPath.endsWith(".rrce-workflow")) {
312
+ expandedPath = path7.join(expandedPath, ".rrce-workflow");
313
+ }
314
+ return expandedPath;
315
+ }
316
+ var init_tui_utils = __esm({
317
+ "src/lib/tui-utils.ts"() {
318
+ "use strict";
319
+ init_paths();
320
+ init_autocomplete_prompt();
321
+ }
322
+ });
323
+
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";
328
+
329
+ // src/lib/git.ts
330
+ import { execSync } from "child_process";
331
+ function getGitUser() {
332
+ try {
333
+ const result = execSync("git config user.name", { encoding: "utf-8" });
334
+ return result.trim() || null;
335
+ } catch {
336
+ return null;
337
+ }
338
+ }
339
+
340
+ // src/commands/wizard/index.ts
341
+ init_paths();
135
342
 
136
343
  // src/lib/detection.ts
344
+ init_paths();
137
345
  import * as fs2 from "fs";
138
346
  import * as path2 from "path";
139
347
  var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -279,10 +487,11 @@ function parseWorkspaceConfig(configPath) {
279
487
  }
280
488
 
281
489
  // 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";
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";
284
493
  import * as fs7 from "fs";
285
- import * as path7 from "path";
494
+ import * as path8 from "path";
286
495
 
287
496
  // src/lib/prompts.ts
288
497
  import * as fs3 from "fs";
@@ -355,6 +564,7 @@ function getAgentCorePromptsDir() {
355
564
  }
356
565
 
357
566
  // src/commands/wizard/utils.ts
567
+ init_paths();
358
568
  import * as fs4 from "fs";
359
569
  import * as path4 from "path";
360
570
  function copyPromptsToDir(prompts, targetDir, extension) {
@@ -381,6 +591,7 @@ function copyDirRecursive(src, dest) {
381
591
  }
382
592
 
383
593
  // src/commands/wizard/vscode.ts
594
+ init_paths();
384
595
  import * as fs5 from "fs";
385
596
  import * as path5 from "path";
386
597
  function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
@@ -458,124 +669,18 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
458
669
  workspace.settings["files.readonlyInclude"] = {
459
670
  ...cleanedReadonly,
460
671
  ...readonlyPatterns
461
- };
462
- }
463
- fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
464
- }
465
-
466
- // src/lib/autocomplete-prompt.ts
467
- import * as fs6 from "fs";
468
- import * as path6 from "path";
469
- import * as readline from "readline";
470
- import pc from "picocolors";
471
- function directoryPrompt(opts) {
472
- return new Promise((resolve) => {
473
- process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
474
- `);
475
- process.stdout.write(`${pc.cyan("\u2502")} `);
476
- const rl = readline.createInterface({
477
- input: process.stdin,
478
- output: process.stdout,
479
- completer: completeDirectory,
480
- terminal: true
481
- });
482
- if (opts.defaultValue) {
483
- rl.write(opts.defaultValue);
484
- }
485
- rl.on("line", (input) => {
486
- const value = input.trim();
487
- const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
488
- if (opts.validate) {
489
- const error = opts.validate(expandedPath);
490
- if (error) {
491
- process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
492
- `);
493
- process.stdout.write(`${pc.cyan("\u2502")} `);
494
- rl.write(value);
495
- return;
496
- }
497
- }
498
- rl.close();
499
- process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
500
- `);
501
- resolve(expandedPath);
502
- });
503
- rl.on("close", () => {
504
- });
505
- rl.on("SIGINT", () => {
506
- rl.close();
507
- process.stdout.write("\n");
508
- resolve(/* @__PURE__ */ Symbol("cancel"));
509
- });
510
- });
511
- }
512
- function completeDirectory(line) {
513
- const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
514
- try {
515
- let dirToScan;
516
- let prefix;
517
- let basePath;
518
- if (expanded === "" || expanded === "/") {
519
- dirToScan = expanded || "/";
520
- prefix = "";
521
- basePath = expanded;
522
- } else if (expanded.endsWith("/")) {
523
- dirToScan = expanded;
524
- prefix = "";
525
- basePath = expanded;
526
- } else {
527
- dirToScan = path6.dirname(expanded);
528
- prefix = path6.basename(expanded).toLowerCase();
529
- basePath = dirToScan === "/" ? "/" : dirToScan + "/";
530
- }
531
- if (!fs6.existsSync(dirToScan)) {
532
- return [[], line];
533
- }
534
- const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
535
- if (!entry.isDirectory()) return false;
536
- if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
537
- return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
538
- }).map((entry) => {
539
- const fullPath = path6.join(dirToScan, entry.name);
540
- const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
541
- return displayPath + "/";
542
- }).sort();
543
- if (entries.length === 1) {
544
- return [entries, line];
545
- }
546
- if (entries.length > 1) {
547
- const commonPrefix = getCommonPrefix(entries);
548
- if (commonPrefix.length > line.length) {
549
- return [[commonPrefix], line];
550
- }
551
- }
552
- return [entries, line];
553
- } catch {
554
- return [[], line];
555
- }
556
- }
557
- function getCommonPrefix(strings) {
558
- if (strings.length === 0) return "";
559
- if (strings.length === 1) return strings[0] || "";
560
- let prefix = strings[0] || "";
561
- for (let i = 1; i < strings.length; i++) {
562
- const str = strings[i] || "";
563
- while (prefix.length > 0 && !str.startsWith(prefix)) {
564
- prefix = prefix.slice(0, -1);
565
- }
672
+ };
566
673
  }
567
- return prefix;
568
- }
569
- function isCancelled(value) {
570
- return typeof value === "symbol";
674
+ fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
571
675
  }
572
676
 
573
677
  // src/commands/wizard/setup-flow.ts
678
+ init_tui_utils();
574
679
  async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
575
680
  const s = spinner();
576
681
  const config = await group(
577
682
  {
578
- storageMode: () => select({
683
+ storageMode: () => select2({
579
684
  message: "Where should workflow data be stored?",
580
685
  options: [
581
686
  { value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
@@ -600,8 +705,8 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
600
705
  options: existingProjects.map((project) => ({
601
706
  value: `${project.name}:${project.source}`,
602
707
  // Unique key
603
- label: `${project.name} ${pc2.dim(`(${project.source})`)}`,
604
- hint: pc2.dim(
708
+ label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
709
+ hint: pc3.dim(
605
710
  project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
606
711
  )
607
712
  })),
@@ -609,7 +714,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
609
714
  });
610
715
  },
611
716
  addToGitignore: () => confirm({
612
- message: "Add generated folders to .gitignore?",
717
+ message: "Add generated folders to .gitignore? (as comments - uncomment if needed)",
613
718
  initialValue: true
614
719
  }),
615
720
  confirm: () => confirm({
@@ -656,7 +761,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
656
761
  `Storage: ${config.storageMode}`
657
762
  ];
658
763
  if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
659
- summary.push(`Global path: ${pc2.cyan(customGlobalPath)}`);
764
+ summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
660
765
  }
661
766
  if (dataPaths.length > 0) {
662
767
  summary.push(`Data paths:`);
@@ -669,13 +774,13 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
669
774
  const linkedProjects = config.linkedProjects;
670
775
  if (linkedProjects.length > 0) {
671
776
  summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
672
- summary.push(`Workspace file: ${pc2.cyan(`${workspaceName}.code-workspace`)}`);
777
+ summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
673
778
  }
674
- note(summary.join("\n"), "Setup Summary");
779
+ note2(summary.join("\n"), "Setup Summary");
675
780
  if (linkedProjects.length > 0) {
676
- outro(pc2.green(`\u2713 Setup complete! Open ${pc2.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
781
+ outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
677
782
  } else {
678
- outro(pc2.green(`\u2713 Setup complete! Your agents are ready to use.`));
783
+ outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
679
784
  }
680
785
  } catch (error) {
681
786
  s.stop("Error occurred");
@@ -683,77 +788,18 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
683
788
  process.exit(1);
684
789
  }
685
790
  }
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
791
  async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
746
792
  const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
747
793
  for (const dataPath of dataPaths) {
748
794
  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"));
795
+ ensureDir(path8.join(dataPath, "knowledge"));
796
+ ensureDir(path8.join(dataPath, "refs"));
797
+ ensureDir(path8.join(dataPath, "tasks"));
798
+ ensureDir(path8.join(dataPath, "templates"));
753
799
  }
754
800
  const agentCoreDir = getAgentCoreDir();
755
801
  syncMetadataToAll(agentCoreDir, dataPaths);
756
- copyDirToAllStoragePaths(path7.join(agentCoreDir, "templates"), "templates", dataPaths);
802
+ copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
757
803
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
758
804
  if (config.tools.includes("copilot")) {
759
805
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
@@ -765,8 +811,8 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
765
811
  ensureDir(antigravityPath);
766
812
  copyPromptsToDir(prompts, antigravityPath, ".md");
767
813
  }
768
- const workspaceConfigPath = path7.join(workspacePath, ".rrce-workflow", "config.yaml");
769
- ensureDir(path7.dirname(workspaceConfigPath));
814
+ const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
815
+ ensureDir(path8.dirname(workspaceConfigPath));
770
816
  let configContent = `# RRCE-Workflow Configuration
771
817
  version: 1
772
818
 
@@ -806,8 +852,8 @@ linked_projects:
806
852
  }
807
853
  }
808
854
  function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
809
- const globalPath = path7.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
810
- const workspacePath = path7.join(workspaceRoot, ".rrce-workflow");
855
+ const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
856
+ const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
811
857
  switch (mode) {
812
858
  case "global":
813
859
  return [globalPath];
@@ -818,7 +864,7 @@ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
818
864
  }
819
865
  }
820
866
  function updateGitignore(workspacePath, storageMode, tools) {
821
- const gitignorePath = path7.join(workspacePath, ".gitignore");
867
+ const gitignorePath = path8.join(workspacePath, ".gitignore");
822
868
  const entries = [];
823
869
  if (storageMode === "workspace") {
824
870
  entries.push(".rrce-workflow/");
@@ -841,7 +887,11 @@ function updateGitignore(workspacePath, storageMode, tools) {
841
887
  const newEntries = [];
842
888
  for (const entry of entries) {
843
889
  const entryWithoutSlash = entry.replace(/\/$/, "");
844
- if (!lines.some((line) => line === entry || line === entryWithoutSlash)) {
890
+ const commentedEntry = `# ${entry}`;
891
+ const commentedEntryNoSlash = `# ${entryWithoutSlash}`;
892
+ if (!lines.some(
893
+ (line) => line === entry || line === entryWithoutSlash || line === commentedEntry || line === commentedEntryNoSlash
894
+ )) {
845
895
  newEntries.push(entry);
846
896
  }
847
897
  }
@@ -853,9 +903,9 @@ function updateGitignore(workspacePath, storageMode, tools) {
853
903
  newContent += "\n";
854
904
  }
855
905
  if (newContent === "" || !content.includes("# rrce-workflow")) {
856
- newContent += "\n# rrce-workflow generated folders\n";
906
+ newContent += "\n# rrce-workflow generated folders (uncomment to ignore)\n";
857
907
  }
858
- newContent += newEntries.join("\n") + "\n";
908
+ newContent += newEntries.map((e) => `# ${e}`).join("\n") + "\n";
859
909
  fs7.writeFileSync(gitignorePath, newContent);
860
910
  return true;
861
911
  } catch {
@@ -864,8 +914,9 @@ function updateGitignore(workspacePath, storageMode, tools) {
864
914
  }
865
915
 
866
916
  // src/commands/wizard/link-flow.ts
867
- import { multiselect as multiselect2, spinner as spinner2, note as note2, outro as outro2, cancel as cancel2, isCancel as isCancel2 } from "@clack/prompts";
868
- import pc3 from "picocolors";
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";
869
920
  import * as fs8 from "fs";
870
921
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
871
922
  const projects = scanForProjects({
@@ -873,7 +924,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
873
924
  workspacePath
874
925
  });
875
926
  if (projects.length === 0) {
876
- outro2(pc3.yellow("No other projects found. Try setting up another project first."));
927
+ outro2(pc4.yellow("No other projects found. Try setting up another project first."));
877
928
  return;
878
929
  }
879
930
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
@@ -882,14 +933,14 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
882
933
  options: projects.map((project) => ({
883
934
  value: `${project.name}:${project.source}`,
884
935
  // Unique key
885
- label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
886
- hint: pc3.dim(
936
+ label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
937
+ hint: pc4.dim(
887
938
  project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
888
939
  )
889
940
  })),
890
941
  required: true
891
942
  });
892
- if (isCancel2(linkedProjects)) {
943
+ if (isCancel3(linkedProjects)) {
893
944
  cancel2("Cancelled.");
894
945
  process.exit(0);
895
946
  }
@@ -937,43 +988,44 @@ linked_projects:
937
988
  const workspaceFile = `${workspaceName}.code-workspace`;
938
989
  const summary = [
939
990
  `Linked projects:`,
940
- ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc3.dim(`(${p.source})`)}`),
991
+ ...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
941
992
  ``,
942
- `Workspace file: ${pc3.cyan(workspaceFile)}`
993
+ `Workspace file: ${pc4.cyan(workspaceFile)}`
943
994
  ];
944
- note2(summary.join("\n"), "Link Summary");
945
- outro2(pc3.green(`\u2713 Projects linked! Open ${pc3.bold(workspaceFile)} in VSCode to access linked data.`));
995
+ note3(summary.join("\n"), "Link Summary");
996
+ outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
946
997
  }
947
998
 
948
999
  // src/commands/wizard/sync-flow.ts
949
- import { confirm as confirm2, spinner as spinner3, note as note3, outro as outro3, cancel as cancel3, isCancel as isCancel3 } from "@clack/prompts";
950
- import pc4 from "picocolors";
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";
951
1003
  import * as fs9 from "fs";
952
- import * as path8 from "path";
1004
+ import * as path9 from "path";
953
1005
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
954
1006
  const localPath = getLocalWorkspacePath(workspacePath);
955
1007
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
956
- const globalPath = path8.join(customGlobalPath, "workspaces", workspaceName);
1008
+ const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
957
1009
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
958
1010
  const existingDirs = subdirs.filter(
959
- (dir) => fs9.existsSync(path8.join(localPath, dir))
1011
+ (dir) => fs9.existsSync(path9.join(localPath, dir))
960
1012
  );
961
1013
  if (existingDirs.length === 0) {
962
- outro3(pc4.yellow("No data found in workspace storage to sync."));
1014
+ outro3(pc5.yellow("No data found in workspace storage to sync."));
963
1015
  return;
964
1016
  }
965
- note3(
1017
+ note4(
966
1018
  `The following will be copied to global storage:
967
1019
  ${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
968
1020
 
969
- Destination: ${pc4.cyan(globalPath)}`,
1021
+ Destination: ${pc5.cyan(globalPath)}`,
970
1022
  "Sync Preview"
971
1023
  );
972
1024
  const shouldSync = await confirm2({
973
1025
  message: "Proceed with sync to global storage?",
974
1026
  initialValue: true
975
1027
  });
976
- if (isCancel3(shouldSync) || !shouldSync) {
1028
+ if (isCancel4(shouldSync) || !shouldSync) {
977
1029
  outro3("Sync cancelled.");
978
1030
  return;
979
1031
  }
@@ -982,8 +1034,8 @@ Destination: ${pc4.cyan(globalPath)}`,
982
1034
  try {
983
1035
  ensureDir(globalPath);
984
1036
  for (const dir of existingDirs) {
985
- const srcDir = path8.join(localPath, dir);
986
- const destDir = path8.join(globalPath, dir);
1037
+ const srcDir = path9.join(localPath, dir);
1038
+ const destDir = path9.join(globalPath, dir);
987
1039
  ensureDir(destDir);
988
1040
  copyDirRecursive(srcDir, destDir);
989
1041
  }
@@ -992,12 +1044,12 @@ Destination: ${pc4.cyan(globalPath)}`,
992
1044
  `Synced directories:`,
993
1045
  ...existingDirs.map((d) => ` \u2713 ${d}/`),
994
1046
  ``,
995
- `Global path: ${pc4.cyan(globalPath)}`,
1047
+ `Global path: ${pc5.cyan(globalPath)}`,
996
1048
  ``,
997
1049
  `Other projects can now link this knowledge!`
998
1050
  ];
999
- note3(summary.join("\n"), "Sync Summary");
1000
- outro3(pc4.green("\u2713 Workspace knowledge synced to global storage!"));
1051
+ note4(summary.join("\n"), "Sync Summary");
1052
+ outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
1001
1053
  } catch (error) {
1002
1054
  s.stop("Error occurred");
1003
1055
  cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
@@ -1006,10 +1058,11 @@ Destination: ${pc4.cyan(globalPath)}`,
1006
1058
  }
1007
1059
 
1008
1060
  // src/commands/wizard/update-flow.ts
1009
- import { confirm as confirm3, spinner as spinner4, note as note4, outro as outro4, cancel as cancel4, isCancel as isCancel4 } from "@clack/prompts";
1010
- import pc5 from "picocolors";
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";
1011
1064
  import * as fs10 from "fs";
1012
- import * as path9 from "path";
1065
+ import * as path10 from "path";
1013
1066
  async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
1014
1067
  const s = spinner4();
1015
1068
  s.start("Checking for updates");
@@ -1020,7 +1073,7 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
1020
1073
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
1021
1074
  const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
1022
1075
  s.stop("Updates found");
1023
- note4(
1076
+ note5(
1024
1077
  `The following will be updated from the package:
1025
1078
  \u2022 prompts/ (${prompts.length} agent prompts)
1026
1079
  \u2022 templates/ (output templates)
@@ -1033,13 +1086,13 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1033
1086
  message: "Proceed with update?",
1034
1087
  initialValue: true
1035
1088
  });
1036
- if (isCancel4(shouldUpdate) || !shouldUpdate) {
1089
+ if (isCancel5(shouldUpdate) || !shouldUpdate) {
1037
1090
  outro4("Update cancelled.");
1038
1091
  return;
1039
1092
  }
1040
1093
  s.start("Updating from package");
1041
1094
  for (const dataPath of dataPaths) {
1042
- copyDirToAllStoragePaths(path9.join(agentCoreDir, "templates"), "templates", [dataPath]);
1095
+ copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
1043
1096
  }
1044
1097
  const configFilePath = getConfigPath(workspacePath);
1045
1098
  const configContent = fs10.readFileSync(configFilePath, "utf-8");
@@ -1061,8 +1114,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1061
1114
  ``,
1062
1115
  `Your configuration and knowledge files were preserved.`
1063
1116
  ];
1064
- note4(summary.join("\n"), "Update Summary");
1065
- outro4(pc5.green("\u2713 Successfully updated from package!"));
1117
+ note5(summary.join("\n"), "Update Summary");
1118
+ outro4(pc6.green("\u2713 Successfully updated from package!"));
1066
1119
  } catch (error) {
1067
1120
  s.stop("Error occurred");
1068
1121
  cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
@@ -1070,8 +1123,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
1070
1123
  }
1071
1124
  }
1072
1125
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
1073
- const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
1074
- const workspacePath = path9.join(workspaceRoot, ".rrce-workflow");
1126
+ const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
1127
+ const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
1075
1128
  switch (mode) {
1076
1129
  case "global":
1077
1130
  return [globalPath];
@@ -1084,7 +1137,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
1084
1137
 
1085
1138
  // src/commands/wizard/index.ts
1086
1139
  async function runWizard() {
1087
- intro(pc6.cyan(pc6.inverse(" RRCE-Workflow Setup ")));
1140
+ intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
1088
1141
  const s = spinner5();
1089
1142
  s.start("Detecting environment");
1090
1143
  const workspacePath = detectWorkspaceRoot();
@@ -1092,9 +1145,9 @@ async function runWizard() {
1092
1145
  const gitUser = getGitUser();
1093
1146
  await new Promise((r) => setTimeout(r, 800));
1094
1147
  s.stop("Environment detected");
1095
- note5(
1096
- `Git User: ${pc6.bold(gitUser || "(not found)")}
1097
- Workspace: ${pc6.bold(workspaceName)}`,
1148
+ note6(
1149
+ `Git User: ${pc7.bold(gitUser || "(not found)")}
1150
+ Workspace: ${pc7.bold(workspaceName)}`,
1098
1151
  "Context"
1099
1152
  );
1100
1153
  const detectedProjects = scanForProjects({
@@ -1132,11 +1185,11 @@ Workspace: ${pc6.bold(workspaceName)}`,
1132
1185
  }
1133
1186
  menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
1134
1187
  menuOptions.push({ value: "exit", label: "Exit" });
1135
- const action = await select2({
1188
+ const action = await select3({
1136
1189
  message: "This workspace is already configured. What would you like to do?",
1137
1190
  options: menuOptions
1138
1191
  });
1139
- if (isCancel5(action) || action === "exit") {
1192
+ if (isCancel6(action) || action === "exit") {
1140
1193
  outro5("Exited.");
1141
1194
  process.exit(0);
1142
1195
  }
@@ -1157,18 +1210,18 @@ Workspace: ${pc6.bold(workspaceName)}`,
1157
1210
  }
1158
1211
 
1159
1212
  // src/commands/selector.ts
1160
- import { intro as intro2, select as select3, note as note6, cancel as cancel6, isCancel as isCancel6, outro as outro6 } from "@clack/prompts";
1161
- import pc7 from "picocolors";
1162
- import * as path10 from "path";
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";
1163
1216
  async function runSelector() {
1164
- const workspaceName = path10.basename(process.cwd());
1165
- intro2(pc7.cyan(pc7.inverse(` RRCE-Workflow | ${workspaceName} `)));
1217
+ const workspaceName = path11.basename(process.cwd());
1218
+ intro2(pc8.cyan(pc8.inverse(` RRCE-Workflow | ${workspaceName} `)));
1166
1219
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
1167
1220
  if (prompts.length === 0) {
1168
1221
  cancel6("No agents found. Run `rrce-workflow` to set up.");
1169
1222
  process.exit(0);
1170
1223
  }
1171
- const selection = await select3({
1224
+ const selection = await select4({
1172
1225
  message: "Select an agent:",
1173
1226
  options: prompts.map((p) => ({
1174
1227
  value: p,
@@ -1176,23 +1229,814 @@ async function runSelector() {
1176
1229
  hint: p.frontmatter.description
1177
1230
  }))
1178
1231
  });
1179
- if (isCancel6(selection)) {
1232
+ if (isCancel7(selection)) {
1180
1233
  cancel6("Selection cancelled.");
1181
1234
  process.exit(0);
1182
1235
  }
1183
1236
  const prompt = selection;
1184
- note6(
1237
+ note7(
1185
1238
  `Use this agent in your IDE by invoking:
1186
- ${pc7.bold(pc7.cyan(`@${prompt.frontmatter.name}`))}`,
1239
+ ${pc8.bold(pc8.cyan(`@${prompt.frontmatter.name}`))}`,
1187
1240
  "Agent Selected"
1188
1241
  );
1189
1242
  outro6("Done");
1190
1243
  }
1191
1244
 
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";
1248
+
1249
+ // src/mcp/config.ts
1250
+ init_paths();
1251
+ import * as fs12 from "fs";
1252
+ import * as path12 from "path";
1253
+
1254
+ // src/mcp/types.ts
1255
+ var DEFAULT_MCP_CONFIG = {
1256
+ server: {
1257
+ port: 3e3,
1258
+ autoStart: false
1259
+ },
1260
+ projects: [],
1261
+ defaults: {
1262
+ includeNew: true,
1263
+ permissions: {
1264
+ knowledge: true,
1265
+ tasks: true,
1266
+ refs: true
1267
+ }
1268
+ }
1269
+ };
1270
+ var DEFAULT_PERMISSIONS = {
1271
+ knowledge: true,
1272
+ tasks: true,
1273
+ refs: true
1274
+ };
1275
+
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
+ };
1303
+ }
1304
+ function loadMCPConfig() {
1305
+ const configPath = getMCPConfigPath();
1306
+ if (!fs12.existsSync(configPath)) {
1307
+ return { ...DEFAULT_MCP_CONFIG };
1308
+ }
1309
+ try {
1310
+ const content = fs12.readFileSync(configPath, "utf-8");
1311
+ return parseMCPConfig(content);
1312
+ } 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
+ }
1349
+ }
1350
+ return config;
1351
+ }
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
+
1366
+ server:
1367
+ port: ${config.server.port}
1368
+ autoStart: ${config.server.autoStart}
1369
+
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}
1376
+
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
+ }
1391
+ }
1392
+ return content;
1393
+ }
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 };
1400
+ }
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
+ }
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
+ }
1421
+
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 [];
1468
+ }
1469
+ const permissions = getProjectPermissions(config, projectName);
1470
+ if (!permissions.tasks) {
1471
+ return [];
1472
+ }
1473
+ const projects = scanForProjects();
1474
+ const project = projects.find((p) => p.name === projectName);
1475
+ if (!project?.tasksPath || !fs13.existsSync(project.tasksPath)) {
1476
+ return [];
1477
+ }
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
+ }
1490
+ }
1491
+ }
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
+ }
1525
+ }
1526
+ } catch {
1527
+ }
1528
+ }
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
+ ]
1585
+ }
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 };
1606
+ }
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"
1634
+ });
1635
+ }
1636
+ }
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
+ };
1650
+ }
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
+ };
1663
+ }
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}`);
1720
+ }
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
+ }
1743
+ }
1744
+ return {
1745
+ description: promptDef.description,
1746
+ messages: [{ role: "user", content: { type: "text", text: promptContent } }]
1747
+ };
1748
+ });
1749
+ }
1750
+ function stopMCPServer() {
1751
+ if (mcpServer) {
1752
+ mcpServer.close();
1753
+ mcpServer = null;
1754
+ }
1755
+ serverState = { running: false };
1756
+ console.error("RRCE MCP Hub stopped");
1757
+ }
1758
+ function getMCPServerStatus() {
1759
+ return { ...serverState };
1760
+ }
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
+ }
1781
+ }
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
+ }
1790
+ }
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
+ }
1829
+ }
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) {
1846
+ return false;
1847
+ }
1848
+ try {
1849
+ if (!fs15.existsSync(resolvedPath)) {
1850
+ fs15.mkdirSync(resolvedPath, { recursive: true });
1851
+ }
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
+ );
1861
+ 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
+ );
1867
+ return false;
1868
+ }
1869
+ }
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");
1876
+ if (projects.length === 0) {
1877
+ note8('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
1878
+ return;
1879
+ }
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}`);
1890
+ }
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}`));
1896
+ } else {
1897
+ lines.push(pc9.dim("Server: not running"));
1898
+ }
1899
+ note8(lines.join("\n"), "MCP Hub Status");
1900
+ }
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)) {
1931
+ return;
1932
+ }
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!
1942
+
1943
+ Exposed projects: ${exposedCount}
1944
+ Hidden projects: ${projects.length - exposedCount}`,
1945
+ "Configuration Updated"
1946
+ );
1947
+ }
1948
+ async function handleStartServer() {
1949
+ const s = spinner6();
1950
+ s.start("Starting MCP server...");
1951
+ 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.
1956
+
1957
+ To connect from Claude Desktop, add to your config:
1958
+ ` + pc9.cyan(`~/.config/claude/claude_desktop_config.json`),
1959
+ "Server Started"
1960
+ );
1961
+ } 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
+ );
1967
+ }
1968
+ }
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;
1981
+ }
1982
+ stopMCPServer();
1983
+ note8(pc9.green("MCP server stopped."), "Server Stopped");
1984
+ }
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
+
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"]
2013
+ }
2014
+ }
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
2021
+
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");
2031
+ }
2032
+
1192
2033
  // src/index.ts
1193
2034
  var command = process.argv[2];
2035
+ var subcommand = process.argv[3];
1194
2036
  if (!command || command === "wizard") {
1195
2037
  runWizard();
2038
+ } else if (command === "mcp") {
2039
+ runMCP(subcommand);
1196
2040
  } else {
1197
2041
  runSelector();
1198
2042
  }