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