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