rrce-workflow 0.2.22 → 0.2.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -0
- package/dist/index.js +1609 -344
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const result = execSync("git config user.name", { encoding: "utf-8" });
|
|
11
|
-
return result.trim() || null;
|
|
12
|
-
} catch {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
16
10
|
|
|
17
11
|
// src/lib/paths.ts
|
|
18
12
|
import * as fs from "fs";
|
|
19
13
|
import * as path from "path";
|
|
20
|
-
var RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
|
|
21
|
-
var RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
|
|
22
14
|
function detectWorkspaceRoot() {
|
|
23
15
|
if (RRCE_WORKSPACE) {
|
|
24
16
|
return RRCE_WORKSPACE;
|
|
@@ -132,30 +124,18 @@ function getEffectiveRRCEHome(workspaceRoot) {
|
|
|
132
124
|
}
|
|
133
125
|
return getDefaultRRCEHome();
|
|
134
126
|
}
|
|
127
|
+
var RRCE_HOME, RRCE_WORKSPACE;
|
|
128
|
+
var init_paths = __esm({
|
|
129
|
+
"src/lib/paths.ts"() {
|
|
130
|
+
"use strict";
|
|
131
|
+
RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || "~", ".rrce-workflow");
|
|
132
|
+
RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
135
|
|
|
136
136
|
// src/lib/detection.ts
|
|
137
137
|
import * as fs2 from "fs";
|
|
138
138
|
import * as path2 from "path";
|
|
139
|
-
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
140
|
-
"node_modules",
|
|
141
|
-
".git",
|
|
142
|
-
".cache",
|
|
143
|
-
".npm",
|
|
144
|
-
".yarn",
|
|
145
|
-
".pnpm",
|
|
146
|
-
".local",
|
|
147
|
-
".config",
|
|
148
|
-
".vscode",
|
|
149
|
-
".vscode-server",
|
|
150
|
-
"Library",
|
|
151
|
-
"Applications",
|
|
152
|
-
".Trash",
|
|
153
|
-
"snap",
|
|
154
|
-
".cargo",
|
|
155
|
-
".rustup",
|
|
156
|
-
".go",
|
|
157
|
-
".docker"
|
|
158
|
-
]);
|
|
159
139
|
function scanForProjects(options = {}) {
|
|
160
140
|
const { excludeWorkspace, workspacePath } = options;
|
|
161
141
|
const projects = [];
|
|
@@ -277,84 +257,1509 @@ function parseWorkspaceConfig(configPath) {
|
|
|
277
257
|
return null;
|
|
278
258
|
}
|
|
279
259
|
}
|
|
260
|
+
var SKIP_DIRECTORIES;
|
|
261
|
+
var init_detection = __esm({
|
|
262
|
+
"src/lib/detection.ts"() {
|
|
263
|
+
"use strict";
|
|
264
|
+
init_paths();
|
|
265
|
+
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
266
|
+
"node_modules",
|
|
267
|
+
".git",
|
|
268
|
+
".cache",
|
|
269
|
+
".npm",
|
|
270
|
+
".yarn",
|
|
271
|
+
".pnpm",
|
|
272
|
+
".local",
|
|
273
|
+
".config",
|
|
274
|
+
".vscode",
|
|
275
|
+
".vscode-server",
|
|
276
|
+
"Library",
|
|
277
|
+
"Applications",
|
|
278
|
+
".Trash",
|
|
279
|
+
"snap",
|
|
280
|
+
".cargo",
|
|
281
|
+
".rustup",
|
|
282
|
+
".go",
|
|
283
|
+
".docker"
|
|
284
|
+
]);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// src/types/prompt.ts
|
|
289
|
+
import { z } from "zod";
|
|
290
|
+
var PromptArgSchema, AutoIdentitySchema, PromptFrontmatterSchema;
|
|
291
|
+
var init_prompt = __esm({
|
|
292
|
+
"src/types/prompt.ts"() {
|
|
293
|
+
"use strict";
|
|
294
|
+
PromptArgSchema = z.object({
|
|
295
|
+
name: z.string(),
|
|
296
|
+
default: z.string().optional(),
|
|
297
|
+
prompt: z.string().optional()
|
|
298
|
+
});
|
|
299
|
+
AutoIdentitySchema = z.object({
|
|
300
|
+
user: z.string(),
|
|
301
|
+
model: z.string()
|
|
302
|
+
});
|
|
303
|
+
PromptFrontmatterSchema = z.object({
|
|
304
|
+
name: z.string(),
|
|
305
|
+
description: z.string(),
|
|
306
|
+
"argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
|
|
307
|
+
tools: z.array(z.string()).optional(),
|
|
308
|
+
"required-args": z.array(PromptArgSchema).optional(),
|
|
309
|
+
"optional-args": z.array(PromptArgSchema).optional(),
|
|
310
|
+
"auto-identity": AutoIdentitySchema.optional()
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// src/lib/prompts.ts
|
|
316
|
+
import * as fs3 from "fs";
|
|
317
|
+
import * as path3 from "path";
|
|
318
|
+
import { fileURLToPath } from "url";
|
|
319
|
+
import matter from "gray-matter";
|
|
320
|
+
function parsePromptFile(filePath) {
|
|
321
|
+
try {
|
|
322
|
+
const fileContent = fs3.readFileSync(filePath, "utf-8");
|
|
323
|
+
const { data, content } = matter(fileContent);
|
|
324
|
+
const parsed = PromptFrontmatterSchema.safeParse(data);
|
|
325
|
+
if (!parsed.success) {
|
|
326
|
+
console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
frontmatter: parsed.data,
|
|
331
|
+
content: content.trim(),
|
|
332
|
+
filePath
|
|
333
|
+
};
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.error(`Error reading prompt file ${filePath}:`, error);
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function loadPromptsFromDir(dirPath) {
|
|
340
|
+
if (!fs3.existsSync(dirPath)) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".md"));
|
|
344
|
+
const prompts = [];
|
|
345
|
+
for (const file of files) {
|
|
346
|
+
const prompt = parsePromptFile(path3.join(dirPath, file));
|
|
347
|
+
if (prompt) {
|
|
348
|
+
prompts.push(prompt);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return prompts;
|
|
352
|
+
}
|
|
353
|
+
function getAgentCoreDir() {
|
|
354
|
+
return path3.join(__dirname, "..", "agent-core");
|
|
355
|
+
}
|
|
356
|
+
function getAgentCorePromptsDir() {
|
|
357
|
+
return path3.join(getAgentCoreDir(), "prompts");
|
|
358
|
+
}
|
|
359
|
+
var __filename, __dirname;
|
|
360
|
+
var init_prompts = __esm({
|
|
361
|
+
"src/lib/prompts.ts"() {
|
|
362
|
+
"use strict";
|
|
363
|
+
init_prompt();
|
|
364
|
+
__filename = fileURLToPath(import.meta.url);
|
|
365
|
+
__dirname = path3.dirname(__filename);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// src/lib/autocomplete-prompt.ts
|
|
370
|
+
import * as fs6 from "fs";
|
|
371
|
+
import * as path6 from "path";
|
|
372
|
+
import * as readline from "readline";
|
|
373
|
+
import pc from "picocolors";
|
|
374
|
+
function directoryPrompt(opts) {
|
|
375
|
+
return new Promise((resolve) => {
|
|
376
|
+
process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
|
|
377
|
+
`);
|
|
378
|
+
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
379
|
+
const rl = readline.createInterface({
|
|
380
|
+
input: process.stdin,
|
|
381
|
+
output: process.stdout,
|
|
382
|
+
completer: completeDirectory,
|
|
383
|
+
terminal: true
|
|
384
|
+
});
|
|
385
|
+
if (opts.defaultValue) {
|
|
386
|
+
rl.write(opts.defaultValue);
|
|
387
|
+
}
|
|
388
|
+
rl.on("line", (input) => {
|
|
389
|
+
const value = input.trim();
|
|
390
|
+
const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
|
|
391
|
+
if (opts.validate) {
|
|
392
|
+
const error = opts.validate(expandedPath);
|
|
393
|
+
if (error) {
|
|
394
|
+
process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
|
|
395
|
+
`);
|
|
396
|
+
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
397
|
+
rl.write(value);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
rl.close();
|
|
402
|
+
process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
|
|
403
|
+
`);
|
|
404
|
+
resolve(expandedPath);
|
|
405
|
+
});
|
|
406
|
+
rl.on("close", () => {
|
|
407
|
+
});
|
|
408
|
+
rl.on("SIGINT", () => {
|
|
409
|
+
rl.close();
|
|
410
|
+
process.stdout.write("\n");
|
|
411
|
+
resolve(/* @__PURE__ */ Symbol("cancel"));
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
function completeDirectory(line) {
|
|
416
|
+
const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
|
|
417
|
+
try {
|
|
418
|
+
let dirToScan;
|
|
419
|
+
let prefix;
|
|
420
|
+
let basePath;
|
|
421
|
+
if (expanded === "" || expanded === "/") {
|
|
422
|
+
dirToScan = expanded || "/";
|
|
423
|
+
prefix = "";
|
|
424
|
+
basePath = expanded;
|
|
425
|
+
} else if (expanded.endsWith("/")) {
|
|
426
|
+
dirToScan = expanded;
|
|
427
|
+
prefix = "";
|
|
428
|
+
basePath = expanded;
|
|
429
|
+
} else {
|
|
430
|
+
dirToScan = path6.dirname(expanded);
|
|
431
|
+
prefix = path6.basename(expanded).toLowerCase();
|
|
432
|
+
basePath = dirToScan === "/" ? "/" : dirToScan + "/";
|
|
433
|
+
}
|
|
434
|
+
if (!fs6.existsSync(dirToScan)) {
|
|
435
|
+
return [[], line];
|
|
436
|
+
}
|
|
437
|
+
const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
|
|
438
|
+
if (!entry.isDirectory()) return false;
|
|
439
|
+
if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
|
|
440
|
+
return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
|
|
441
|
+
}).map((entry) => {
|
|
442
|
+
const fullPath = path6.join(dirToScan, entry.name);
|
|
443
|
+
const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
|
|
444
|
+
return displayPath + "/";
|
|
445
|
+
}).sort();
|
|
446
|
+
if (entries.length === 1) {
|
|
447
|
+
return [entries, line];
|
|
448
|
+
}
|
|
449
|
+
if (entries.length > 1) {
|
|
450
|
+
const commonPrefix = getCommonPrefix(entries);
|
|
451
|
+
if (commonPrefix.length > line.length) {
|
|
452
|
+
return [[commonPrefix], line];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return [entries, line];
|
|
456
|
+
} catch {
|
|
457
|
+
return [[], line];
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
function getCommonPrefix(strings) {
|
|
461
|
+
if (strings.length === 0) return "";
|
|
462
|
+
if (strings.length === 1) return strings[0] || "";
|
|
463
|
+
let prefix = strings[0] || "";
|
|
464
|
+
for (let i = 1; i < strings.length; i++) {
|
|
465
|
+
const str = strings[i] || "";
|
|
466
|
+
while (prefix.length > 0 && !str.startsWith(prefix)) {
|
|
467
|
+
prefix = prefix.slice(0, -1);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return prefix;
|
|
471
|
+
}
|
|
472
|
+
function isCancelled(value) {
|
|
473
|
+
return typeof value === "symbol";
|
|
474
|
+
}
|
|
475
|
+
var init_autocomplete_prompt = __esm({
|
|
476
|
+
"src/lib/autocomplete-prompt.ts"() {
|
|
477
|
+
"use strict";
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// src/lib/tui-utils.ts
|
|
482
|
+
var tui_utils_exports = {};
|
|
483
|
+
__export(tui_utils_exports, {
|
|
484
|
+
resolveGlobalPath: () => resolveGlobalPath
|
|
485
|
+
});
|
|
486
|
+
import { select, note, isCancel } from "@clack/prompts";
|
|
487
|
+
import pc2 from "picocolors";
|
|
488
|
+
import * as path7 from "path";
|
|
489
|
+
async function resolveGlobalPath() {
|
|
490
|
+
const defaultPath = getDefaultRRCEHome();
|
|
491
|
+
const isDefaultWritable = checkWriteAccess(defaultPath);
|
|
492
|
+
const options = [
|
|
493
|
+
{
|
|
494
|
+
value: "default",
|
|
495
|
+
label: `Default (${defaultPath})`,
|
|
496
|
+
hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
value: "custom",
|
|
500
|
+
label: "Custom path",
|
|
501
|
+
hint: "Specify your own directory"
|
|
502
|
+
}
|
|
503
|
+
];
|
|
504
|
+
const choice = await select({
|
|
505
|
+
message: "Global storage location:",
|
|
506
|
+
options,
|
|
507
|
+
initialValue: isDefaultWritable ? "default" : "custom"
|
|
508
|
+
});
|
|
509
|
+
if (isCancel(choice)) {
|
|
510
|
+
return void 0;
|
|
511
|
+
}
|
|
512
|
+
if (choice === "default") {
|
|
513
|
+
if (!isDefaultWritable) {
|
|
514
|
+
note(
|
|
515
|
+
`${pc2.yellow("\u26A0")} Cannot write to default path:
|
|
516
|
+
${pc2.dim(defaultPath)}
|
|
517
|
+
|
|
518
|
+
This can happen when running via npx/bunx in restricted environments.
|
|
519
|
+
Please choose a custom path instead.`,
|
|
520
|
+
"Write Access Issue"
|
|
521
|
+
);
|
|
522
|
+
return resolveGlobalPath();
|
|
523
|
+
}
|
|
524
|
+
return defaultPath;
|
|
525
|
+
}
|
|
526
|
+
const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
|
|
527
|
+
const customPath = await directoryPrompt({
|
|
528
|
+
message: "Enter custom global path (Tab to autocomplete):",
|
|
529
|
+
defaultValue: suggestedPath,
|
|
530
|
+
validate: (value) => {
|
|
531
|
+
if (!value.trim()) {
|
|
532
|
+
return "Path cannot be empty";
|
|
533
|
+
}
|
|
534
|
+
if (!checkWriteAccess(value)) {
|
|
535
|
+
return `Cannot write to ${value}. Please choose a writable path.`;
|
|
536
|
+
}
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
if (isCancelled(customPath)) {
|
|
541
|
+
return void 0;
|
|
542
|
+
}
|
|
543
|
+
let expandedPath = customPath;
|
|
544
|
+
if (!expandedPath.endsWith(".rrce-workflow")) {
|
|
545
|
+
expandedPath = path7.join(expandedPath, ".rrce-workflow");
|
|
546
|
+
}
|
|
547
|
+
return expandedPath;
|
|
548
|
+
}
|
|
549
|
+
var init_tui_utils = __esm({
|
|
550
|
+
"src/lib/tui-utils.ts"() {
|
|
551
|
+
"use strict";
|
|
552
|
+
init_paths();
|
|
553
|
+
init_autocomplete_prompt();
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// src/mcp/types.ts
|
|
558
|
+
var DEFAULT_MCP_CONFIG, DEFAULT_PERMISSIONS;
|
|
559
|
+
var init_types = __esm({
|
|
560
|
+
"src/mcp/types.ts"() {
|
|
561
|
+
"use strict";
|
|
562
|
+
DEFAULT_MCP_CONFIG = {
|
|
563
|
+
server: {
|
|
564
|
+
port: 3e3,
|
|
565
|
+
autoStart: false
|
|
566
|
+
},
|
|
567
|
+
projects: [],
|
|
568
|
+
defaults: {
|
|
569
|
+
includeNew: true,
|
|
570
|
+
permissions: {
|
|
571
|
+
knowledge: true,
|
|
572
|
+
tasks: true,
|
|
573
|
+
refs: true
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
DEFAULT_PERMISSIONS = {
|
|
578
|
+
knowledge: true,
|
|
579
|
+
tasks: true,
|
|
580
|
+
refs: true
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// src/mcp/config.ts
|
|
586
|
+
import * as fs12 from "fs";
|
|
587
|
+
import * as path11 from "path";
|
|
588
|
+
function getMCPConfigPath() {
|
|
589
|
+
const workspaceRoot = detectWorkspaceRoot();
|
|
590
|
+
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
591
|
+
return path11.join(rrceHome, "mcp.yaml");
|
|
592
|
+
}
|
|
593
|
+
function ensureMCPGlobalPath() {
|
|
594
|
+
const workspaceRoot = detectWorkspaceRoot();
|
|
595
|
+
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
596
|
+
if (rrceHome.startsWith(".") || rrceHome.includes(".rrce-workflow/")) {
|
|
597
|
+
const configPath = path11.join(workspaceRoot, ".rrce-workflow", "config.yaml");
|
|
598
|
+
if (fs12.existsSync(configPath)) {
|
|
599
|
+
const content = fs12.readFileSync(configPath, "utf-8");
|
|
600
|
+
const modeMatch = content.match(/mode:\s*(global|workspace)/);
|
|
601
|
+
if (modeMatch?.[1] === "workspace") {
|
|
602
|
+
return {
|
|
603
|
+
configured: false,
|
|
604
|
+
path: rrceHome,
|
|
605
|
+
reason: "Workspace mode configured. MCP requires a global storage path."
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
configured: true,
|
|
612
|
+
path: rrceHome
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
function loadMCPConfig() {
|
|
616
|
+
const configPath = getMCPConfigPath();
|
|
617
|
+
if (!fs12.existsSync(configPath)) {
|
|
618
|
+
return { ...DEFAULT_MCP_CONFIG };
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
const content = fs12.readFileSync(configPath, "utf-8");
|
|
622
|
+
return parseMCPConfig(content);
|
|
623
|
+
} catch {
|
|
624
|
+
return { ...DEFAULT_MCP_CONFIG };
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function saveMCPConfig(config) {
|
|
628
|
+
const configPath = getMCPConfigPath();
|
|
629
|
+
const dir = path11.dirname(configPath);
|
|
630
|
+
if (!fs12.existsSync(dir)) {
|
|
631
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
632
|
+
}
|
|
633
|
+
const content = serializeMCPConfig(config);
|
|
634
|
+
fs12.writeFileSync(configPath, content);
|
|
635
|
+
}
|
|
636
|
+
function parseMCPConfig(content) {
|
|
637
|
+
const config = { ...DEFAULT_MCP_CONFIG, projects: [] };
|
|
638
|
+
const lines = content.split("\n");
|
|
639
|
+
let currentSection = null;
|
|
640
|
+
let currentProject = null;
|
|
641
|
+
let inPermissions = false;
|
|
642
|
+
for (const line of lines) {
|
|
643
|
+
const trimmed = line.trim();
|
|
644
|
+
if (trimmed.startsWith("#") || trimmed === "") continue;
|
|
645
|
+
if (line.match(/^server:/)) {
|
|
646
|
+
currentSection = "server";
|
|
647
|
+
currentProject = null;
|
|
648
|
+
inPermissions = false;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (line.match(/^defaults:/)) {
|
|
652
|
+
currentSection = "defaults";
|
|
653
|
+
currentProject = null;
|
|
654
|
+
inPermissions = false;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (line.match(/^projects:/)) {
|
|
658
|
+
currentSection = "projects";
|
|
659
|
+
currentProject = null;
|
|
660
|
+
inPermissions = false;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (currentSection === "server") {
|
|
664
|
+
const portMatch = trimmed.match(/^port:\s*(\d+)/);
|
|
665
|
+
if (portMatch?.[1]) config.server.port = parseInt(portMatch[1], 10);
|
|
666
|
+
const autoStartMatch = trimmed.match(/^autoStart:\s*(true|false)/);
|
|
667
|
+
if (autoStartMatch) config.server.autoStart = autoStartMatch[1] === "true";
|
|
668
|
+
}
|
|
669
|
+
if (currentSection === "defaults") {
|
|
670
|
+
const includeNewMatch = trimmed.match(/^includeNew:\s*(true|false)/);
|
|
671
|
+
if (includeNewMatch) config.defaults.includeNew = includeNewMatch[1] === "true";
|
|
672
|
+
if (trimmed === "permissions:") {
|
|
673
|
+
inPermissions = true;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (inPermissions) {
|
|
677
|
+
const knowledgeMatch = trimmed.match(/^knowledge:\s*(true|false)/);
|
|
678
|
+
if (knowledgeMatch) config.defaults.permissions.knowledge = knowledgeMatch[1] === "true";
|
|
679
|
+
const tasksMatch = trimmed.match(/^tasks:\s*(true|false)/);
|
|
680
|
+
if (tasksMatch) config.defaults.permissions.tasks = tasksMatch[1] === "true";
|
|
681
|
+
const refsMatch = trimmed.match(/^refs:\s*(true|false)/);
|
|
682
|
+
if (refsMatch) config.defaults.permissions.refs = refsMatch[1] === "true";
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (currentSection === "projects") {
|
|
686
|
+
const projectNameMatch = line.match(/^\s+-\s+name:\s*["']?([^"'\n]+)["']?/);
|
|
687
|
+
if (projectNameMatch) {
|
|
688
|
+
if (currentProject && currentProject.name) {
|
|
689
|
+
config.projects.push(currentProject);
|
|
690
|
+
}
|
|
691
|
+
currentProject = {
|
|
692
|
+
name: projectNameMatch[1].trim(),
|
|
693
|
+
expose: true,
|
|
694
|
+
permissions: { ...DEFAULT_PERMISSIONS }
|
|
695
|
+
};
|
|
696
|
+
inPermissions = false;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (currentProject) {
|
|
700
|
+
const exposeMatch = trimmed.match(/^expose:\s*(true|false)/);
|
|
701
|
+
if (exposeMatch) {
|
|
702
|
+
currentProject.expose = exposeMatch[1] === "true";
|
|
703
|
+
}
|
|
704
|
+
if (trimmed === "permissions:") {
|
|
705
|
+
inPermissions = true;
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (inPermissions) {
|
|
709
|
+
const knowledgeMatch = trimmed.match(/^knowledge:\s*(true|false)/);
|
|
710
|
+
if (knowledgeMatch) currentProject.permissions.knowledge = knowledgeMatch[1] === "true";
|
|
711
|
+
const tasksMatch = trimmed.match(/^tasks:\s*(true|false)/);
|
|
712
|
+
if (tasksMatch) currentProject.permissions.tasks = tasksMatch[1] === "true";
|
|
713
|
+
const refsMatch = trimmed.match(/^refs:\s*(true|false)/);
|
|
714
|
+
if (refsMatch) currentProject.permissions.refs = refsMatch[1] === "true";
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (currentProject && currentProject.name) {
|
|
720
|
+
config.projects.push(currentProject);
|
|
721
|
+
}
|
|
722
|
+
return config;
|
|
723
|
+
}
|
|
724
|
+
function serializeMCPConfig(config) {
|
|
725
|
+
let content = `# RRCE MCP Hub Configuration
|
|
726
|
+
# Manages which projects are exposed via MCP
|
|
727
|
+
|
|
728
|
+
server:
|
|
729
|
+
port: ${config.server.port}
|
|
730
|
+
autoStart: ${config.server.autoStart}
|
|
731
|
+
|
|
732
|
+
defaults:
|
|
733
|
+
includeNew: ${config.defaults.includeNew}
|
|
734
|
+
permissions:
|
|
735
|
+
knowledge: ${config.defaults.permissions.knowledge}
|
|
736
|
+
tasks: ${config.defaults.permissions.tasks}
|
|
737
|
+
refs: ${config.defaults.permissions.refs}
|
|
738
|
+
|
|
739
|
+
projects:
|
|
740
|
+
`;
|
|
741
|
+
if (config.projects.length === 0) {
|
|
742
|
+
content += ' # No projects configured yet. Run "rrce-workflow mcp" to add projects.\n';
|
|
743
|
+
} else {
|
|
744
|
+
for (const project of config.projects) {
|
|
745
|
+
content += ` - name: "${project.name}"
|
|
746
|
+
expose: ${project.expose}
|
|
747
|
+
permissions:
|
|
748
|
+
knowledge: ${project.permissions.knowledge}
|
|
749
|
+
tasks: ${project.permissions.tasks}
|
|
750
|
+
refs: ${project.permissions.refs}
|
|
751
|
+
`;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return content;
|
|
755
|
+
}
|
|
756
|
+
function setProjectConfig(config, name, expose, permissions) {
|
|
757
|
+
const existing = config.projects.find((p) => p.name === name);
|
|
758
|
+
if (existing) {
|
|
759
|
+
existing.expose = expose;
|
|
760
|
+
if (permissions) {
|
|
761
|
+
existing.permissions = { ...existing.permissions, ...permissions };
|
|
762
|
+
}
|
|
763
|
+
} else {
|
|
764
|
+
config.projects.push({
|
|
765
|
+
name,
|
|
766
|
+
expose,
|
|
767
|
+
permissions: permissions ? { ...DEFAULT_PERMISSIONS, ...permissions } : { ...DEFAULT_PERMISSIONS }
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return config;
|
|
771
|
+
}
|
|
772
|
+
function isProjectExposed(config, name) {
|
|
773
|
+
const project = config.projects.find((p) => p.name === name);
|
|
774
|
+
if (project) {
|
|
775
|
+
return project.expose;
|
|
776
|
+
}
|
|
777
|
+
return config.defaults.includeNew;
|
|
778
|
+
}
|
|
779
|
+
function getProjectPermissions(config, name) {
|
|
780
|
+
const project = config.projects.find((p) => p.name === name);
|
|
781
|
+
return project?.permissions ?? config.defaults.permissions;
|
|
782
|
+
}
|
|
783
|
+
var init_config = __esm({
|
|
784
|
+
"src/mcp/config.ts"() {
|
|
785
|
+
"use strict";
|
|
786
|
+
init_paths();
|
|
787
|
+
init_types();
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
// src/mcp/logger.ts
|
|
792
|
+
var logger_exports = {};
|
|
793
|
+
__export(logger_exports, {
|
|
794
|
+
getLogFilePath: () => getLogFilePath,
|
|
795
|
+
logger: () => logger
|
|
796
|
+
});
|
|
797
|
+
import * as fs13 from "fs";
|
|
798
|
+
import * as path12 from "path";
|
|
799
|
+
function getLogFilePath() {
|
|
800
|
+
const workspaceRoot = detectWorkspaceRoot();
|
|
801
|
+
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
802
|
+
return path12.join(rrceHome, "mcp-server.log");
|
|
803
|
+
}
|
|
804
|
+
var Logger, logger;
|
|
805
|
+
var init_logger = __esm({
|
|
806
|
+
"src/mcp/logger.ts"() {
|
|
807
|
+
"use strict";
|
|
808
|
+
init_paths();
|
|
809
|
+
Logger = class {
|
|
810
|
+
logPath;
|
|
811
|
+
constructor() {
|
|
812
|
+
this.logPath = getLogFilePath();
|
|
813
|
+
}
|
|
814
|
+
write(level, message, data) {
|
|
815
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
816
|
+
let logMessage = `[${timestamp}] [${level}] ${message}`;
|
|
817
|
+
if (data) {
|
|
818
|
+
if (data instanceof Error) {
|
|
819
|
+
logMessage += `
|
|
820
|
+
${data.stack || data.message}`;
|
|
821
|
+
} else {
|
|
822
|
+
try {
|
|
823
|
+
logMessage += `
|
|
824
|
+
${JSON.stringify(data, null, 2)}`;
|
|
825
|
+
} catch (e) {
|
|
826
|
+
logMessage += `
|
|
827
|
+
[Circular or invalid data]`;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
logMessage += "\n";
|
|
832
|
+
try {
|
|
833
|
+
const dir = path12.dirname(this.logPath);
|
|
834
|
+
if (!fs13.existsSync(dir)) {
|
|
835
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
836
|
+
}
|
|
837
|
+
fs13.appendFileSync(this.logPath, logMessage);
|
|
838
|
+
} catch (e) {
|
|
839
|
+
console.error(`[Logger Failure] Could not write to ${this.logPath}`, e);
|
|
840
|
+
console.error(logMessage);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
info(message, data) {
|
|
844
|
+
this.write("INFO", message, data);
|
|
845
|
+
}
|
|
846
|
+
error(message, error) {
|
|
847
|
+
this.write("ERROR", message, error);
|
|
848
|
+
}
|
|
849
|
+
warn(message, data) {
|
|
850
|
+
this.write("WARN", message, data);
|
|
851
|
+
}
|
|
852
|
+
debug(message, data) {
|
|
853
|
+
this.write("DEBUG", message, data);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
logger = new Logger();
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
// src/mcp/resources.ts
|
|
861
|
+
import * as fs14 from "fs";
|
|
862
|
+
import * as path13 from "path";
|
|
863
|
+
function getExposedProjects() {
|
|
864
|
+
const config = loadMCPConfig();
|
|
865
|
+
const allProjects = scanForProjects();
|
|
866
|
+
return allProjects.filter((project) => isProjectExposed(config, project.name));
|
|
867
|
+
}
|
|
868
|
+
function getProjectContext(projectName) {
|
|
869
|
+
const config = loadMCPConfig();
|
|
870
|
+
if (!isProjectExposed(config, projectName)) {
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
const permissions = getProjectPermissions(config, projectName);
|
|
874
|
+
if (!permissions.knowledge) {
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
const projects = scanForProjects();
|
|
878
|
+
const project = projects.find((p) => p.name === projectName);
|
|
879
|
+
if (!project?.knowledgePath) {
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
const contextPath = path13.join(project.knowledgePath, "project-context.md");
|
|
883
|
+
if (!fs14.existsSync(contextPath)) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
return fs14.readFileSync(contextPath, "utf-8");
|
|
887
|
+
}
|
|
888
|
+
function getProjectTasks(projectName) {
|
|
889
|
+
const config = loadMCPConfig();
|
|
890
|
+
if (!isProjectExposed(config, projectName)) {
|
|
891
|
+
return [];
|
|
892
|
+
}
|
|
893
|
+
const permissions = getProjectPermissions(config, projectName);
|
|
894
|
+
if (!permissions.tasks) {
|
|
895
|
+
return [];
|
|
896
|
+
}
|
|
897
|
+
const projects = scanForProjects();
|
|
898
|
+
const project = projects.find((p) => p.name === projectName);
|
|
899
|
+
if (!project?.tasksPath || !fs14.existsSync(project.tasksPath)) {
|
|
900
|
+
return [];
|
|
901
|
+
}
|
|
902
|
+
const tasks = [];
|
|
903
|
+
try {
|
|
904
|
+
const taskDirs = fs14.readdirSync(project.tasksPath, { withFileTypes: true });
|
|
905
|
+
for (const dir of taskDirs) {
|
|
906
|
+
if (!dir.isDirectory()) continue;
|
|
907
|
+
const metaPath = path13.join(project.tasksPath, dir.name, "meta.json");
|
|
908
|
+
if (fs14.existsSync(metaPath)) {
|
|
909
|
+
try {
|
|
910
|
+
const meta = JSON.parse(fs14.readFileSync(metaPath, "utf-8"));
|
|
911
|
+
tasks.push(meta);
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
918
|
+
return tasks;
|
|
919
|
+
}
|
|
920
|
+
function searchKnowledge(query) {
|
|
921
|
+
const config = loadMCPConfig();
|
|
922
|
+
const projects = getExposedProjects();
|
|
923
|
+
const results = [];
|
|
924
|
+
const queryLower = query.toLowerCase();
|
|
925
|
+
for (const project of projects) {
|
|
926
|
+
const permissions = getProjectPermissions(config, project.name);
|
|
927
|
+
if (!permissions.knowledge || !project.knowledgePath) continue;
|
|
928
|
+
try {
|
|
929
|
+
const files = fs14.readdirSync(project.knowledgePath);
|
|
930
|
+
for (const file of files) {
|
|
931
|
+
if (!file.endsWith(".md")) continue;
|
|
932
|
+
const filePath = path13.join(project.knowledgePath, file);
|
|
933
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
934
|
+
const lines = content.split("\n");
|
|
935
|
+
const matches = [];
|
|
936
|
+
for (const line of lines) {
|
|
937
|
+
if (line.toLowerCase().includes(queryLower)) {
|
|
938
|
+
matches.push(line.trim());
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (matches.length > 0) {
|
|
942
|
+
results.push({
|
|
943
|
+
project: project.name,
|
|
944
|
+
file,
|
|
945
|
+
matches: matches.slice(0, 5)
|
|
946
|
+
// Limit to 5 matches per file
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
} catch {
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return results;
|
|
954
|
+
}
|
|
955
|
+
var init_resources = __esm({
|
|
956
|
+
"src/mcp/resources.ts"() {
|
|
957
|
+
"use strict";
|
|
958
|
+
init_config();
|
|
959
|
+
init_detection();
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// src/mcp/prompts.ts
|
|
964
|
+
var AGENT_PROMPTS;
|
|
965
|
+
var init_prompts2 = __esm({
|
|
966
|
+
"src/mcp/prompts.ts"() {
|
|
967
|
+
"use strict";
|
|
968
|
+
AGENT_PROMPTS = [
|
|
969
|
+
{
|
|
970
|
+
name: "init",
|
|
971
|
+
description: "Initialize project context by analyzing codebase structure, tech stack, and conventions",
|
|
972
|
+
file: "init.md",
|
|
973
|
+
arguments: [
|
|
974
|
+
{ name: "PROJECT_NAME", description: "Project name (optional, auto-detected if omitted)", required: false }
|
|
975
|
+
]
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: "research",
|
|
979
|
+
description: "Research and clarify requirements for a new task",
|
|
980
|
+
file: "research_discussion.md",
|
|
981
|
+
arguments: [
|
|
982
|
+
{ name: "REQUEST", description: "Description of the task or feature to research", required: true },
|
|
983
|
+
{ name: "TASK_SLUG", description: "Kebab-case identifier for the task", required: true },
|
|
984
|
+
{ name: "TITLE", description: "Human-readable title for the task", required: false }
|
|
985
|
+
]
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
name: "plan",
|
|
989
|
+
description: "Create an actionable execution plan from research findings",
|
|
990
|
+
file: "planning_orchestrator.md",
|
|
991
|
+
arguments: [
|
|
992
|
+
{ name: "TASK_SLUG", description: "Task slug to create plan for", required: true }
|
|
993
|
+
]
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
name: "execute",
|
|
997
|
+
description: "Implement the planned work with code and tests",
|
|
998
|
+
file: "executor.md",
|
|
999
|
+
arguments: [
|
|
1000
|
+
{ name: "TASK_SLUG", description: "Task slug to execute", required: true },
|
|
1001
|
+
{ name: "BRANCH", description: "Git branch reference (optional)", required: false }
|
|
1002
|
+
]
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
name: "docs",
|
|
1006
|
+
description: "Generate documentation for completed work",
|
|
1007
|
+
file: "documentation.md",
|
|
1008
|
+
arguments: [
|
|
1009
|
+
{ name: "DOC_TYPE", description: "Type of documentation (api, architecture, runbook, changelog)", required: true },
|
|
1010
|
+
{ name: "TASK_SLUG", description: "Task slug if documenting specific task", required: false }
|
|
1011
|
+
]
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
name: "sync",
|
|
1015
|
+
description: "Reconcile knowledge base with actual codebase state",
|
|
1016
|
+
file: "sync.md",
|
|
1017
|
+
arguments: [
|
|
1018
|
+
{ name: "SCOPE", description: "Specific path or module to sync (optional)", required: false }
|
|
1019
|
+
]
|
|
1020
|
+
}
|
|
1021
|
+
];
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
// src/mcp/server.ts
|
|
1026
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1027
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1028
|
+
import {
|
|
1029
|
+
CallToolRequestSchema,
|
|
1030
|
+
ListResourcesRequestSchema,
|
|
1031
|
+
ListToolsRequestSchema,
|
|
1032
|
+
ReadResourceRequestSchema,
|
|
1033
|
+
ListPromptsRequestSchema,
|
|
1034
|
+
GetPromptRequestSchema
|
|
1035
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
1036
|
+
import * as fs15 from "fs";
|
|
1037
|
+
import * as path14 from "path";
|
|
1038
|
+
async function startMCPServer() {
|
|
1039
|
+
try {
|
|
1040
|
+
logger.info("Starting MCP Server...");
|
|
1041
|
+
process.on("uncaughtException", (error) => {
|
|
1042
|
+
logger.error("Uncaught Exception", error);
|
|
1043
|
+
console.error("Uncaught Exception:", error);
|
|
1044
|
+
});
|
|
1045
|
+
process.on("unhandledRejection", (reason) => {
|
|
1046
|
+
logger.error("Unhandled Rejection", reason);
|
|
1047
|
+
console.error("Unhandled Rejection:", reason);
|
|
1048
|
+
});
|
|
1049
|
+
const config = loadMCPConfig();
|
|
1050
|
+
mcpServer = new Server(
|
|
1051
|
+
{ name: "rrce-mcp-hub", version: "1.0.0" },
|
|
1052
|
+
{ capabilities: { resources: {}, tools: {}, prompts: {} } }
|
|
1053
|
+
);
|
|
1054
|
+
mcpServer.onerror = (error) => {
|
|
1055
|
+
logger.error("MCP Server Error", error);
|
|
1056
|
+
};
|
|
1057
|
+
registerResourceHandlers(mcpServer);
|
|
1058
|
+
registerToolHandlers(mcpServer);
|
|
1059
|
+
registerPromptHandlers(mcpServer);
|
|
1060
|
+
const transport = new StdioServerTransport();
|
|
1061
|
+
await mcpServer.connect(transport);
|
|
1062
|
+
serverState = { running: true, port: config.server.port, pid: process.pid };
|
|
1063
|
+
const exposed = getExposedProjects().map((p) => p.name).join(", ");
|
|
1064
|
+
logger.info(`RRCE MCP Hub started (pid: ${process.pid})`, { exposedProjects: exposed });
|
|
1065
|
+
console.error(`RRCE MCP Hub started (pid: ${process.pid})`);
|
|
1066
|
+
console.error(`Exposed projects: ${exposed}`);
|
|
1067
|
+
return { port: config.server.port, pid: process.pid };
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
logger.error("Failed to start MCP server", error);
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
function registerResourceHandlers(server) {
|
|
1074
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1075
|
+
logger.debug("Listing resources");
|
|
1076
|
+
const projects = getExposedProjects();
|
|
1077
|
+
const resources = [];
|
|
1078
|
+
resources.push({
|
|
1079
|
+
uri: "rrce://projects",
|
|
1080
|
+
name: "Project List",
|
|
1081
|
+
description: "List of all RRCE projects exposed via MCP",
|
|
1082
|
+
mimeType: "application/json"
|
|
1083
|
+
});
|
|
1084
|
+
for (const project of projects) {
|
|
1085
|
+
const config = loadMCPConfig();
|
|
1086
|
+
const permissions = getProjectPermissions(config, project.name);
|
|
1087
|
+
if (permissions.knowledge) {
|
|
1088
|
+
resources.push({
|
|
1089
|
+
uri: `rrce://projects/${project.name}/context`,
|
|
1090
|
+
name: `${project.name} - Project Context`,
|
|
1091
|
+
description: `Project context and architecture for ${project.name}`,
|
|
1092
|
+
mimeType: "text/markdown"
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
if (permissions.tasks) {
|
|
1096
|
+
resources.push({
|
|
1097
|
+
uri: `rrce://projects/${project.name}/tasks`,
|
|
1098
|
+
name: `${project.name} - Tasks`,
|
|
1099
|
+
description: `Task list and status for ${project.name}`,
|
|
1100
|
+
mimeType: "application/json"
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return { resources };
|
|
1105
|
+
});
|
|
1106
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1107
|
+
const { uri } = request.params;
|
|
1108
|
+
logger.info(`Reading resource: ${uri}`);
|
|
1109
|
+
try {
|
|
1110
|
+
if (uri === "rrce://projects") {
|
|
1111
|
+
const projects = getExposedProjects();
|
|
1112
|
+
return {
|
|
1113
|
+
contents: [{
|
|
1114
|
+
uri,
|
|
1115
|
+
mimeType: "application/json",
|
|
1116
|
+
text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
|
|
1117
|
+
}]
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
const projectMatch = uri.match(/^rrce:\/\/projects\/([^/]+)\/(.+)$/);
|
|
1121
|
+
if (projectMatch) {
|
|
1122
|
+
const [, projectName, resourceType] = projectMatch;
|
|
1123
|
+
const content = resourceType === "context" ? getProjectContext(projectName) : JSON.stringify(getProjectTasks(projectName), null, 2);
|
|
1124
|
+
if (content === null) throw new Error(`Resource not found: ${uri}`);
|
|
1125
|
+
return {
|
|
1126
|
+
contents: [{
|
|
1127
|
+
uri,
|
|
1128
|
+
mimeType: resourceType === "tasks" ? "application/json" : "text/markdown",
|
|
1129
|
+
text: content
|
|
1130
|
+
}]
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
logger.error(`Failed to read resource: ${uri}`, error);
|
|
1136
|
+
throw error;
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
function registerToolHandlers(server) {
|
|
1141
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1142
|
+
tools: [
|
|
1143
|
+
{
|
|
1144
|
+
name: "search_knowledge",
|
|
1145
|
+
description: "Search across all exposed project knowledge bases",
|
|
1146
|
+
inputSchema: {
|
|
1147
|
+
type: "object",
|
|
1148
|
+
properties: { query: { type: "string", description: "Search query to find in knowledge files" } },
|
|
1149
|
+
required: ["query"]
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
name: "list_projects",
|
|
1154
|
+
description: "List all projects exposed via MCP",
|
|
1155
|
+
inputSchema: { type: "object", properties: {} }
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
name: "get_project_context",
|
|
1159
|
+
description: "Get the project context/architecture for a specific project",
|
|
1160
|
+
inputSchema: {
|
|
1161
|
+
type: "object",
|
|
1162
|
+
properties: { project: { type: "string", description: "Name of the project to get context for" } },
|
|
1163
|
+
required: ["project"]
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
]
|
|
1167
|
+
}));
|
|
1168
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1169
|
+
const { name, arguments: args } = request.params;
|
|
1170
|
+
logger.info(`Calling tool: ${name}`, args);
|
|
1171
|
+
try {
|
|
1172
|
+
switch (name) {
|
|
1173
|
+
case "search_knowledge": {
|
|
1174
|
+
const results = searchKnowledge(args.query);
|
|
1175
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
1176
|
+
}
|
|
1177
|
+
case "list_projects": {
|
|
1178
|
+
const projects = getExposedProjects();
|
|
1179
|
+
return {
|
|
1180
|
+
content: [{
|
|
1181
|
+
type: "text",
|
|
1182
|
+
text: JSON.stringify(projects.map((p) => ({ name: p.name, source: p.source, path: p.path })), null, 2)
|
|
1183
|
+
}]
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
case "get_project_context": {
|
|
1187
|
+
const context = getProjectContext(args.project);
|
|
1188
|
+
if (!context) {
|
|
1189
|
+
const msg = `No project context found for "${args.project}"`;
|
|
1190
|
+
logger.warn(msg);
|
|
1191
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
1192
|
+
}
|
|
1193
|
+
return { content: [{ type: "text", text: context }] };
|
|
1194
|
+
}
|
|
1195
|
+
default:
|
|
1196
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
1197
|
+
}
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
logger.error(`Tool execution failed: ${name}`, error);
|
|
1200
|
+
throw error;
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
function registerPromptHandlers(server) {
|
|
1205
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
1206
|
+
prompts: AGENT_PROMPTS.map((p) => ({
|
|
1207
|
+
name: p.name,
|
|
1208
|
+
description: p.description,
|
|
1209
|
+
arguments: p.arguments.map((a) => ({ name: a.name, description: a.description, required: a.required }))
|
|
1210
|
+
}))
|
|
1211
|
+
}));
|
|
1212
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1213
|
+
const { name, arguments: args } = request.params;
|
|
1214
|
+
logger.info(`Getting prompt: ${name}`, args);
|
|
1215
|
+
try {
|
|
1216
|
+
const promptDef = AGENT_PROMPTS.find((p) => p.name === name);
|
|
1217
|
+
if (!promptDef) throw new Error(`Unknown prompt: ${name}`);
|
|
1218
|
+
const promptsDir = getAgentCorePromptsDir();
|
|
1219
|
+
const promptPath = path14.join(promptsDir, promptDef.file);
|
|
1220
|
+
if (!fs15.existsSync(promptPath)) throw new Error(`Prompt file not found: ${promptDef.file}`);
|
|
1221
|
+
let promptContent = fs15.readFileSync(promptPath, "utf-8");
|
|
1222
|
+
if (args) {
|
|
1223
|
+
for (const [key, value] of Object.entries(args)) {
|
|
1224
|
+
promptContent = promptContent.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), String(value));
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
description: promptDef.description,
|
|
1229
|
+
messages: [{ role: "user", content: { type: "text", text: promptContent } }]
|
|
1230
|
+
};
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
logger.error(`Failed to get prompt: ${name}`, error);
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
function stopMCPServer() {
|
|
1238
|
+
if (mcpServer) {
|
|
1239
|
+
logger.info("Stopping MCP Server...");
|
|
1240
|
+
mcpServer.close();
|
|
1241
|
+
mcpServer = null;
|
|
1242
|
+
}
|
|
1243
|
+
serverState = { running: false };
|
|
1244
|
+
logger.info("RRCE MCP Hub stopped");
|
|
1245
|
+
console.error("RRCE MCP Hub stopped");
|
|
1246
|
+
}
|
|
1247
|
+
function getMCPServerStatus() {
|
|
1248
|
+
return { ...serverState };
|
|
1249
|
+
}
|
|
1250
|
+
var serverState, mcpServer;
|
|
1251
|
+
var init_server = __esm({
|
|
1252
|
+
"src/mcp/server.ts"() {
|
|
1253
|
+
"use strict";
|
|
1254
|
+
init_logger();
|
|
1255
|
+
init_config();
|
|
1256
|
+
init_resources();
|
|
1257
|
+
init_prompts2();
|
|
1258
|
+
init_prompts();
|
|
1259
|
+
serverState = { running: false };
|
|
1260
|
+
mcpServer = null;
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// src/mcp/install.ts
|
|
1265
|
+
import * as fs16 from "fs";
|
|
1266
|
+
import * as path15 from "path";
|
|
1267
|
+
import * as os from "os";
|
|
1268
|
+
function checkInstallStatus() {
|
|
1269
|
+
return {
|
|
1270
|
+
antigravity: checkConfigFile(ANTIGRAVITY_CONFIG),
|
|
1271
|
+
claude: checkConfigFile(CLAUDE_CONFIG)
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
function checkConfigFile(configPath) {
|
|
1275
|
+
if (!fs16.existsSync(configPath)) return false;
|
|
1276
|
+
try {
|
|
1277
|
+
const content = JSON.parse(fs16.readFileSync(configPath, "utf-8"));
|
|
1278
|
+
return !!content.mcpServers?.["rrce"];
|
|
1279
|
+
} catch {
|
|
1280
|
+
return false;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function installToConfig(target) {
|
|
1284
|
+
const configPath = target === "antigravity" ? ANTIGRAVITY_CONFIG : CLAUDE_CONFIG;
|
|
1285
|
+
const dir = path15.dirname(configPath);
|
|
1286
|
+
if (!fs16.existsSync(dir)) {
|
|
1287
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
1288
|
+
}
|
|
1289
|
+
let config = { mcpServers: {} };
|
|
1290
|
+
if (fs16.existsSync(configPath)) {
|
|
1291
|
+
try {
|
|
1292
|
+
config = JSON.parse(fs16.readFileSync(configPath, "utf-8"));
|
|
1293
|
+
} catch {
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
1297
|
+
config.mcpServers["rrce"] = {
|
|
1298
|
+
command: "npx",
|
|
1299
|
+
args: ["-y", "rrce-workflow", "mcp", "start"]
|
|
1300
|
+
// -y to avoid interactive prompts
|
|
1301
|
+
};
|
|
1302
|
+
try {
|
|
1303
|
+
fs16.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1304
|
+
return true;
|
|
1305
|
+
} catch {
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
var ANTIGRAVITY_CONFIG, CLAUDE_CONFIG;
|
|
1310
|
+
var init_install = __esm({
|
|
1311
|
+
"src/mcp/install.ts"() {
|
|
1312
|
+
"use strict";
|
|
1313
|
+
ANTIGRAVITY_CONFIG = path15.join(os.homedir(), ".gemini/antigravity/mcp_config.json");
|
|
1314
|
+
CLAUDE_CONFIG = path15.join(os.homedir(), ".config/claude/claude_desktop_config.json");
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
// src/mcp/index.ts
|
|
1319
|
+
var mcp_exports = {};
|
|
1320
|
+
__export(mcp_exports, {
|
|
1321
|
+
runMCP: () => runMCP
|
|
1322
|
+
});
|
|
1323
|
+
import { intro as intro2, outro as outro6, select as select4, multiselect as multiselect3, confirm as confirm4, spinner as spinner6, note as note7, cancel as cancel6, isCancel as isCancel7, text as text2 } from "@clack/prompts";
|
|
1324
|
+
import pc8 from "picocolors";
|
|
1325
|
+
async function runMCP(subcommand2) {
|
|
1326
|
+
if (subcommand2) {
|
|
1327
|
+
switch (subcommand2) {
|
|
1328
|
+
case "start":
|
|
1329
|
+
await startMCPServer();
|
|
1330
|
+
await new Promise(() => {
|
|
1331
|
+
});
|
|
1332
|
+
return;
|
|
1333
|
+
case "stop":
|
|
1334
|
+
await handleStopServer();
|
|
1335
|
+
return;
|
|
1336
|
+
case "status":
|
|
1337
|
+
await handleShowStatus();
|
|
1338
|
+
return;
|
|
1339
|
+
case "help":
|
|
1340
|
+
showHelp();
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
intro2(pc8.bgCyan(pc8.black(" RRCE MCP Hub ")));
|
|
1345
|
+
const globalPathCheck = await ensureMCPGlobalPath();
|
|
1346
|
+
if (!globalPathCheck.configured) {
|
|
1347
|
+
const configured = await handleConfigureGlobalPath();
|
|
1348
|
+
if (!configured) {
|
|
1349
|
+
outro6(pc8.yellow("MCP requires a global storage path. Setup cancelled."));
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
const status = checkInstallStatus();
|
|
1354
|
+
if (!status.antigravity && !status.claude) {
|
|
1355
|
+
const shouldInstall = await confirm4({
|
|
1356
|
+
message: "MCP server is not installed in your IDEs. Install now?",
|
|
1357
|
+
initialValue: true
|
|
1358
|
+
});
|
|
1359
|
+
if (shouldInstall && !isCancel7(shouldInstall)) {
|
|
1360
|
+
await handleInstallWizard();
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
let running = true;
|
|
1364
|
+
while (running) {
|
|
1365
|
+
const serverStatus = getMCPServerStatus();
|
|
1366
|
+
const serverLabel = serverStatus.running ? pc8.green("Running") : pc8.dim("Stopped");
|
|
1367
|
+
const action = await select4({
|
|
1368
|
+
message: "What would you like to do?",
|
|
1369
|
+
options: [
|
|
1370
|
+
{ value: "start", label: `\u25B6\uFE0F Start MCP server`, hint: `Current status: ${serverLabel}` },
|
|
1371
|
+
{ value: "configure", label: "\u2699\uFE0F Configure projects", hint: "Choose which projects to expose" },
|
|
1372
|
+
{ value: "install", label: "\u{1F4E5} Install to IDE", hint: "Add to Antigravity or Claude Desktop" },
|
|
1373
|
+
{ value: "status", label: "\u{1F4CB} View status", hint: "See details" },
|
|
1374
|
+
{ value: "help", label: "\u2753 Help", hint: "Learn about MCP Hub" },
|
|
1375
|
+
{ value: "exit", label: "\u21A9 Exit", hint: "Return to shell" }
|
|
1376
|
+
]
|
|
1377
|
+
});
|
|
1378
|
+
if (isCancel7(action)) {
|
|
1379
|
+
cancel6("MCP Hub closed.");
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
switch (action) {
|
|
1383
|
+
case "start":
|
|
1384
|
+
await handleStartServer();
|
|
1385
|
+
break;
|
|
1386
|
+
case "configure":
|
|
1387
|
+
await handleConfigure();
|
|
1388
|
+
break;
|
|
1389
|
+
case "install":
|
|
1390
|
+
await handleInstallWizard();
|
|
1391
|
+
break;
|
|
1392
|
+
case "status":
|
|
1393
|
+
await handleShowStatus();
|
|
1394
|
+
break;
|
|
1395
|
+
case "help":
|
|
1396
|
+
showHelp();
|
|
1397
|
+
break;
|
|
1398
|
+
case "exit":
|
|
1399
|
+
running = false;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
outro6(pc8.green("MCP Hub closed."));
|
|
1404
|
+
}
|
|
1405
|
+
async function handleInstallWizard() {
|
|
1406
|
+
const status = checkInstallStatus();
|
|
1407
|
+
const options = [
|
|
1408
|
+
{ value: "antigravity", label: "Antigravity IDE", hint: status.antigravity ? "Installed" : "Not installed" },
|
|
1409
|
+
{ value: "claude", label: "Claude Desktop", hint: status.claude ? "Installed" : "Not installed" }
|
|
1410
|
+
];
|
|
1411
|
+
const selected = await multiselect3({
|
|
1412
|
+
message: "Select where to install RRCE MCP Server:",
|
|
1413
|
+
options,
|
|
1414
|
+
initialValues: [
|
|
1415
|
+
...status.antigravity ? ["antigravity"] : [],
|
|
1416
|
+
...status.claude ? ["claude"] : []
|
|
1417
|
+
],
|
|
1418
|
+
required: false
|
|
1419
|
+
});
|
|
1420
|
+
if (isCancel7(selected)) return;
|
|
1421
|
+
const targets = selected;
|
|
1422
|
+
const results = [];
|
|
1423
|
+
for (const target of targets) {
|
|
1424
|
+
const success = installToConfig(target);
|
|
1425
|
+
results.push(`${target}: ${success ? pc8.green("Success") : pc8.red("Failed")}`);
|
|
1426
|
+
}
|
|
1427
|
+
if (results.length > 0) {
|
|
1428
|
+
note7(results.join("\n"), "Installation Results");
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
async function handleStartServer() {
|
|
1432
|
+
const fs17 = await import("fs");
|
|
1433
|
+
const { getLogFilePath: getLogFilePath2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
|
|
1434
|
+
const config = loadMCPConfig();
|
|
1435
|
+
const exposedCount = config.projects.filter((p) => p.expose).length;
|
|
1436
|
+
if (exposedCount === 0 && !config.defaults.includeNew) {
|
|
1437
|
+
const shouldConfig = await confirm4({
|
|
1438
|
+
message: "No projects are currently exposed. Configure now?",
|
|
1439
|
+
initialValue: true
|
|
1440
|
+
});
|
|
1441
|
+
if (shouldConfig && !isCancel7(shouldConfig)) {
|
|
1442
|
+
await handleConfigure();
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const portInput = await text2({
|
|
1446
|
+
message: "Select port for MCP Server",
|
|
1447
|
+
initialValue: config.server.port.toString(),
|
|
1448
|
+
placeholder: "3200",
|
|
1449
|
+
validate(value) {
|
|
1450
|
+
if (isNaN(Number(value))) return "Port must be a number";
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
if (isCancel7(portInput)) return;
|
|
1454
|
+
const newPort = parseInt(portInput, 10);
|
|
1455
|
+
if (newPort !== config.server.port) {
|
|
1456
|
+
config.server.port = newPort;
|
|
1457
|
+
saveMCPConfig(config);
|
|
1458
|
+
}
|
|
1459
|
+
console.clear();
|
|
1460
|
+
const logPath = getLogFilePath2();
|
|
1461
|
+
const renderHeader = () => {
|
|
1462
|
+
console.clear();
|
|
1463
|
+
console.log(pc8.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
1464
|
+
console.log(pc8.cyan("\u2551 RRCE MCP Hub Running \u2551"));
|
|
1465
|
+
console.log(pc8.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
1466
|
+
console.log(pc8.dim("Server is running in Stdio mode (JSON-RPC)."));
|
|
1467
|
+
console.log(pc8.dim(`Port: ${newPort} | PID: ${process.pid}`));
|
|
1468
|
+
console.log(pc8.dim(`Logging to: ${logPath}`));
|
|
1469
|
+
console.log(pc8.dim("---------------------------------------------"));
|
|
1470
|
+
};
|
|
1471
|
+
const renderFooter = () => {
|
|
1472
|
+
console.log(pc8.dim("---------------------------------------------"));
|
|
1473
|
+
console.log(pc8.bgBlue(pc8.white(pc8.bold(" COMMANDS "))));
|
|
1474
|
+
console.log(`${pc8.bold("q")}: Stop & Quit ${pc8.bold("g")}: Configure Projects ${pc8.bold("p")}: Change Port`);
|
|
1475
|
+
};
|
|
1476
|
+
renderHeader();
|
|
1477
|
+
renderFooter();
|
|
1478
|
+
try {
|
|
1479
|
+
await startMCPServer();
|
|
1480
|
+
let lastSize = 0;
|
|
1481
|
+
if (fs17.existsSync(logPath)) {
|
|
1482
|
+
const stats = fs17.statSync(logPath);
|
|
1483
|
+
lastSize = stats.size;
|
|
1484
|
+
}
|
|
1485
|
+
let isRunning = true;
|
|
1486
|
+
if (process.stdin.setRawMode) {
|
|
1487
|
+
process.stdin.setRawMode(true);
|
|
1488
|
+
process.stdin.resume();
|
|
1489
|
+
process.stdin.setEncoding("utf8");
|
|
1490
|
+
}
|
|
1491
|
+
return new Promise((resolve) => {
|
|
1492
|
+
const onKey = async (key) => {
|
|
1493
|
+
if (key === "" || key.toLowerCase() === "q") {
|
|
1494
|
+
cleanup();
|
|
1495
|
+
resolve();
|
|
1496
|
+
}
|
|
1497
|
+
if (key.toLowerCase() === "p" || key.toLowerCase() === "g") {
|
|
1498
|
+
cleanup();
|
|
1499
|
+
resolve();
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
process.stdin.on("data", onKey);
|
|
1503
|
+
const interval = setInterval(() => {
|
|
1504
|
+
if (!isRunning) return;
|
|
1505
|
+
if (fs17.existsSync(logPath)) {
|
|
1506
|
+
const stats = fs17.statSync(logPath);
|
|
1507
|
+
if (stats.size > lastSize) {
|
|
1508
|
+
const stream = fs17.createReadStream(logPath, {
|
|
1509
|
+
start: lastSize,
|
|
1510
|
+
end: stats.size,
|
|
1511
|
+
encoding: "utf-8"
|
|
1512
|
+
});
|
|
1513
|
+
stream.on("data", (chunk) => {
|
|
1514
|
+
process.stderr.write(chunk);
|
|
1515
|
+
});
|
|
1516
|
+
lastSize = stats.size;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}, 500);
|
|
1520
|
+
const cleanup = () => {
|
|
1521
|
+
isRunning = false;
|
|
1522
|
+
clearInterval(interval);
|
|
1523
|
+
if (process.stdin.setRawMode) {
|
|
1524
|
+
process.stdin.setRawMode(false);
|
|
1525
|
+
}
|
|
1526
|
+
process.stdin.removeListener("data", onKey);
|
|
1527
|
+
process.stdin.pause();
|
|
1528
|
+
stopMCPServer();
|
|
1529
|
+
console.log("");
|
|
1530
|
+
};
|
|
1531
|
+
});
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
if (process.stdin.setRawMode) {
|
|
1534
|
+
process.stdin.setRawMode(false);
|
|
1535
|
+
}
|
|
1536
|
+
console.error(pc8.red("\nFailed to start server:"));
|
|
1537
|
+
console.error(error);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
async function handleConfigureGlobalPath() {
|
|
1541
|
+
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
1542
|
+
const fs17 = await import("fs");
|
|
1543
|
+
const path17 = await import("path");
|
|
1544
|
+
note7(
|
|
1545
|
+
`MCP Hub requires a ${pc8.bold("global storage path")} to store its configuration
|
|
1546
|
+
and coordinate across projects.
|
|
1547
|
+
|
|
1548
|
+
Your current setup uses ${pc8.cyan("workspace")} mode, which stores data
|
|
1549
|
+
locally in each project. MCP needs a central location.`,
|
|
1550
|
+
"Global Path Required"
|
|
1551
|
+
);
|
|
1552
|
+
const resolvedPath = await resolveGlobalPath2();
|
|
1553
|
+
if (!resolvedPath) {
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
try {
|
|
1557
|
+
if (!fs17.existsSync(resolvedPath)) {
|
|
1558
|
+
fs17.mkdirSync(resolvedPath, { recursive: true });
|
|
1559
|
+
}
|
|
1560
|
+
const config = loadMCPConfig();
|
|
1561
|
+
saveMCPConfig(config);
|
|
1562
|
+
note7(
|
|
1563
|
+
`${pc8.green("\u2713")} Global path configured: ${pc8.cyan(resolvedPath)}
|
|
1564
|
+
|
|
1565
|
+
MCP config will be stored at:
|
|
1566
|
+
${path17.join(resolvedPath, "mcp.yaml")}`,
|
|
1567
|
+
"Configuration Saved"
|
|
1568
|
+
);
|
|
1569
|
+
return true;
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
note7(
|
|
1572
|
+
`${pc8.red("\u2717")} Failed to create directory: ${error instanceof Error ? error.message : String(error)}`,
|
|
1573
|
+
"Error"
|
|
1574
|
+
);
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
async function handleShowStatus() {
|
|
1579
|
+
const s = spinner6();
|
|
1580
|
+
s.start("Loading projects...");
|
|
1581
|
+
const config = loadMCPConfig();
|
|
1582
|
+
const projects = scanForProjects();
|
|
1583
|
+
s.stop("Projects loaded");
|
|
1584
|
+
if (projects.length === 0) {
|
|
1585
|
+
note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
const lines = [
|
|
1589
|
+
`${pc8.bold("Project Status")}`,
|
|
1590
|
+
""
|
|
1591
|
+
];
|
|
1592
|
+
for (const project of projects) {
|
|
1593
|
+
const projectConfig = config.projects.find((p) => p.name === project.name);
|
|
1594
|
+
const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
|
|
1595
|
+
const status = isExposed ? pc8.green("\u2713 exposed") : pc8.dim("\u25CB hidden");
|
|
1596
|
+
const source = pc8.dim(`(${project.source})`);
|
|
1597
|
+
lines.push(` ${status} ${project.name} ${source}`);
|
|
1598
|
+
}
|
|
1599
|
+
lines.push("");
|
|
1600
|
+
lines.push(pc8.dim(`Config: ${getMCPConfigPath()}`));
|
|
1601
|
+
const serverStatus = getMCPServerStatus();
|
|
1602
|
+
if (serverStatus.running) {
|
|
1603
|
+
lines.push(pc8.green(`Server: running on port ${serverStatus.port}`));
|
|
1604
|
+
} else {
|
|
1605
|
+
lines.push(pc8.dim("Server: not running"));
|
|
1606
|
+
}
|
|
1607
|
+
note7(lines.join("\n"), "MCP Hub Status");
|
|
1608
|
+
}
|
|
1609
|
+
async function handleConfigure() {
|
|
1610
|
+
const { logger: logger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
|
|
1611
|
+
const s = spinner6();
|
|
1612
|
+
s.start("Scanning for projects...");
|
|
1613
|
+
const config = loadMCPConfig();
|
|
1614
|
+
const projects = scanForProjects();
|
|
1615
|
+
logger2.info("Configure: Loaded config", { projects: config.projects, defaultMode: config.defaults.includeNew });
|
|
1616
|
+
s.stop("Projects found");
|
|
1617
|
+
if (projects.length === 0) {
|
|
1618
|
+
note7('No RRCE projects detected. Run "rrce-workflow" in a project to set it up.', "No Projects");
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const options = projects.map((project) => {
|
|
1622
|
+
const projectConfig = config.projects.find((p) => p.name === project.name);
|
|
1623
|
+
const isExposed = projectConfig?.expose ?? config.defaults.includeNew;
|
|
1624
|
+
return {
|
|
1625
|
+
value: project.name,
|
|
1626
|
+
label: `${project.name} ${pc8.dim(`(${project.source})`)}`,
|
|
1627
|
+
hint: project.dataPath
|
|
1628
|
+
};
|
|
1629
|
+
});
|
|
1630
|
+
const currentlyExposed = projects.filter((p) => {
|
|
1631
|
+
const cfg = config.projects.find((c) => c.name === p.name);
|
|
1632
|
+
return cfg?.expose ?? config.defaults.includeNew;
|
|
1633
|
+
}).map((p) => p.name);
|
|
1634
|
+
const selected = await multiselect3({
|
|
1635
|
+
message: "Select projects to expose via MCP:",
|
|
1636
|
+
options,
|
|
1637
|
+
initialValues: currentlyExposed,
|
|
1638
|
+
required: false
|
|
1639
|
+
});
|
|
1640
|
+
if (isCancel7(selected)) {
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const selectedNames = selected;
|
|
1644
|
+
logger2.info("Configure: User selected projects", selectedNames);
|
|
1645
|
+
for (const project of projects) {
|
|
1646
|
+
const shouldExpose = selectedNames.includes(project.name);
|
|
1647
|
+
setProjectConfig(config, project.name, shouldExpose);
|
|
1648
|
+
}
|
|
1649
|
+
saveMCPConfig(config);
|
|
1650
|
+
logger2.info("Configure: Config saved", config);
|
|
1651
|
+
const exposedCount = selectedNames.length;
|
|
1652
|
+
note7(
|
|
1653
|
+
`${pc8.green("\u2713")} Configuration saved!
|
|
1654
|
+
|
|
1655
|
+
Exposed projects: ${exposedCount}
|
|
1656
|
+
Hidden projects: ${projects.length - exposedCount}`,
|
|
1657
|
+
"Configuration Updated"
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
async function handleStopServer() {
|
|
1661
|
+
const status = getMCPServerStatus();
|
|
1662
|
+
if (!status.running) {
|
|
1663
|
+
note7("MCP server is not running.", "Status");
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const confirmed = await confirm4({
|
|
1667
|
+
message: "Stop the MCP server?",
|
|
1668
|
+
initialValue: true
|
|
1669
|
+
});
|
|
1670
|
+
if (isCancel7(confirmed) || !confirmed) {
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
stopMCPServer();
|
|
1674
|
+
note7(pc8.green("MCP server stopped."), "Server Stopped");
|
|
1675
|
+
}
|
|
1676
|
+
function showHelp() {
|
|
1677
|
+
const help = `
|
|
1678
|
+
${pc8.bold("RRCE MCP Hub")} - Cross-project AI assistant server
|
|
280
1679
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
import * as path7 from "path";
|
|
1680
|
+
${pc8.bold("ABOUT")}
|
|
1681
|
+
MCP (Model Context Protocol) allows AI assistants like Claude to
|
|
1682
|
+
access your project knowledge in real-time. The RRCE MCP Hub
|
|
1683
|
+
provides a central server that exposes selected projects.
|
|
286
1684
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
1685
|
+
${pc8.bold("MENU OPTIONS")}
|
|
1686
|
+
${pc8.cyan("View project status")} See which projects are exposed
|
|
1687
|
+
${pc8.cyan("Configure projects")} Choose which projects to expose
|
|
1688
|
+
${pc8.cyan("Start MCP server")} Start the server for AI access
|
|
1689
|
+
${pc8.cyan("Stop MCP server")} Stop the running server
|
|
292
1690
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
prompt: z.string().optional()
|
|
299
|
-
});
|
|
300
|
-
var AutoIdentitySchema = z.object({
|
|
301
|
-
user: z.string(),
|
|
302
|
-
model: z.string()
|
|
303
|
-
});
|
|
304
|
-
var PromptFrontmatterSchema = z.object({
|
|
305
|
-
name: z.string(),
|
|
306
|
-
description: z.string(),
|
|
307
|
-
"argument-hint": z.union([z.string(), z.array(z.string())]).optional(),
|
|
308
|
-
tools: z.array(z.string()).optional(),
|
|
309
|
-
"required-args": z.array(PromptArgSchema).optional(),
|
|
310
|
-
"optional-args": z.array(PromptArgSchema).optional(),
|
|
311
|
-
"auto-identity": AutoIdentitySchema.optional()
|
|
312
|
-
});
|
|
1691
|
+
${pc8.bold("DIRECT COMMANDS")}
|
|
1692
|
+
${pc8.dim("rrce-workflow mcp start")} Start server directly
|
|
1693
|
+
${pc8.dim("rrce-workflow mcp stop")} Stop server directly
|
|
1694
|
+
${pc8.dim("rrce-workflow mcp status")} Show status directly
|
|
1695
|
+
${pc8.dim("rrce-workflow mcp help")} Show this help
|
|
313
1696
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const parsed = PromptFrontmatterSchema.safeParse(data);
|
|
322
|
-
if (!parsed.success) {
|
|
323
|
-
console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
|
|
324
|
-
return null;
|
|
1697
|
+
${pc8.bold("CLAUDE DESKTOP SETUP")}
|
|
1698
|
+
Add to ${pc8.cyan("~/.config/claude/claude_desktop_config.json")}:
|
|
1699
|
+
${pc8.dim(`{
|
|
1700
|
+
"mcpServers": {
|
|
1701
|
+
"rrce": {
|
|
1702
|
+
"command": "npx",
|
|
1703
|
+
"args": ["rrce-workflow", "mcp", "start"]
|
|
325
1704
|
}
|
|
326
|
-
return {
|
|
327
|
-
frontmatter: parsed.data,
|
|
328
|
-
content: content.trim(),
|
|
329
|
-
filePath
|
|
330
|
-
};
|
|
331
|
-
} catch (error) {
|
|
332
|
-
console.error(`Error reading prompt file ${filePath}:`, error);
|
|
333
|
-
return null;
|
|
334
1705
|
}
|
|
1706
|
+
}`)}
|
|
1707
|
+
|
|
1708
|
+
${pc8.bold("RESOURCES EXPOSED")}
|
|
1709
|
+
${pc8.cyan("rrce://projects")} List all exposed projects
|
|
1710
|
+
${pc8.cyan("rrce://projects/{name}/context")} Get project context
|
|
1711
|
+
${pc8.cyan("rrce://projects/{name}/tasks")} Get project tasks
|
|
1712
|
+
|
|
1713
|
+
${pc8.bold("PROMPTS (Agent Commands)")}
|
|
1714
|
+
${pc8.cyan("init")} Initialize project context
|
|
1715
|
+
${pc8.cyan("research")} Research requirements for a task
|
|
1716
|
+
${pc8.cyan("plan")} Create execution plan
|
|
1717
|
+
${pc8.cyan("execute")} Implement planned work
|
|
1718
|
+
${pc8.cyan("docs")} Generate documentation
|
|
1719
|
+
${pc8.cyan("sync")} Sync knowledge with codebase
|
|
1720
|
+
`;
|
|
1721
|
+
note7(help.trim(), "Help");
|
|
335
1722
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
1723
|
+
var init_mcp = __esm({
|
|
1724
|
+
"src/mcp/index.ts"() {
|
|
1725
|
+
"use strict";
|
|
1726
|
+
init_config();
|
|
1727
|
+
init_detection();
|
|
1728
|
+
init_server();
|
|
1729
|
+
init_install();
|
|
339
1730
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
// src/commands/wizard/index.ts
|
|
1734
|
+
import { intro, select as select3, spinner as spinner5, note as note6, outro as outro5, isCancel as isCancel6 } from "@clack/prompts";
|
|
1735
|
+
import pc7 from "picocolors";
|
|
1736
|
+
import * as fs11 from "fs";
|
|
1737
|
+
|
|
1738
|
+
// src/lib/git.ts
|
|
1739
|
+
import { execSync } from "child_process";
|
|
1740
|
+
function getGitUser() {
|
|
1741
|
+
try {
|
|
1742
|
+
const result = execSync("git config user.name", { encoding: "utf-8" });
|
|
1743
|
+
return result.trim() || null;
|
|
1744
|
+
} catch {
|
|
1745
|
+
return null;
|
|
347
1746
|
}
|
|
348
|
-
return prompts;
|
|
349
|
-
}
|
|
350
|
-
function getAgentCoreDir() {
|
|
351
|
-
return path3.join(__dirname, "..", "agent-core");
|
|
352
|
-
}
|
|
353
|
-
function getAgentCorePromptsDir() {
|
|
354
|
-
return path3.join(getAgentCoreDir(), "prompts");
|
|
355
1747
|
}
|
|
356
1748
|
|
|
1749
|
+
// src/commands/wizard/index.ts
|
|
1750
|
+
init_paths();
|
|
1751
|
+
init_detection();
|
|
1752
|
+
|
|
1753
|
+
// src/commands/wizard/setup-flow.ts
|
|
1754
|
+
init_paths();
|
|
1755
|
+
init_prompts();
|
|
1756
|
+
import { group, select as select2, multiselect, confirm, spinner, note as note2, outro, cancel } from "@clack/prompts";
|
|
1757
|
+
import pc3 from "picocolors";
|
|
1758
|
+
import * as fs7 from "fs";
|
|
1759
|
+
import * as path8 from "path";
|
|
1760
|
+
|
|
357
1761
|
// src/commands/wizard/utils.ts
|
|
1762
|
+
init_paths();
|
|
358
1763
|
import * as fs4 from "fs";
|
|
359
1764
|
import * as path4 from "path";
|
|
360
1765
|
function copyPromptsToDir(prompts, targetDir, extension) {
|
|
@@ -381,6 +1786,8 @@ function copyDirRecursive(src, dest) {
|
|
|
381
1786
|
}
|
|
382
1787
|
|
|
383
1788
|
// src/commands/wizard/vscode.ts
|
|
1789
|
+
init_paths();
|
|
1790
|
+
init_detection();
|
|
384
1791
|
import * as fs5 from "fs";
|
|
385
1792
|
import * as path5 from "path";
|
|
386
1793
|
function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
|
|
@@ -463,119 +1870,14 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
|
|
|
463
1870
|
fs5.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
|
|
464
1871
|
}
|
|
465
1872
|
|
|
466
|
-
// src/lib/autocomplete-prompt.ts
|
|
467
|
-
import * as fs6 from "fs";
|
|
468
|
-
import * as path6 from "path";
|
|
469
|
-
import * as readline from "readline";
|
|
470
|
-
import pc from "picocolors";
|
|
471
|
-
function directoryPrompt(opts) {
|
|
472
|
-
return new Promise((resolve) => {
|
|
473
|
-
process.stdout.write(`${pc.cyan("\u25C6")} ${opts.message}
|
|
474
|
-
`);
|
|
475
|
-
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
476
|
-
const rl = readline.createInterface({
|
|
477
|
-
input: process.stdin,
|
|
478
|
-
output: process.stdout,
|
|
479
|
-
completer: completeDirectory,
|
|
480
|
-
terminal: true
|
|
481
|
-
});
|
|
482
|
-
if (opts.defaultValue) {
|
|
483
|
-
rl.write(opts.defaultValue);
|
|
484
|
-
}
|
|
485
|
-
rl.on("line", (input) => {
|
|
486
|
-
const value = input.trim();
|
|
487
|
-
const expandedPath = value.startsWith("~") ? value.replace(/^~/, process.env.HOME || "") : value;
|
|
488
|
-
if (opts.validate) {
|
|
489
|
-
const error = opts.validate(expandedPath);
|
|
490
|
-
if (error) {
|
|
491
|
-
process.stdout.write(`${pc.yellow("\u2502")} ${pc.yellow(error)}
|
|
492
|
-
`);
|
|
493
|
-
process.stdout.write(`${pc.cyan("\u2502")} `);
|
|
494
|
-
rl.write(value);
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
rl.close();
|
|
499
|
-
process.stdout.write(`${pc.green("\u2713")} ${pc.dim(expandedPath)}
|
|
500
|
-
`);
|
|
501
|
-
resolve(expandedPath);
|
|
502
|
-
});
|
|
503
|
-
rl.on("close", () => {
|
|
504
|
-
});
|
|
505
|
-
rl.on("SIGINT", () => {
|
|
506
|
-
rl.close();
|
|
507
|
-
process.stdout.write("\n");
|
|
508
|
-
resolve(/* @__PURE__ */ Symbol("cancel"));
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
function completeDirectory(line) {
|
|
513
|
-
const expanded = line.startsWith("~") ? line.replace(/^~/, process.env.HOME || "") : line;
|
|
514
|
-
try {
|
|
515
|
-
let dirToScan;
|
|
516
|
-
let prefix;
|
|
517
|
-
let basePath;
|
|
518
|
-
if (expanded === "" || expanded === "/") {
|
|
519
|
-
dirToScan = expanded || "/";
|
|
520
|
-
prefix = "";
|
|
521
|
-
basePath = expanded;
|
|
522
|
-
} else if (expanded.endsWith("/")) {
|
|
523
|
-
dirToScan = expanded;
|
|
524
|
-
prefix = "";
|
|
525
|
-
basePath = expanded;
|
|
526
|
-
} else {
|
|
527
|
-
dirToScan = path6.dirname(expanded);
|
|
528
|
-
prefix = path6.basename(expanded).toLowerCase();
|
|
529
|
-
basePath = dirToScan === "/" ? "/" : dirToScan + "/";
|
|
530
|
-
}
|
|
531
|
-
if (!fs6.existsSync(dirToScan)) {
|
|
532
|
-
return [[], line];
|
|
533
|
-
}
|
|
534
|
-
const entries = fs6.readdirSync(dirToScan, { withFileTypes: true }).filter((entry) => {
|
|
535
|
-
if (!entry.isDirectory()) return false;
|
|
536
|
-
if (entry.name.startsWith(".") && !prefix.startsWith(".")) return false;
|
|
537
|
-
return prefix === "" || entry.name.toLowerCase().startsWith(prefix);
|
|
538
|
-
}).map((entry) => {
|
|
539
|
-
const fullPath = path6.join(dirToScan, entry.name);
|
|
540
|
-
const displayPath = fullPath.startsWith(process.env.HOME || "") ? fullPath.replace(process.env.HOME || "", "~") : fullPath;
|
|
541
|
-
return displayPath + "/";
|
|
542
|
-
}).sort();
|
|
543
|
-
if (entries.length === 1) {
|
|
544
|
-
return [entries, line];
|
|
545
|
-
}
|
|
546
|
-
if (entries.length > 1) {
|
|
547
|
-
const commonPrefix = getCommonPrefix(entries);
|
|
548
|
-
if (commonPrefix.length > line.length) {
|
|
549
|
-
return [[commonPrefix], line];
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return [entries, line];
|
|
553
|
-
} catch {
|
|
554
|
-
return [[], line];
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
function getCommonPrefix(strings) {
|
|
558
|
-
if (strings.length === 0) return "";
|
|
559
|
-
if (strings.length === 1) return strings[0] || "";
|
|
560
|
-
let prefix = strings[0] || "";
|
|
561
|
-
for (let i = 1; i < strings.length; i++) {
|
|
562
|
-
const str = strings[i] || "";
|
|
563
|
-
while (prefix.length > 0 && !str.startsWith(prefix)) {
|
|
564
|
-
prefix = prefix.slice(0, -1);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return prefix;
|
|
568
|
-
}
|
|
569
|
-
function isCancelled(value) {
|
|
570
|
-
return typeof value === "symbol";
|
|
571
|
-
}
|
|
572
|
-
|
|
573
1873
|
// src/commands/wizard/setup-flow.ts
|
|
1874
|
+
init_detection();
|
|
1875
|
+
init_tui_utils();
|
|
574
1876
|
async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
575
1877
|
const s = spinner();
|
|
576
1878
|
const config = await group(
|
|
577
1879
|
{
|
|
578
|
-
storageMode: () =>
|
|
1880
|
+
storageMode: () => select2({
|
|
579
1881
|
message: "Where should workflow data be stored?",
|
|
580
1882
|
options: [
|
|
581
1883
|
{ value: "global", label: "Global (~/.rrce-workflow/)", hint: "Cross-project access, clean workspace" },
|
|
@@ -600,8 +1902,8 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
600
1902
|
options: existingProjects.map((project) => ({
|
|
601
1903
|
value: `${project.name}:${project.source}`,
|
|
602
1904
|
// Unique key
|
|
603
|
-
label: `${project.name} ${
|
|
604
|
-
hint:
|
|
1905
|
+
label: `${project.name} ${pc3.dim(`(${project.source})`)}`,
|
|
1906
|
+
hint: pc3.dim(
|
|
605
1907
|
project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
|
|
606
1908
|
)
|
|
607
1909
|
})),
|
|
@@ -656,7 +1958,7 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
656
1958
|
`Storage: ${config.storageMode}`
|
|
657
1959
|
];
|
|
658
1960
|
if (customGlobalPath && customGlobalPath !== getDefaultRRCEHome()) {
|
|
659
|
-
summary.push(`Global path: ${
|
|
1961
|
+
summary.push(`Global path: ${pc3.cyan(customGlobalPath)}`);
|
|
660
1962
|
}
|
|
661
1963
|
if (dataPaths.length > 0) {
|
|
662
1964
|
summary.push(`Data paths:`);
|
|
@@ -669,13 +1971,13 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
669
1971
|
const linkedProjects = config.linkedProjects;
|
|
670
1972
|
if (linkedProjects.length > 0) {
|
|
671
1973
|
summary.push(`Linked projects: ${linkedProjects.join(", ")}`);
|
|
672
|
-
summary.push(`Workspace file: ${
|
|
1974
|
+
summary.push(`Workspace file: ${pc3.cyan(`${workspaceName}.code-workspace`)}`);
|
|
673
1975
|
}
|
|
674
|
-
|
|
1976
|
+
note2(summary.join("\n"), "Setup Summary");
|
|
675
1977
|
if (linkedProjects.length > 0) {
|
|
676
|
-
outro(
|
|
1978
|
+
outro(pc3.green(`\u2713 Setup complete! Open ${pc3.bold(`${workspaceName}.code-workspace`)} in VSCode to access linked knowledge.`));
|
|
677
1979
|
} else {
|
|
678
|
-
outro(
|
|
1980
|
+
outro(pc3.green(`\u2713 Setup complete! Your agents are ready to use.`));
|
|
679
1981
|
}
|
|
680
1982
|
} catch (error) {
|
|
681
1983
|
s.stop("Error occurred");
|
|
@@ -683,77 +1985,18 @@ async function runSetupFlow(workspacePath, workspaceName, existingProjects) {
|
|
|
683
1985
|
process.exit(1);
|
|
684
1986
|
}
|
|
685
1987
|
}
|
|
686
|
-
async function resolveGlobalPath() {
|
|
687
|
-
const defaultPath = getDefaultRRCEHome();
|
|
688
|
-
const isDefaultWritable = checkWriteAccess(defaultPath);
|
|
689
|
-
const options = [];
|
|
690
|
-
options.push({
|
|
691
|
-
value: "default",
|
|
692
|
-
label: `Default (${defaultPath})`,
|
|
693
|
-
hint: isDefaultWritable ? pc2.green("\u2713 writable") : pc2.red("\u2717 not writable")
|
|
694
|
-
});
|
|
695
|
-
options.push({
|
|
696
|
-
value: "custom",
|
|
697
|
-
label: "Custom path",
|
|
698
|
-
hint: "Specify your own directory"
|
|
699
|
-
});
|
|
700
|
-
const choice = await select({
|
|
701
|
-
message: "Global storage location:",
|
|
702
|
-
options,
|
|
703
|
-
initialValue: isDefaultWritable ? "default" : "custom"
|
|
704
|
-
});
|
|
705
|
-
if (isCancel(choice)) {
|
|
706
|
-
return void 0;
|
|
707
|
-
}
|
|
708
|
-
if (choice === "default") {
|
|
709
|
-
if (!isDefaultWritable) {
|
|
710
|
-
note(
|
|
711
|
-
`${pc2.yellow("\u26A0")} Cannot write to default path:
|
|
712
|
-
${pc2.dim(defaultPath)}
|
|
713
|
-
|
|
714
|
-
This can happen when running via npx/bunx in restricted environments.
|
|
715
|
-
Please choose a custom path instead.`,
|
|
716
|
-
"Write Access Issue"
|
|
717
|
-
);
|
|
718
|
-
return resolveGlobalPath();
|
|
719
|
-
}
|
|
720
|
-
return defaultPath;
|
|
721
|
-
}
|
|
722
|
-
const suggestedPath = path7.join(process.env.HOME || "~", ".local", "share", "rrce-workflow");
|
|
723
|
-
const customPath = await directoryPrompt({
|
|
724
|
-
message: "Enter custom global path (Tab to autocomplete):",
|
|
725
|
-
defaultValue: suggestedPath,
|
|
726
|
-
validate: (value) => {
|
|
727
|
-
if (!value.trim()) {
|
|
728
|
-
return "Path cannot be empty";
|
|
729
|
-
}
|
|
730
|
-
if (!checkWriteAccess(value)) {
|
|
731
|
-
return `Cannot write to ${value}. Please choose a writable path.`;
|
|
732
|
-
}
|
|
733
|
-
return void 0;
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
if (isCancelled(customPath)) {
|
|
737
|
-
return void 0;
|
|
738
|
-
}
|
|
739
|
-
let expandedPath = customPath;
|
|
740
|
-
if (!expandedPath.endsWith(".rrce-workflow")) {
|
|
741
|
-
expandedPath = path7.join(expandedPath, ".rrce-workflow");
|
|
742
|
-
}
|
|
743
|
-
return expandedPath;
|
|
744
|
-
}
|
|
745
1988
|
async function generateConfiguration(config, workspacePath, workspaceName, allProjects = []) {
|
|
746
1989
|
const dataPaths = getDataPaths(config.storageMode, workspaceName, workspacePath, config.globalPath);
|
|
747
1990
|
for (const dataPath of dataPaths) {
|
|
748
1991
|
ensureDir(dataPath);
|
|
749
|
-
ensureDir(
|
|
750
|
-
ensureDir(
|
|
751
|
-
ensureDir(
|
|
752
|
-
ensureDir(
|
|
1992
|
+
ensureDir(path8.join(dataPath, "knowledge"));
|
|
1993
|
+
ensureDir(path8.join(dataPath, "refs"));
|
|
1994
|
+
ensureDir(path8.join(dataPath, "tasks"));
|
|
1995
|
+
ensureDir(path8.join(dataPath, "templates"));
|
|
753
1996
|
}
|
|
754
1997
|
const agentCoreDir = getAgentCoreDir();
|
|
755
1998
|
syncMetadataToAll(agentCoreDir, dataPaths);
|
|
756
|
-
copyDirToAllStoragePaths(
|
|
1999
|
+
copyDirToAllStoragePaths(path8.join(agentCoreDir, "templates"), "templates", dataPaths);
|
|
757
2000
|
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
758
2001
|
if (config.tools.includes("copilot")) {
|
|
759
2002
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
@@ -765,8 +2008,8 @@ async function generateConfiguration(config, workspacePath, workspaceName, allPr
|
|
|
765
2008
|
ensureDir(antigravityPath);
|
|
766
2009
|
copyPromptsToDir(prompts, antigravityPath, ".md");
|
|
767
2010
|
}
|
|
768
|
-
const workspaceConfigPath =
|
|
769
|
-
ensureDir(
|
|
2011
|
+
const workspaceConfigPath = path8.join(workspacePath, ".rrce-workflow", "config.yaml");
|
|
2012
|
+
ensureDir(path8.dirname(workspaceConfigPath));
|
|
770
2013
|
let configContent = `# RRCE-Workflow Configuration
|
|
771
2014
|
version: 1
|
|
772
2015
|
|
|
@@ -806,8 +2049,8 @@ linked_projects:
|
|
|
806
2049
|
}
|
|
807
2050
|
}
|
|
808
2051
|
function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
809
|
-
const globalPath =
|
|
810
|
-
const workspacePath =
|
|
2052
|
+
const globalPath = path8.join(customGlobalPath || getDefaultRRCEHome(), "workspaces", workspaceName);
|
|
2053
|
+
const workspacePath = path8.join(workspaceRoot, ".rrce-workflow");
|
|
811
2054
|
switch (mode) {
|
|
812
2055
|
case "global":
|
|
813
2056
|
return [globalPath];
|
|
@@ -818,7 +2061,7 @@ function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
|
818
2061
|
}
|
|
819
2062
|
}
|
|
820
2063
|
function updateGitignore(workspacePath, storageMode, tools) {
|
|
821
|
-
const gitignorePath =
|
|
2064
|
+
const gitignorePath = path8.join(workspacePath, ".gitignore");
|
|
822
2065
|
const entries = [];
|
|
823
2066
|
if (storageMode === "workspace") {
|
|
824
2067
|
entries.push(".rrce-workflow/");
|
|
@@ -868,16 +2111,18 @@ function updateGitignore(workspacePath, storageMode, tools) {
|
|
|
868
2111
|
}
|
|
869
2112
|
|
|
870
2113
|
// src/commands/wizard/link-flow.ts
|
|
871
|
-
|
|
872
|
-
import
|
|
2114
|
+
init_paths();
|
|
2115
|
+
import { multiselect as multiselect2, spinner as spinner2, note as note3, outro as outro2, cancel as cancel2, isCancel as isCancel3 } from "@clack/prompts";
|
|
2116
|
+
import pc4 from "picocolors";
|
|
873
2117
|
import * as fs8 from "fs";
|
|
2118
|
+
init_detection();
|
|
874
2119
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
875
2120
|
const projects = scanForProjects({
|
|
876
2121
|
excludeWorkspace: workspaceName,
|
|
877
2122
|
workspacePath
|
|
878
2123
|
});
|
|
879
2124
|
if (projects.length === 0) {
|
|
880
|
-
outro2(
|
|
2125
|
+
outro2(pc4.yellow("No other projects found. Try setting up another project first."));
|
|
881
2126
|
return;
|
|
882
2127
|
}
|
|
883
2128
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
@@ -886,14 +2131,14 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
886
2131
|
options: projects.map((project) => ({
|
|
887
2132
|
value: `${project.name}:${project.source}`,
|
|
888
2133
|
// Unique key
|
|
889
|
-
label: `${project.name} ${
|
|
890
|
-
hint:
|
|
2134
|
+
label: `${project.name} ${pc4.dim(`(${project.source})`)}`,
|
|
2135
|
+
hint: pc4.dim(
|
|
891
2136
|
project.source === "global" ? `~/.rrce-workflow/workspaces/${project.name}` : project.dataPath
|
|
892
2137
|
)
|
|
893
2138
|
})),
|
|
894
2139
|
required: true
|
|
895
2140
|
});
|
|
896
|
-
if (
|
|
2141
|
+
if (isCancel3(linkedProjects)) {
|
|
897
2142
|
cancel2("Cancelled.");
|
|
898
2143
|
process.exit(0);
|
|
899
2144
|
}
|
|
@@ -941,43 +2186,44 @@ linked_projects:
|
|
|
941
2186
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
942
2187
|
const summary = [
|
|
943
2188
|
`Linked projects:`,
|
|
944
|
-
...selectedProjects.map((p) => ` \u2713 ${p.name} ${
|
|
2189
|
+
...selectedProjects.map((p) => ` \u2713 ${p.name} ${pc4.dim(`(${p.source})`)}`),
|
|
945
2190
|
``,
|
|
946
|
-
`Workspace file: ${
|
|
2191
|
+
`Workspace file: ${pc4.cyan(workspaceFile)}`
|
|
947
2192
|
];
|
|
948
|
-
|
|
949
|
-
outro2(
|
|
2193
|
+
note3(summary.join("\n"), "Link Summary");
|
|
2194
|
+
outro2(pc4.green(`\u2713 Projects linked! Open ${pc4.bold(workspaceFile)} in VSCode to access linked data.`));
|
|
950
2195
|
}
|
|
951
2196
|
|
|
952
2197
|
// src/commands/wizard/sync-flow.ts
|
|
953
|
-
|
|
954
|
-
import
|
|
2198
|
+
init_paths();
|
|
2199
|
+
import { confirm as confirm2, spinner as spinner3, note as note4, outro as outro3, cancel as cancel3, isCancel as isCancel4 } from "@clack/prompts";
|
|
2200
|
+
import pc5 from "picocolors";
|
|
955
2201
|
import * as fs9 from "fs";
|
|
956
|
-
import * as
|
|
2202
|
+
import * as path9 from "path";
|
|
957
2203
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
958
2204
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
959
2205
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
960
|
-
const globalPath =
|
|
2206
|
+
const globalPath = path9.join(customGlobalPath, "workspaces", workspaceName);
|
|
961
2207
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
962
2208
|
const existingDirs = subdirs.filter(
|
|
963
|
-
(dir) => fs9.existsSync(
|
|
2209
|
+
(dir) => fs9.existsSync(path9.join(localPath, dir))
|
|
964
2210
|
);
|
|
965
2211
|
if (existingDirs.length === 0) {
|
|
966
|
-
outro3(
|
|
2212
|
+
outro3(pc5.yellow("No data found in workspace storage to sync."));
|
|
967
2213
|
return;
|
|
968
2214
|
}
|
|
969
|
-
|
|
2215
|
+
note4(
|
|
970
2216
|
`The following will be copied to global storage:
|
|
971
2217
|
${existingDirs.map((d) => ` \u2022 ${d}/`).join("\n")}
|
|
972
2218
|
|
|
973
|
-
Destination: ${
|
|
2219
|
+
Destination: ${pc5.cyan(globalPath)}`,
|
|
974
2220
|
"Sync Preview"
|
|
975
2221
|
);
|
|
976
2222
|
const shouldSync = await confirm2({
|
|
977
2223
|
message: "Proceed with sync to global storage?",
|
|
978
2224
|
initialValue: true
|
|
979
2225
|
});
|
|
980
|
-
if (
|
|
2226
|
+
if (isCancel4(shouldSync) || !shouldSync) {
|
|
981
2227
|
outro3("Sync cancelled.");
|
|
982
2228
|
return;
|
|
983
2229
|
}
|
|
@@ -986,8 +2232,8 @@ Destination: ${pc4.cyan(globalPath)}`,
|
|
|
986
2232
|
try {
|
|
987
2233
|
ensureDir(globalPath);
|
|
988
2234
|
for (const dir of existingDirs) {
|
|
989
|
-
const srcDir =
|
|
990
|
-
const destDir =
|
|
2235
|
+
const srcDir = path9.join(localPath, dir);
|
|
2236
|
+
const destDir = path9.join(globalPath, dir);
|
|
991
2237
|
ensureDir(destDir);
|
|
992
2238
|
copyDirRecursive(srcDir, destDir);
|
|
993
2239
|
}
|
|
@@ -996,12 +2242,12 @@ Destination: ${pc4.cyan(globalPath)}`,
|
|
|
996
2242
|
`Synced directories:`,
|
|
997
2243
|
...existingDirs.map((d) => ` \u2713 ${d}/`),
|
|
998
2244
|
``,
|
|
999
|
-
`Global path: ${
|
|
2245
|
+
`Global path: ${pc5.cyan(globalPath)}`,
|
|
1000
2246
|
``,
|
|
1001
2247
|
`Other projects can now link this knowledge!`
|
|
1002
2248
|
];
|
|
1003
|
-
|
|
1004
|
-
outro3(
|
|
2249
|
+
note4(summary.join("\n"), "Sync Summary");
|
|
2250
|
+
outro3(pc5.green("\u2713 Workspace knowledge synced to global storage!"));
|
|
1005
2251
|
} catch (error) {
|
|
1006
2252
|
s.stop("Error occurred");
|
|
1007
2253
|
cancel3(`Failed to sync: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1010,10 +2256,12 @@ Destination: ${pc4.cyan(globalPath)}`,
|
|
|
1010
2256
|
}
|
|
1011
2257
|
|
|
1012
2258
|
// src/commands/wizard/update-flow.ts
|
|
1013
|
-
|
|
1014
|
-
|
|
2259
|
+
init_paths();
|
|
2260
|
+
init_prompts();
|
|
2261
|
+
import { confirm as confirm3, spinner as spinner4, note as note5, outro as outro4, cancel as cancel4, isCancel as isCancel5 } from "@clack/prompts";
|
|
2262
|
+
import pc6 from "picocolors";
|
|
1015
2263
|
import * as fs10 from "fs";
|
|
1016
|
-
import * as
|
|
2264
|
+
import * as path10 from "path";
|
|
1017
2265
|
async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
1018
2266
|
const s = spinner4();
|
|
1019
2267
|
s.start("Checking for updates");
|
|
@@ -1024,7 +2272,7 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
1024
2272
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
1025
2273
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
1026
2274
|
s.stop("Updates found");
|
|
1027
|
-
|
|
2275
|
+
note5(
|
|
1028
2276
|
`The following will be updated from the package:
|
|
1029
2277
|
\u2022 prompts/ (${prompts.length} agent prompts)
|
|
1030
2278
|
\u2022 templates/ (output templates)
|
|
@@ -1037,13 +2285,13 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
1037
2285
|
message: "Proceed with update?",
|
|
1038
2286
|
initialValue: true
|
|
1039
2287
|
});
|
|
1040
|
-
if (
|
|
2288
|
+
if (isCancel5(shouldUpdate) || !shouldUpdate) {
|
|
1041
2289
|
outro4("Update cancelled.");
|
|
1042
2290
|
return;
|
|
1043
2291
|
}
|
|
1044
2292
|
s.start("Updating from package");
|
|
1045
2293
|
for (const dataPath of dataPaths) {
|
|
1046
|
-
copyDirToAllStoragePaths(
|
|
2294
|
+
copyDirToAllStoragePaths(path10.join(agentCoreDir, "templates"), "templates", [dataPath]);
|
|
1047
2295
|
}
|
|
1048
2296
|
const configFilePath = getConfigPath(workspacePath);
|
|
1049
2297
|
const configContent = fs10.readFileSync(configFilePath, "utf-8");
|
|
@@ -1065,8 +2313,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
1065
2313
|
``,
|
|
1066
2314
|
`Your configuration and knowledge files were preserved.`
|
|
1067
2315
|
];
|
|
1068
|
-
|
|
1069
|
-
outro4(
|
|
2316
|
+
note5(summary.join("\n"), "Update Summary");
|
|
2317
|
+
outro4(pc6.green("\u2713 Successfully updated from package!"));
|
|
1070
2318
|
} catch (error) {
|
|
1071
2319
|
s.stop("Error occurred");
|
|
1072
2320
|
cancel4(`Failed to update: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1074,8 +2322,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
1074
2322
|
}
|
|
1075
2323
|
}
|
|
1076
2324
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
1077
|
-
const globalPath =
|
|
1078
|
-
const workspacePath =
|
|
2325
|
+
const globalPath = path10.join(customGlobalPath, "workspaces", workspaceName);
|
|
2326
|
+
const workspacePath = path10.join(workspaceRoot, ".rrce-workflow");
|
|
1079
2327
|
switch (mode) {
|
|
1080
2328
|
case "global":
|
|
1081
2329
|
return [globalPath];
|
|
@@ -1088,7 +2336,7 @@ function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot,
|
|
|
1088
2336
|
|
|
1089
2337
|
// src/commands/wizard/index.ts
|
|
1090
2338
|
async function runWizard() {
|
|
1091
|
-
intro(
|
|
2339
|
+
intro(pc7.cyan(pc7.inverse(" RRCE-Workflow Setup ")));
|
|
1092
2340
|
const s = spinner5();
|
|
1093
2341
|
s.start("Detecting environment");
|
|
1094
2342
|
const workspacePath = detectWorkspaceRoot();
|
|
@@ -1096,9 +2344,9 @@ async function runWizard() {
|
|
|
1096
2344
|
const gitUser = getGitUser();
|
|
1097
2345
|
await new Promise((r) => setTimeout(r, 800));
|
|
1098
2346
|
s.stop("Environment detected");
|
|
1099
|
-
|
|
1100
|
-
`Git User: ${
|
|
1101
|
-
Workspace: ${
|
|
2347
|
+
note6(
|
|
2348
|
+
`Git User: ${pc7.bold(gitUser || "(not found)")}
|
|
2349
|
+
Workspace: ${pc7.bold(workspaceName)}`,
|
|
1102
2350
|
"Context"
|
|
1103
2351
|
);
|
|
1104
2352
|
const detectedProjects = scanForProjects({
|
|
@@ -1136,11 +2384,11 @@ Workspace: ${pc6.bold(workspaceName)}`,
|
|
|
1136
2384
|
}
|
|
1137
2385
|
menuOptions.push({ value: "update", label: "Update from package", hint: "Get latest prompts & templates" });
|
|
1138
2386
|
menuOptions.push({ value: "exit", label: "Exit" });
|
|
1139
|
-
const action = await
|
|
2387
|
+
const action = await select3({
|
|
1140
2388
|
message: "This workspace is already configured. What would you like to do?",
|
|
1141
2389
|
options: menuOptions
|
|
1142
2390
|
});
|
|
1143
|
-
if (
|
|
2391
|
+
if (isCancel6(action) || action === "exit") {
|
|
1144
2392
|
outro5("Exited.");
|
|
1145
2393
|
process.exit(0);
|
|
1146
2394
|
}
|
|
@@ -1161,42 +2409,59 @@ Workspace: ${pc6.bold(workspaceName)}`,
|
|
|
1161
2409
|
}
|
|
1162
2410
|
|
|
1163
2411
|
// src/commands/selector.ts
|
|
1164
|
-
|
|
1165
|
-
import
|
|
1166
|
-
import
|
|
2412
|
+
init_prompts();
|
|
2413
|
+
import { intro as intro3, select as select5, note as note8, cancel as cancel7, isCancel as isCancel8, outro as outro7 } from "@clack/prompts";
|
|
2414
|
+
import pc9 from "picocolors";
|
|
2415
|
+
import * as path16 from "path";
|
|
1167
2416
|
async function runSelector() {
|
|
1168
|
-
const workspaceName =
|
|
1169
|
-
|
|
2417
|
+
const workspaceName = path16.basename(process.cwd());
|
|
2418
|
+
intro3(pc9.cyan(pc9.inverse(` RRCE-Workflow | ${workspaceName} `)));
|
|
1170
2419
|
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
1171
2420
|
if (prompts.length === 0) {
|
|
1172
|
-
|
|
2421
|
+
cancel7("No agents found. Run `rrce-workflow` to set up.");
|
|
1173
2422
|
process.exit(0);
|
|
1174
2423
|
}
|
|
1175
|
-
const selection = await
|
|
2424
|
+
const selection = await select5({
|
|
1176
2425
|
message: "Select an agent:",
|
|
1177
|
-
options:
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
2426
|
+
options: [
|
|
2427
|
+
{
|
|
2428
|
+
value: "mcp",
|
|
2429
|
+
label: "Manage MCP Hub",
|
|
2430
|
+
hint: "Configure & Start MCP Server"
|
|
2431
|
+
},
|
|
2432
|
+
...prompts.map((p) => ({
|
|
2433
|
+
value: p,
|
|
2434
|
+
label: p.frontmatter.name,
|
|
2435
|
+
hint: p.frontmatter.description
|
|
2436
|
+
}))
|
|
2437
|
+
]
|
|
1182
2438
|
});
|
|
1183
|
-
if (
|
|
1184
|
-
|
|
2439
|
+
if (isCancel8(selection)) {
|
|
2440
|
+
cancel7("Selection cancelled.");
|
|
1185
2441
|
process.exit(0);
|
|
1186
2442
|
}
|
|
2443
|
+
if (selection === "mcp") {
|
|
2444
|
+
const { runMCP: runMCP2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
|
|
2445
|
+
await runMCP2();
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
1187
2448
|
const prompt = selection;
|
|
1188
|
-
|
|
2449
|
+
note8(
|
|
1189
2450
|
`Use this agent in your IDE by invoking:
|
|
1190
|
-
${
|
|
2451
|
+
${pc9.bold(pc9.cyan(`@${prompt.frontmatter.name}`))}`,
|
|
1191
2452
|
"Agent Selected"
|
|
1192
2453
|
);
|
|
1193
|
-
|
|
2454
|
+
outro7("Done");
|
|
1194
2455
|
}
|
|
1195
2456
|
|
|
1196
2457
|
// src/index.ts
|
|
2458
|
+
init_mcp();
|
|
1197
2459
|
var command = process.argv[2];
|
|
2460
|
+
var subcommand = process.argv[3];
|
|
1198
2461
|
if (!command || command === "wizard") {
|
|
1199
2462
|
runWizard();
|
|
2463
|
+
} else if (command === "mcp") {
|
|
2464
|
+
runMCP(subcommand);
|
|
1200
2465
|
} else {
|
|
1201
2466
|
runSelector();
|
|
1202
2467
|
}
|