rrce-workflow 0.3.22 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1028,19 +1028,60 @@ var init_install = __esm({
1028
1028
  }
1029
1029
  });
1030
1030
 
1031
- // src/commands/wizard/utils.ts
1031
+ // src/commands/wizard/fs-utils.ts
1032
1032
  import * as fs7 from "fs";
1033
1033
  import * as path8 from "path";
1034
+ function copyDirRecursive(src, dest) {
1035
+ const entries = fs7.readdirSync(src, { withFileTypes: true });
1036
+ for (const entry of entries) {
1037
+ const srcPath = path8.join(src, entry.name);
1038
+ const destPath = path8.join(dest, entry.name);
1039
+ if (entry.isDirectory()) {
1040
+ ensureDir(destPath);
1041
+ copyDirRecursive(srcPath, destPath);
1042
+ } else {
1043
+ fs7.copyFileSync(srcPath, destPath);
1044
+ }
1045
+ }
1046
+ }
1047
+ function clearDirectory(dirPath) {
1048
+ if (!fs7.existsSync(dirPath)) return;
1049
+ const entries = fs7.readdirSync(dirPath, { withFileTypes: true });
1050
+ for (const entry of entries) {
1051
+ if (entry.isFile()) {
1052
+ fs7.unlinkSync(path8.join(dirPath, entry.name));
1053
+ }
1054
+ }
1055
+ }
1056
+ function copyPromptsToDir(prompts, targetDir, extension) {
1057
+ for (const prompt of prompts) {
1058
+ const baseName = path8.basename(prompt.filePath, ".md");
1059
+ const targetName = baseName + extension;
1060
+ const targetPath = path8.join(targetDir, targetName);
1061
+ const content = fs7.readFileSync(prompt.filePath, "utf-8");
1062
+ fs7.writeFileSync(targetPath, content);
1063
+ }
1064
+ }
1065
+ var init_fs_utils = __esm({
1066
+ "src/commands/wizard/fs-utils.ts"() {
1067
+ "use strict";
1068
+ init_paths();
1069
+ }
1070
+ });
1071
+
1072
+ // src/commands/wizard/opencode-service.ts
1073
+ import * as fs8 from "fs";
1074
+ import * as path9 from "path";
1034
1075
  import * as os2 from "os";
1035
1076
  import { stringify } from "yaml";
1036
1077
  function getOpenCodeConfigPath() {
1037
- return path8.join(os2.homedir(), ".config", "opencode", "opencode.json");
1078
+ return path9.join(os2.homedir(), ".config", "opencode", "opencode.json");
1038
1079
  }
1039
1080
  function getOpenCodeCommandDir(mode, dataPath) {
1040
1081
  if (mode === "global") {
1041
- return path8.join(os2.homedir(), ".config", "opencode", "command");
1082
+ return path9.join(os2.homedir(), ".config", "opencode", "command");
1042
1083
  }
1043
- return path8.join(dataPath, ".opencode", "command");
1084
+ return path9.join(dataPath, ".opencode", "command");
1044
1085
  }
1045
1086
  function mapPromptToCommandName(baseName) {
1046
1087
  const mapping = {
@@ -1054,9 +1095,9 @@ function mapPromptToCommandName(baseName) {
1054
1095
  return mapping[baseName] || baseName;
1055
1096
  }
1056
1097
  function loadBaseProtocol() {
1057
- const basePath = path8.join(getAgentCorePromptsDir(), "_base.md");
1058
- if (fs7.existsSync(basePath)) {
1059
- return fs7.readFileSync(basePath, "utf-8");
1098
+ const basePath = path9.join(getAgentCorePromptsDir(), "_base.md");
1099
+ if (fs8.existsSync(basePath)) {
1100
+ return fs8.readFileSync(basePath, "utf-8");
1060
1101
  }
1061
1102
  return "";
1062
1103
  }
@@ -1073,18 +1114,18 @@ function buildCommandFrontmatter(prompt, baseName) {
1073
1114
  function generateOpenCodeCommands(prompts, mode, dataPath) {
1074
1115
  const commandDir = getOpenCodeCommandDir(mode, dataPath);
1075
1116
  ensureDir(commandDir);
1076
- if (fs7.existsSync(commandDir)) {
1077
- const entries = fs7.readdirSync(commandDir, { withFileTypes: true });
1117
+ if (fs8.existsSync(commandDir)) {
1118
+ const entries = fs8.readdirSync(commandDir, { withFileTypes: true });
1078
1119
  for (const entry of entries) {
1079
1120
  const fileName = entry.name;
1080
1121
  if (entry.isFile() && fileName.startsWith("rrce_") && fileName.endsWith(".md")) {
1081
- fs7.unlinkSync(path8.join(commandDir, fileName));
1122
+ fs8.unlinkSync(path9.join(commandDir, fileName));
1082
1123
  }
1083
1124
  }
1084
1125
  }
1085
1126
  const baseProtocol = loadBaseProtocol();
1086
1127
  for (const prompt of prompts) {
1087
- const baseName = path8.basename(prompt.filePath, ".md");
1128
+ const baseName = path9.basename(prompt.filePath, ".md");
1088
1129
  if (baseName === "orchestrator") continue;
1089
1130
  if (baseName.startsWith("_")) continue;
1090
1131
  const commandName = mapPromptToCommandName(baseName);
@@ -1095,52 +1136,16 @@ ${prompt.content}` : prompt.content;
1095
1136
  const content = `---
1096
1137
  ${stringify(frontmatter)}---
1097
1138
  ${fullContent}`;
1098
- fs7.writeFileSync(path8.join(commandDir, commandFile), content);
1099
- }
1100
- }
1101
- function enableProviderCaching() {
1102
- const opencodePath = getOpenCodeConfigPath();
1103
- let config = {};
1104
- if (fs7.existsSync(opencodePath)) {
1105
- try {
1106
- config = JSON.parse(fs7.readFileSync(opencodePath, "utf8"));
1107
- } catch (e) {
1108
- console.error("Warning: Could not parse existing OpenCode config, creating new provider section");
1109
- }
1110
- } else {
1111
- ensureDir(path8.dirname(opencodePath));
1112
- }
1113
- if (!config.provider) {
1114
- config.provider = {};
1115
- }
1116
- const providers = ["anthropic", "openai", "openrouter", "google"];
1117
- for (const provider of providers) {
1118
- if (!config.provider[provider]) {
1119
- config.provider[provider] = {};
1120
- }
1121
- if (!config.provider[provider].options) {
1122
- config.provider[provider].options = {};
1123
- }
1124
- config.provider[provider].options.setCacheKey = true;
1125
- }
1126
- fs7.writeFileSync(opencodePath, JSON.stringify(config, null, 2));
1127
- }
1128
- function copyPromptsToDir(prompts, targetDir, extension) {
1129
- for (const prompt of prompts) {
1130
- const baseName = path8.basename(prompt.filePath, ".md");
1131
- const targetName = baseName + extension;
1132
- const targetPath = path8.join(targetDir, targetName);
1133
- const content = fs7.readFileSync(prompt.filePath, "utf-8");
1134
- fs7.writeFileSync(targetPath, content);
1139
+ fs8.writeFileSync(path9.join(commandDir, commandFile), content);
1135
1140
  }
1136
1141
  }
1137
1142
  function updateOpenCodeConfig(newAgents) {
1138
1143
  const opencodePath = getOpenCodeConfigPath();
1139
- if (!fs7.existsSync(opencodePath)) {
1144
+ if (!fs8.existsSync(opencodePath)) {
1140
1145
  return;
1141
1146
  }
1142
1147
  try {
1143
- const config = JSON.parse(fs7.readFileSync(opencodePath, "utf8"));
1148
+ const config = JSON.parse(fs8.readFileSync(opencodePath, "utf8"));
1144
1149
  const agentConfig = config.agent ?? config.agents ?? {};
1145
1150
  const existingAgentKeys = Object.keys(agentConfig);
1146
1151
  const rrceKeys = existingAgentKeys.filter((key) => key.startsWith("rrce_"));
@@ -1156,7 +1161,7 @@ function updateOpenCodeConfig(newAgents) {
1156
1161
  }
1157
1162
  config.agent = agentConfig;
1158
1163
  if (config.agents) delete config.agents;
1159
- fs7.writeFileSync(opencodePath, JSON.stringify(config, null, 2));
1164
+ fs8.writeFileSync(opencodePath, JSON.stringify(config, null, 2));
1160
1165
  } catch (e) {
1161
1166
  console.error("Failed to update OpenCode config:", e);
1162
1167
  }
@@ -1164,7 +1169,6 @@ function updateOpenCodeConfig(newAgents) {
1164
1169
  function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath) {
1165
1170
  const { frontmatter, content } = prompt;
1166
1171
  const tools = {};
1167
- const hostTools = ["read", "write", "edit", "bash", "grep", "glob", "webfetch", "terminalLastCommand", "task"];
1168
1172
  if (frontmatter.tools) {
1169
1173
  for (const tool of frontmatter.tools) {
1170
1174
  tools[tool] = true;
@@ -1180,48 +1184,26 @@ function convertToOpenCodeAgent(prompt, useFileReference = false, promptFilePath
1180
1184
  tools
1181
1185
  };
1182
1186
  }
1183
- function copyDirRecursive(src, dest) {
1184
- const entries = fs7.readdirSync(src, { withFileTypes: true });
1185
- for (const entry of entries) {
1186
- const srcPath = path8.join(src, entry.name);
1187
- const destPath = path8.join(dest, entry.name);
1188
- if (entry.isDirectory()) {
1189
- ensureDir(destPath);
1190
- copyDirRecursive(srcPath, destPath);
1191
- } else {
1192
- fs7.copyFileSync(srcPath, destPath);
1193
- }
1194
- }
1195
- }
1196
- function clearDirectory(dirPath) {
1197
- if (!fs7.existsSync(dirPath)) return;
1198
- const entries = fs7.readdirSync(dirPath, { withFileTypes: true });
1199
- for (const entry of entries) {
1200
- if (entry.isFile()) {
1201
- fs7.unlinkSync(path8.join(dirPath, entry.name));
1202
- }
1203
- }
1204
- }
1205
1187
  function surgicalUpdateOpenCodeAgents(prompts, mode, dataPath) {
1206
1188
  const agentPrompts = prompts.filter((p) => {
1207
- const baseName = path8.basename(p.filePath, ".md");
1189
+ const baseName = path9.basename(p.filePath, ".md");
1208
1190
  return baseName === "orchestrator" || baseName === "develop";
1209
1191
  });
1210
1192
  if (mode === "global") {
1211
1193
  try {
1212
- const openCodeConfig = getOpenCodeConfigPath();
1213
- const promptsDir = path8.join(path8.dirname(openCodeConfig), "prompts");
1194
+ const openCodeConfigPath = getOpenCodeConfigPath();
1195
+ const promptsDir = path9.join(path9.dirname(openCodeConfigPath), "prompts");
1214
1196
  ensureDir(promptsDir);
1215
1197
  const baseProtocol = loadBaseProtocol();
1216
1198
  const newAgents = {};
1217
1199
  for (const prompt of agentPrompts) {
1218
- const baseName = path8.basename(prompt.filePath, ".md");
1200
+ const baseName = path9.basename(prompt.filePath, ".md");
1219
1201
  const agentId = `rrce_${baseName}`;
1220
1202
  const promptFileName = `rrce-${baseName}.md`;
1221
- const promptFilePath = path8.join(promptsDir, promptFileName);
1203
+ const promptFilePath = path9.join(promptsDir, promptFileName);
1222
1204
  const fullContent = baseProtocol ? `${baseProtocol}
1223
1205
  ${prompt.content}` : prompt.content;
1224
- fs7.writeFileSync(promptFilePath, fullContent);
1206
+ fs8.writeFileSync(promptFilePath, fullContent);
1225
1207
  const agentConfig = convertToOpenCodeAgent(prompt, true, `./prompts/${promptFileName}`);
1226
1208
  if (baseName === "develop") {
1227
1209
  agentConfig.description = "Develop planned tasks - use /rrce_develop (in-context) or @rrce_develop (isolated)";
@@ -1229,22 +1211,22 @@ ${prompt.content}` : prompt.content;
1229
1211
  newAgents[agentId] = agentConfig;
1230
1212
  }
1231
1213
  updateOpenCodeConfig(newAgents);
1232
- if (fs7.existsSync(openCodeConfig)) {
1233
- const config = JSON.parse(fs7.readFileSync(openCodeConfig, "utf8"));
1214
+ if (fs8.existsSync(openCodeConfigPath)) {
1215
+ const config = JSON.parse(fs8.readFileSync(openCodeConfigPath, "utf8"));
1234
1216
  if (!config.agent) config.agent = {};
1235
- fs7.writeFileSync(openCodeConfig, JSON.stringify(config, null, 2));
1217
+ fs8.writeFileSync(openCodeConfigPath, JSON.stringify(config, null, 2));
1236
1218
  }
1237
1219
  } catch (e) {
1238
1220
  console.error("Failed to update global OpenCode config with agents:", e);
1239
1221
  throw e;
1240
1222
  }
1241
1223
  } else {
1242
- const opencodeBaseDir = path8.join(dataPath, ".opencode", "agent");
1224
+ const opencodeBaseDir = path9.join(dataPath, ".opencode", "agent");
1243
1225
  ensureDir(opencodeBaseDir);
1244
1226
  clearDirectory(opencodeBaseDir);
1245
1227
  const baseProtocol = loadBaseProtocol();
1246
1228
  for (const prompt of agentPrompts) {
1247
- const baseName = path8.basename(prompt.filePath, ".md");
1229
+ const baseName = path9.basename(prompt.filePath, ".md");
1248
1230
  const agentId = `rrce_${baseName}`;
1249
1231
  const agentConfig = convertToOpenCodeAgent(prompt);
1250
1232
  if (baseName === "develop") {
@@ -1259,16 +1241,53 @@ ${stringify({
1259
1241
  tools: agentConfig.tools
1260
1242
  })}---
1261
1243
  ${fullContent}`;
1262
- fs7.writeFileSync(path8.join(opencodeBaseDir, `${agentId}.md`), content);
1244
+ fs8.writeFileSync(path9.join(opencodeBaseDir, `${agentId}.md`), content);
1263
1245
  }
1264
1246
  }
1265
1247
  generateOpenCodeCommands(prompts, mode, dataPath);
1266
1248
  }
1267
- var init_utils = __esm({
1268
- "src/commands/wizard/utils.ts"() {
1249
+ function enableProviderCaching() {
1250
+ const opencodePath = getOpenCodeConfigPath();
1251
+ let config = {};
1252
+ if (fs8.existsSync(opencodePath)) {
1253
+ try {
1254
+ config = JSON.parse(fs8.readFileSync(opencodePath, "utf8"));
1255
+ } catch (e) {
1256
+ console.error("Warning: Could not parse existing OpenCode config, creating new provider section");
1257
+ }
1258
+ } else {
1259
+ ensureDir(path9.dirname(opencodePath));
1260
+ }
1261
+ if (!config.provider) {
1262
+ config.provider = {};
1263
+ }
1264
+ const providers = ["anthropic", "openai", "openrouter", "google"];
1265
+ for (const provider of providers) {
1266
+ if (!config.provider[provider]) {
1267
+ config.provider[provider] = {};
1268
+ }
1269
+ if (!config.provider[provider].options) {
1270
+ config.provider[provider].options = {};
1271
+ }
1272
+ config.provider[provider].options.setCacheKey = true;
1273
+ }
1274
+ fs8.writeFileSync(opencodePath, JSON.stringify(config, null, 2));
1275
+ }
1276
+ var init_opencode_service = __esm({
1277
+ "src/commands/wizard/opencode-service.ts"() {
1269
1278
  "use strict";
1270
1279
  init_paths();
1271
1280
  init_prompts();
1281
+ init_fs_utils();
1282
+ }
1283
+ });
1284
+
1285
+ // src/commands/wizard/utils.ts
1286
+ var init_utils = __esm({
1287
+ "src/commands/wizard/utils.ts"() {
1288
+ "use strict";
1289
+ init_fs_utils();
1290
+ init_opencode_service();
1272
1291
  }
1273
1292
  });
1274
1293
 
@@ -1301,14 +1320,14 @@ var init_types = __esm({
1301
1320
  });
1302
1321
 
1303
1322
  // src/mcp/config-utils.ts
1304
- import * as path10 from "path";
1323
+ import * as path11 from "path";
1305
1324
  function normalizeProjectPath(projectPath) {
1306
1325
  let normalized = projectPath;
1307
1326
  while (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) {
1308
1327
  normalized = normalized.slice(0, -1);
1309
1328
  }
1310
1329
  if (normalized.endsWith(".rrce-workflow")) {
1311
- return path10.dirname(normalized);
1330
+ return path11.dirname(normalized);
1312
1331
  }
1313
1332
  return normalized;
1314
1333
  }
@@ -1347,8 +1366,8 @@ __export(config_exports, {
1347
1366
  saveMCPConfig: () => saveMCPConfig,
1348
1367
  setProjectConfig: () => setProjectConfig
1349
1368
  });
1350
- import * as fs9 from "fs";
1351
- import * as path11 from "path";
1369
+ import * as fs10 from "fs";
1370
+ import * as path12 from "path";
1352
1371
  import YAML from "yaml";
1353
1372
  function migrateConfig(config) {
1354
1373
  let changed = false;
@@ -1367,15 +1386,15 @@ function migrateConfig(config) {
1367
1386
  function getMCPConfigPath() {
1368
1387
  const workspaceRoot = detectWorkspaceRoot();
1369
1388
  const rrceHome = getEffectiveRRCEHome(workspaceRoot);
1370
- return path11.join(rrceHome, "mcp.yaml");
1389
+ return path12.join(rrceHome, "mcp.yaml");
1371
1390
  }
1372
1391
  function loadMCPConfig() {
1373
1392
  const configPath = getMCPConfigPath();
1374
- if (!fs9.existsSync(configPath)) {
1393
+ if (!fs10.existsSync(configPath)) {
1375
1394
  return { ...DEFAULT_MCP_CONFIG };
1376
1395
  }
1377
1396
  try {
1378
- const content = fs9.readFileSync(configPath, "utf-8");
1397
+ const content = fs10.readFileSync(configPath, "utf-8");
1379
1398
  let config = parseMCPConfig(content);
1380
1399
  config = migrateConfig(config);
1381
1400
  return config;
@@ -1387,9 +1406,9 @@ function ensureMCPGlobalPath() {
1387
1406
  const workspaceRoot = detectWorkspaceRoot();
1388
1407
  const rrceHome = getEffectiveRRCEHome(workspaceRoot);
1389
1408
  if (rrceHome.startsWith(".") || rrceHome.includes(".rrce-workflow/")) {
1390
- const configPath = path11.join(workspaceRoot, ".rrce-workflow", "config.yaml");
1391
- if (fs9.existsSync(configPath)) {
1392
- const content = fs9.readFileSync(configPath, "utf-8");
1409
+ const configPath = path12.join(workspaceRoot, ".rrce-workflow", "config.yaml");
1410
+ if (fs10.existsSync(configPath)) {
1411
+ const content = fs10.readFileSync(configPath, "utf-8");
1393
1412
  const modeMatch = content.match(/mode:\s*(global|workspace)/);
1394
1413
  if (modeMatch?.[1] === "workspace") {
1395
1414
  return {
@@ -1407,12 +1426,12 @@ function ensureMCPGlobalPath() {
1407
1426
  }
1408
1427
  function saveMCPConfig(config) {
1409
1428
  const configPath = getMCPConfigPath();
1410
- const dir = path11.dirname(configPath);
1411
- if (!fs9.existsSync(dir)) {
1412
- fs9.mkdirSync(dir, { recursive: true });
1429
+ const dir = path12.dirname(configPath);
1430
+ if (!fs10.existsSync(dir)) {
1431
+ fs10.mkdirSync(dir, { recursive: true });
1413
1432
  }
1414
1433
  const content = serializeMCPConfig(config);
1415
- fs9.writeFileSync(configPath, content);
1434
+ fs10.writeFileSync(configPath, content);
1416
1435
  }
1417
1436
  function parseMCPConfig(content) {
1418
1437
  try {
@@ -1501,16 +1520,16 @@ function getProjectPermissions(config, name, projectPath) {
1501
1520
  }
1502
1521
  function cleanStaleProjects(config) {
1503
1522
  const rrceHome = getEffectiveGlobalPath();
1504
- const globalWorkspacesDir = path11.join(rrceHome, "workspaces");
1523
+ const globalWorkspacesDir = path12.join(rrceHome, "workspaces");
1505
1524
  const validProjects = [];
1506
1525
  const removed = [];
1507
1526
  for (const project of config.projects) {
1508
1527
  let exists = false;
1509
1528
  if (project.path) {
1510
- exists = fs9.existsSync(project.path);
1529
+ exists = fs10.existsSync(project.path);
1511
1530
  } else {
1512
- const globalPath = path11.join(globalWorkspacesDir, project.name);
1513
- exists = fs9.existsSync(globalPath);
1531
+ const globalPath = path12.join(globalWorkspacesDir, project.name);
1532
+ exists = fs10.existsSync(globalPath);
1514
1533
  }
1515
1534
  if (exists) {
1516
1535
  validProjects.push(project);
@@ -1537,10 +1556,10 @@ var gitignore_exports = {};
1537
1556
  __export(gitignore_exports, {
1538
1557
  updateGitignore: () => updateGitignore
1539
1558
  });
1540
- import * as fs11 from "fs";
1541
- import * as path13 from "path";
1559
+ import * as fs12 from "fs";
1560
+ import * as path14 from "path";
1542
1561
  function updateGitignore(workspacePath, storageMode, tools) {
1543
- const gitignorePath = path13.join(workspacePath, ".gitignore");
1562
+ const gitignorePath = path14.join(workspacePath, ".gitignore");
1544
1563
  const entries = [];
1545
1564
  if (storageMode === "workspace") {
1546
1565
  entries.push(".rrce-workflow/");
@@ -1559,8 +1578,8 @@ function updateGitignore(workspacePath, storageMode, tools) {
1559
1578
  return false;
1560
1579
  }
1561
1580
  let existingContent = "";
1562
- if (fs11.existsSync(gitignorePath)) {
1563
- existingContent = fs11.readFileSync(gitignorePath, "utf-8");
1581
+ if (fs12.existsSync(gitignorePath)) {
1582
+ existingContent = fs12.readFileSync(gitignorePath, "utf-8");
1564
1583
  }
1565
1584
  const sectionMarker = "# RRCE-Workflow Generated";
1566
1585
  if (existingContent.includes(sectionMarker)) {
@@ -1571,7 +1590,7 @@ ${sectionMarker}
1571
1590
  ${entries.join("\n")}
1572
1591
  `;
1573
1592
  const updatedContent = existingContent.trimEnd() + newSection;
1574
- fs11.writeFileSync(gitignorePath, updatedContent);
1593
+ fs12.writeFileSync(gitignorePath, updatedContent);
1575
1594
  return true;
1576
1595
  }
1577
1596
  var init_gitignore = __esm({
@@ -1581,12 +1600,12 @@ var init_gitignore = __esm({
1581
1600
  });
1582
1601
 
1583
1602
  // src/mcp/logger.ts
1584
- import * as fs12 from "fs";
1585
- import * as path14 from "path";
1603
+ import * as fs13 from "fs";
1604
+ import * as path15 from "path";
1586
1605
  function getLogFilePath() {
1587
1606
  const workspaceRoot = detectWorkspaceRoot();
1588
1607
  const rrceHome = getEffectiveRRCEHome(workspaceRoot);
1589
- return path14.join(rrceHome, "mcp-server.log");
1608
+ return path15.join(rrceHome, "mcp-server.log");
1590
1609
  }
1591
1610
  var Logger, logger;
1592
1611
  var init_logger = __esm({
@@ -1617,11 +1636,11 @@ ${JSON.stringify(data, null, 2)}`;
1617
1636
  }
1618
1637
  logMessage += "\n";
1619
1638
  try {
1620
- const dir = path14.dirname(this.logPath);
1621
- if (!fs12.existsSync(dir)) {
1622
- fs12.mkdirSync(dir, { recursive: true });
1639
+ const dir = path15.dirname(this.logPath);
1640
+ if (!fs13.existsSync(dir)) {
1641
+ fs13.mkdirSync(dir, { recursive: true });
1623
1642
  }
1624
- fs12.appendFileSync(this.logPath, logMessage);
1643
+ fs13.appendFileSync(this.logPath, logMessage);
1625
1644
  } catch (e) {
1626
1645
  console.error(`[Logger Failure] Could not write to ${this.logPath}`, e);
1627
1646
  console.error(logMessage);
@@ -1737,18 +1756,18 @@ var init_constants = __esm({
1737
1756
  });
1738
1757
 
1739
1758
  // src/mcp/resources/utils.ts
1740
- import * as fs13 from "fs";
1741
- import * as path15 from "path";
1759
+ import * as fs14 from "fs";
1760
+ import * as path16 from "path";
1742
1761
  import ignore from "ignore";
1743
1762
  function estimateTokens(text2) {
1744
1763
  return Math.ceil(text2.length / 4);
1745
1764
  }
1746
1765
  function getScanContext(project, scanRoot) {
1747
- const gitignorePath = path15.join(scanRoot, ".gitignore");
1748
- const ig = fs13.existsSync(gitignorePath) ? ignore().add(fs13.readFileSync(gitignorePath, "utf-8")) : null;
1766
+ const gitignorePath = path16.join(scanRoot, ".gitignore");
1767
+ const ig = fs14.existsSync(gitignorePath) ? ignore().add(fs14.readFileSync(gitignorePath, "utf-8")) : null;
1749
1768
  const toPosixRelativePath = (absolutePath) => {
1750
- const rel = path15.relative(scanRoot, absolutePath);
1751
- return rel.split(path15.sep).join("/");
1769
+ const rel = path16.relative(scanRoot, absolutePath);
1770
+ return rel.split(path16.sep).join("/");
1752
1771
  };
1753
1772
  const isUnderGitDir = (absolutePath) => {
1754
1773
  const rel = toPosixRelativePath(absolutePath);
@@ -1760,7 +1779,7 @@ function getScanContext(project, scanRoot) {
1760
1779
  return ig.ignores(isDir ? `${rel}/` : rel);
1761
1780
  };
1762
1781
  const shouldSkipEntryDir = (absolutePath) => {
1763
- const dirName = path15.basename(absolutePath);
1782
+ const dirName = path16.basename(absolutePath);
1764
1783
  if (dirName === ".git") return true;
1765
1784
  if (SKIP_DIRS.includes(dirName)) return true;
1766
1785
  if (isIgnoredByGitignore(absolutePath, true)) return true;
@@ -1781,7 +1800,7 @@ var init_utils2 = __esm({
1781
1800
  });
1782
1801
 
1783
1802
  // src/mcp/resources/paths.ts
1784
- import * as fs14 from "fs";
1803
+ import * as fs15 from "fs";
1785
1804
  function resolveProjectPaths(project, pathInput) {
1786
1805
  const config = loadMCPConfig();
1787
1806
  let workspaceRoot = pathInput;
@@ -1809,8 +1828,8 @@ function resolveProjectPaths(project, pathInput) {
1809
1828
  mode = "global";
1810
1829
  } else {
1811
1830
  mode = "workspace";
1812
- if (fs14.existsSync(configFilePath)) {
1813
- const content = fs14.readFileSync(configFilePath, "utf-8");
1831
+ if (fs15.existsSync(configFilePath)) {
1832
+ const content = fs15.readFileSync(configFilePath, "utf-8");
1814
1833
  if (content.includes("mode: global")) mode = "global";
1815
1834
  if (content.includes("mode: workspace")) mode = "workspace";
1816
1835
  }
@@ -1838,8 +1857,8 @@ var init_paths2 = __esm({
1838
1857
  });
1839
1858
 
1840
1859
  // src/mcp/resources/projects.ts
1841
- import * as fs15 from "fs";
1842
- import * as path16 from "path";
1860
+ import * as fs16 from "fs";
1861
+ import * as path17 from "path";
1843
1862
  function getExposedProjects() {
1844
1863
  const config = loadMCPConfig();
1845
1864
  const knownProjects = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
@@ -1848,10 +1867,10 @@ function getExposedProjects() {
1848
1867
  const potentialProjects = [...allProjects];
1849
1868
  if (activeProject) {
1850
1869
  let cfgContent = null;
1851
- if (fs15.existsSync(path16.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"))) {
1852
- cfgContent = fs15.readFileSync(path16.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"), "utf-8");
1853
- } else if (fs15.existsSync(path16.join(activeProject.dataPath, ".rrce-workflow.yaml"))) {
1854
- cfgContent = fs15.readFileSync(path16.join(activeProject.dataPath, ".rrce-workflow.yaml"), "utf-8");
1870
+ if (fs16.existsSync(path17.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"))) {
1871
+ cfgContent = fs16.readFileSync(path17.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"), "utf-8");
1872
+ } else if (fs16.existsSync(path17.join(activeProject.dataPath, ".rrce-workflow.yaml"))) {
1873
+ cfgContent = fs16.readFileSync(path17.join(activeProject.dataPath, ".rrce-workflow.yaml"), "utf-8");
1855
1874
  }
1856
1875
  if (cfgContent) {
1857
1876
  if (cfgContent.includes("linked_projects:")) {
@@ -1909,15 +1928,15 @@ function getProjectContext(projectName) {
1909
1928
  if (!project.knowledgePath) {
1910
1929
  return null;
1911
1930
  }
1912
- const contextPath = path16.join(project.knowledgePath, "project-context.md");
1913
- if (!fs15.existsSync(contextPath)) {
1931
+ const contextPath = path17.join(project.knowledgePath, "project-context.md");
1932
+ if (!fs16.existsSync(contextPath)) {
1914
1933
  return null;
1915
1934
  }
1916
- return fs15.readFileSync(contextPath, "utf-8");
1935
+ return fs16.readFileSync(contextPath, "utf-8");
1917
1936
  }
1918
1937
  function getCodeIndexPath(project) {
1919
1938
  const scanRoot = project.path || project.dataPath;
1920
- return path16.join(project.knowledgePath || path16.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
1939
+ return path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
1921
1940
  }
1922
1941
  var init_projects = __esm({
1923
1942
  "src/mcp/resources/projects.ts"() {
@@ -1930,8 +1949,8 @@ var init_projects = __esm({
1930
1949
  });
1931
1950
 
1932
1951
  // src/mcp/resources/tasks.ts
1933
- import * as fs16 from "fs";
1934
- import * as path17 from "path";
1952
+ import * as fs17 from "fs";
1953
+ import * as path18 from "path";
1935
1954
  import * as os3 from "os";
1936
1955
  import * as crypto from "crypto";
1937
1956
  function getProjectTasks(projectName) {
@@ -1945,18 +1964,18 @@ function getProjectTasks(projectName) {
1945
1964
  if (!permissions.tasks) {
1946
1965
  return [];
1947
1966
  }
1948
- if (!project.tasksPath || !fs16.existsSync(project.tasksPath)) {
1967
+ if (!project.tasksPath || !fs17.existsSync(project.tasksPath)) {
1949
1968
  return [];
1950
1969
  }
1951
1970
  const tasks = [];
1952
1971
  try {
1953
- const taskDirs = fs16.readdirSync(project.tasksPath, { withFileTypes: true });
1972
+ const taskDirs = fs17.readdirSync(project.tasksPath, { withFileTypes: true });
1954
1973
  for (const dir of taskDirs) {
1955
1974
  if (!dir.isDirectory()) continue;
1956
- const metaPath = path17.join(project.tasksPath, dir.name, "meta.json");
1957
- if (fs16.existsSync(metaPath)) {
1975
+ const metaPath = path18.join(project.tasksPath, dir.name, "meta.json");
1976
+ if (fs17.existsSync(metaPath)) {
1958
1977
  try {
1959
- const meta = JSON.parse(fs16.readFileSync(metaPath, "utf-8"));
1978
+ const meta = JSON.parse(fs17.readFileSync(metaPath, "utf-8"));
1960
1979
  tasks.push(meta);
1961
1980
  } catch (err) {
1962
1981
  logger.error(`[getProjectTasks] Failed to parse meta.json in ${dir.name}`, err);
@@ -1973,10 +1992,10 @@ function getTask(projectName, taskSlug) {
1973
1992
  const projects = projectService.scan();
1974
1993
  const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
1975
1994
  if (!project || !project.tasksPath) return null;
1976
- const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
1977
- if (!fs16.existsSync(metaPath)) return null;
1995
+ const metaPath = path18.join(project.tasksPath, taskSlug, "meta.json");
1996
+ if (!fs17.existsSync(metaPath)) return null;
1978
1997
  try {
1979
- return JSON.parse(fs16.readFileSync(metaPath, "utf-8"));
1998
+ return JSON.parse(fs17.readFileSync(metaPath, "utf-8"));
1980
1999
  } catch (err) {
1981
2000
  logger.error(`[getTask] Failed to parse meta.json for task ${taskSlug}`, err);
1982
2001
  return null;
@@ -1989,26 +2008,26 @@ async function createTask(projectName, taskSlug, taskData) {
1989
2008
  if (!project || !project.tasksPath) {
1990
2009
  throw new Error(`Project '${projectName}' not found or not configured with a tasks path.`);
1991
2010
  }
1992
- const taskDir = path17.join(project.tasksPath, taskSlug);
1993
- if (fs16.existsSync(taskDir)) {
2011
+ const taskDir = path18.join(project.tasksPath, taskSlug);
2012
+ if (fs17.existsSync(taskDir)) {
1994
2013
  throw new Error(`Task with slug '${taskSlug}' already exists.`);
1995
2014
  }
1996
- fs16.mkdirSync(taskDir, { recursive: true });
1997
- fs16.mkdirSync(path17.join(taskDir, "research"), { recursive: true });
1998
- fs16.mkdirSync(path17.join(taskDir, "planning"), { recursive: true });
1999
- fs16.mkdirSync(path17.join(taskDir, "execution"), { recursive: true });
2000
- fs16.mkdirSync(path17.join(taskDir, "docs"), { recursive: true });
2001
- const rrceHome = process.env.RRCE_HOME || path17.join(os3.homedir(), ".rrce-workflow");
2002
- const templatePath = path17.join(rrceHome, "templates", "meta.template.json");
2015
+ fs17.mkdirSync(taskDir, { recursive: true });
2016
+ fs17.mkdirSync(path18.join(taskDir, "research"), { recursive: true });
2017
+ fs17.mkdirSync(path18.join(taskDir, "planning"), { recursive: true });
2018
+ fs17.mkdirSync(path18.join(taskDir, "execution"), { recursive: true });
2019
+ fs17.mkdirSync(path18.join(taskDir, "docs"), { recursive: true });
2020
+ const rrceHome = process.env.RRCE_HOME || path18.join(os3.homedir(), ".rrce-workflow");
2021
+ const templatePath = path18.join(rrceHome, "templates", "meta.template.json");
2003
2022
  let meta = {
2004
2023
  task_id: crypto.randomUUID(),
2005
2024
  task_slug: taskSlug,
2006
2025
  status: "draft",
2007
2026
  agents: {}
2008
2027
  };
2009
- if (fs16.existsSync(templatePath)) {
2028
+ if (fs17.existsSync(templatePath)) {
2010
2029
  try {
2011
- const template = JSON.parse(fs16.readFileSync(templatePath, "utf-8"));
2030
+ const template = JSON.parse(fs17.readFileSync(templatePath, "utf-8"));
2012
2031
  meta = { ...template, ...meta };
2013
2032
  } catch (e) {
2014
2033
  logger.error("Failed to load meta template", e);
@@ -2022,8 +2041,8 @@ async function createTask(projectName, taskSlug, taskData) {
2022
2041
  hash: project.name
2023
2042
  };
2024
2043
  Object.assign(meta, taskData);
2025
- const metaPath = path17.join(taskDir, "meta.json");
2026
- fs16.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
2044
+ const metaPath = path18.join(taskDir, "meta.json");
2045
+ fs17.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
2027
2046
  return meta;
2028
2047
  }
2029
2048
  async function updateTask(projectName, taskSlug, taskData) {
@@ -2042,8 +2061,8 @@ async function updateTask(projectName, taskSlug, taskData) {
2042
2061
  const projects = projectService.scan();
2043
2062
  const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
2044
2063
  if (!project || !project.tasksPath) return null;
2045
- const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
2046
- fs16.writeFileSync(metaPath, JSON.stringify(updatedMeta, null, 2));
2064
+ const metaPath = path18.join(project.tasksPath, taskSlug, "meta.json");
2065
+ fs17.writeFileSync(metaPath, JSON.stringify(updatedMeta, null, 2));
2047
2066
  return updatedMeta;
2048
2067
  }
2049
2068
  function deleteTask(projectName, taskSlug) {
@@ -2051,12 +2070,12 @@ function deleteTask(projectName, taskSlug) {
2051
2070
  const projects = projectService.scan();
2052
2071
  const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
2053
2072
  if (!project || !project.tasksPath) return false;
2054
- const taskDir = path17.join(project.tasksPath, taskSlug);
2055
- if (!fs16.existsSync(taskDir)) return false;
2056
- if (fs16.rmSync) {
2057
- fs16.rmSync(taskDir, { recursive: true, force: true });
2073
+ const taskDir = path18.join(project.tasksPath, taskSlug);
2074
+ if (!fs17.existsSync(taskDir)) return false;
2075
+ if (fs17.rmSync) {
2076
+ fs17.rmSync(taskDir, { recursive: true, force: true });
2058
2077
  } else {
2059
- fs16.rmdirSync(taskDir, { recursive: true });
2078
+ fs17.rmdirSync(taskDir, { recursive: true });
2060
2079
  }
2061
2080
  return true;
2062
2081
  }
@@ -2070,8 +2089,8 @@ var init_tasks = __esm({
2070
2089
  });
2071
2090
 
2072
2091
  // src/mcp/services/rag.ts
2073
- import * as fs17 from "fs";
2074
- import * as path18 from "path";
2092
+ import * as fs18 from "fs";
2093
+ import * as path19 from "path";
2075
2094
  var INDEX_VERSION, DEFAULT_MODEL, RAGService;
2076
2095
  var init_rag = __esm({
2077
2096
  "src/mcp/services/rag.ts"() {
@@ -2125,9 +2144,9 @@ var init_rag = __esm({
2125
2144
  */
2126
2145
  loadIndex() {
2127
2146
  if (this.index) return;
2128
- if (fs17.existsSync(this.indexPath)) {
2147
+ if (fs18.existsSync(this.indexPath)) {
2129
2148
  try {
2130
- const data = fs17.readFileSync(this.indexPath, "utf-8");
2149
+ const data = fs18.readFileSync(this.indexPath, "utf-8");
2131
2150
  this.index = JSON.parse(data);
2132
2151
  logger.info(`[RAG] Loaded index from ${this.indexPath} with ${this.index?.chunks.length} chunks.`);
2133
2152
  } catch (error) {
@@ -2153,11 +2172,11 @@ var init_rag = __esm({
2153
2172
  saveIndex() {
2154
2173
  if (!this.index) return;
2155
2174
  try {
2156
- const dir = path18.dirname(this.indexPath);
2157
- if (!fs17.existsSync(dir)) {
2158
- fs17.mkdirSync(dir, { recursive: true });
2175
+ const dir = path19.dirname(this.indexPath);
2176
+ if (!fs18.existsSync(dir)) {
2177
+ fs18.mkdirSync(dir, { recursive: true });
2159
2178
  }
2160
- fs17.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2));
2179
+ fs18.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2));
2161
2180
  logger.info(`[RAG] Saved index to ${this.indexPath} with ${this.index.chunks.length} chunks.`);
2162
2181
  } catch (error) {
2163
2182
  logger.error(`[RAG] Failed to save index to ${this.indexPath}`, error);
@@ -2168,13 +2187,13 @@ var init_rag = __esm({
2168
2187
  */
2169
2188
  saveIndexAtomic() {
2170
2189
  if (!this.index) return;
2171
- const dir = path18.dirname(this.indexPath);
2172
- if (!fs17.existsSync(dir)) {
2173
- fs17.mkdirSync(dir, { recursive: true });
2190
+ const dir = path19.dirname(this.indexPath);
2191
+ if (!fs18.existsSync(dir)) {
2192
+ fs18.mkdirSync(dir, { recursive: true });
2174
2193
  }
2175
2194
  const tmpPath = `${this.indexPath}.tmp`;
2176
- fs17.writeFileSync(tmpPath, JSON.stringify(this.index, null, 2));
2177
- fs17.renameSync(tmpPath, this.indexPath);
2195
+ fs18.writeFileSync(tmpPath, JSON.stringify(this.index, null, 2));
2196
+ fs18.renameSync(tmpPath, this.indexPath);
2178
2197
  }
2179
2198
  /**
2180
2199
  * Save index only if enough time passed since last save
@@ -2707,10 +2726,10 @@ var init_context_extractor = __esm({
2707
2726
  });
2708
2727
 
2709
2728
  // src/mcp/services/dependency-graph.ts
2710
- import * as fs18 from "fs";
2711
- import * as path19 from "path";
2729
+ import * as fs19 from "fs";
2730
+ import * as path20 from "path";
2712
2731
  function parseImports(filePath, content) {
2713
- const ext = path19.extname(filePath).toLowerCase();
2732
+ const ext = path20.extname(filePath).toLowerCase();
2714
2733
  const language = getLanguageFromExtension(ext);
2715
2734
  const edges = [];
2716
2735
  const patterns = IMPORT_PATTERNS[language] || IMPORT_PATTERNS.javascript || [];
@@ -2740,20 +2759,20 @@ function parseImports(filePath, content) {
2740
2759
  return edges;
2741
2760
  }
2742
2761
  function resolveImport(fromFile, importPath, language) {
2743
- const fromDir = path19.dirname(fromFile);
2762
+ const fromDir = path20.dirname(fromFile);
2744
2763
  if (importPath.startsWith(".")) {
2745
- const candidates = generateCandidates(path19.resolve(fromDir, importPath), language);
2764
+ const candidates = generateCandidates(path20.resolve(fromDir, importPath), language);
2746
2765
  for (const candidate of candidates) {
2747
- if (fs18.existsSync(candidate)) {
2766
+ if (fs19.existsSync(candidate)) {
2748
2767
  return { path: candidate, isResolved: true };
2749
2768
  }
2750
2769
  }
2751
- return { path: path19.resolve(fromDir, importPath), isResolved: false };
2770
+ return { path: path20.resolve(fromDir, importPath), isResolved: false };
2752
2771
  }
2753
2772
  if (importPath.startsWith("/")) {
2754
2773
  const candidates = generateCandidates(importPath, language);
2755
2774
  for (const candidate of candidates) {
2756
- if (fs18.existsSync(candidate)) {
2775
+ if (fs19.existsSync(candidate)) {
2757
2776
  return { path: candidate, isResolved: true };
2758
2777
  }
2759
2778
  }
@@ -2763,7 +2782,7 @@ function resolveImport(fromFile, importPath, language) {
2763
2782
  }
2764
2783
  function generateCandidates(basePath, language) {
2765
2784
  const candidates = [];
2766
- if (path19.extname(basePath)) {
2785
+ if (path20.extname(basePath)) {
2767
2786
  candidates.push(basePath);
2768
2787
  }
2769
2788
  switch (language) {
@@ -2878,18 +2897,18 @@ async function scanProjectDependencies(projectRoot, options = {}) {
2878
2897
  const files = [];
2879
2898
  function scanDir(dir) {
2880
2899
  try {
2881
- const entries = fs18.readdirSync(dir, { withFileTypes: true });
2900
+ const entries = fs19.readdirSync(dir, { withFileTypes: true });
2882
2901
  for (const entry of entries) {
2883
- const fullPath = path19.join(dir, entry.name);
2902
+ const fullPath = path20.join(dir, entry.name);
2884
2903
  if (entry.isDirectory()) {
2885
2904
  if (!skipDirs.includes(entry.name) && !entry.name.startsWith(".")) {
2886
2905
  scanDir(fullPath);
2887
2906
  }
2888
2907
  } else if (entry.isFile()) {
2889
- const ext = path19.extname(entry.name).toLowerCase();
2908
+ const ext = path20.extname(entry.name).toLowerCase();
2890
2909
  if (extensions.includes(ext)) {
2891
2910
  try {
2892
- const content = fs18.readFileSync(fullPath, "utf-8");
2911
+ const content = fs19.readFileSync(fullPath, "utf-8");
2893
2912
  files.push({ path: fullPath, content });
2894
2913
  } catch {
2895
2914
  }
@@ -3186,9 +3205,53 @@ var init_symbol_extractor = __esm({
3186
3205
  }
3187
3206
  });
3188
3207
 
3208
+ // src/mcp/resources/search-utils.ts
3209
+ function getSearchAdvisory(projectName) {
3210
+ const indexingInProgress = indexingJobs.isRunning(projectName);
3211
+ const advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
3212
+ return { indexingInProgress, advisoryMessage };
3213
+ }
3214
+ function getIndexFreshness(projectName) {
3215
+ const progress = indexingJobs.getProgress(projectName);
3216
+ if (progress.completedAt) {
3217
+ return {
3218
+ lastIndexedAt: new Date(progress.completedAt).toISOString(),
3219
+ indexAgeSeconds: Math.floor((Date.now() - progress.completedAt) / 1e3)
3220
+ };
3221
+ }
3222
+ return {};
3223
+ }
3224
+ function applyTokenBudget(results, maxTokens, getContent) {
3225
+ let truncated = false;
3226
+ let tokenCount = 0;
3227
+ if (maxTokens !== void 0 && maxTokens > 0) {
3228
+ const budgeted = [];
3229
+ for (const result of results) {
3230
+ const resultTokens = estimateTokens(getContent(result));
3231
+ if (tokenCount + resultTokens > maxTokens) {
3232
+ truncated = true;
3233
+ break;
3234
+ }
3235
+ budgeted.push(result);
3236
+ tokenCount += resultTokens;
3237
+ }
3238
+ return { budgetedResults: budgeted, truncated, tokenCount };
3239
+ } else {
3240
+ tokenCount = results.reduce((sum, r) => sum + estimateTokens(getContent(r)), 0);
3241
+ return { budgetedResults: results, truncated: false, tokenCount };
3242
+ }
3243
+ }
3244
+ var init_search_utils = __esm({
3245
+ "src/mcp/resources/search-utils.ts"() {
3246
+ "use strict";
3247
+ init_indexing_jobs();
3248
+ init_utils2();
3249
+ }
3250
+ });
3251
+
3189
3252
  // src/mcp/resources/search.ts
3190
- import * as fs19 from "fs";
3191
- import * as path20 from "path";
3253
+ import * as fs20 from "fs";
3254
+ import * as path21 from "path";
3192
3255
  async function searchCode(query, projectFilter, limit = 10, options) {
3193
3256
  const config = loadMCPConfig();
3194
3257
  const projects = getExposedProjects();
@@ -3197,8 +3260,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
3197
3260
  if (projectFilter && project.name !== projectFilter) continue;
3198
3261
  const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
3199
3262
  if (!permissions.knowledge || !project.knowledgePath) continue;
3200
- const indexingInProgress2 = indexingJobs.isRunning(project.name);
3201
- const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
3263
+ const { indexingInProgress, advisoryMessage } = getSearchAdvisory(project.name);
3202
3264
  const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
3203
3265
  const useRAG = projConfig?.semanticSearch?.enabled;
3204
3266
  if (!useRAG) {
@@ -3207,7 +3269,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
3207
3269
  }
3208
3270
  try {
3209
3271
  const codeIndexPath = getCodeIndexPath(project);
3210
- if (!fs19.existsSync(codeIndexPath)) {
3272
+ if (!fs20.existsSync(codeIndexPath)) {
3211
3273
  logger.debug(`[searchCode] Code index not found for project '${project.name}'`);
3212
3274
  continue;
3213
3275
  }
@@ -3217,15 +3279,13 @@ async function searchCode(query, projectFilter, limit = 10, options) {
3217
3279
  const codeChunk = r;
3218
3280
  results.push({
3219
3281
  project: project.name,
3220
- file: path20.relative(project.sourcePath || project.path || "", codeChunk.filePath),
3282
+ file: path21.relative(project.sourcePath || project.path || "", codeChunk.filePath),
3221
3283
  snippet: codeChunk.content,
3222
3284
  lineStart: codeChunk.lineStart ?? 1,
3223
3285
  lineEnd: codeChunk.lineEnd ?? 1,
3224
3286
  context: codeChunk.context,
3225
3287
  language: codeChunk.language,
3226
- score: codeChunk.score,
3227
- indexingInProgress: indexingInProgress2 || void 0,
3228
- advisoryMessage: advisoryMessage2
3288
+ score: codeChunk.score
3229
3289
  });
3230
3290
  }
3231
3291
  } catch (e) {
@@ -3237,49 +3297,32 @@ async function searchCode(query, projectFilter, limit = 10, options) {
3237
3297
  if (options?.min_score !== void 0 && options.min_score > 0) {
3238
3298
  filteredResults = results.filter((r) => r.score >= options.min_score);
3239
3299
  }
3240
- let limitedResults = filteredResults.slice(0, limit);
3241
- let truncated = false;
3242
- let tokenCount = 0;
3243
- if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
3244
- const budgetedResults = [];
3245
- for (const result of limitedResults) {
3246
- const resultTokens = estimateTokens(result.snippet + (result.context || ""));
3247
- if (tokenCount + resultTokens > options.max_tokens) {
3248
- truncated = true;
3249
- break;
3250
- }
3251
- budgetedResults.push(result);
3252
- tokenCount += resultTokens;
3253
- }
3254
- limitedResults = budgetedResults;
3255
- } else {
3256
- tokenCount = limitedResults.reduce((sum, r) => sum + estimateTokens(r.snippet + (r.context || "")), 0);
3257
- }
3300
+ const limitedResults = filteredResults.slice(0, limit);
3301
+ const { budgetedResults, truncated, tokenCount } = applyTokenBudget(
3302
+ limitedResults,
3303
+ options?.max_tokens,
3304
+ (r) => r.snippet + (r.context || "")
3305
+ );
3258
3306
  let indexAgeSeconds;
3259
3307
  let lastIndexedAt;
3260
- let indexingInProgress;
3261
- let advisoryMessage;
3308
+ let indexingInProgressGlobal;
3309
+ let advisoryMessageGlobal;
3262
3310
  if (projectFilter) {
3263
- const project = projects.find((p) => p.name === projectFilter);
3264
- if (project) {
3265
- indexingInProgress = indexingJobs.isRunning(project.name);
3266
- advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
3267
- const progress = indexingJobs.getProgress(project.name);
3268
- if (progress.completedAt) {
3269
- lastIndexedAt = new Date(progress.completedAt).toISOString();
3270
- indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
3271
- }
3272
- }
3311
+ const { indexingInProgress, advisoryMessage } = getSearchAdvisory(projectFilter);
3312
+ const freshness = getIndexFreshness(projectFilter);
3313
+ indexingInProgressGlobal = indexingInProgress;
3314
+ advisoryMessageGlobal = advisoryMessage;
3315
+ indexAgeSeconds = freshness.indexAgeSeconds;
3316
+ lastIndexedAt = freshness.lastIndexedAt;
3273
3317
  }
3274
- const cleanResults = limitedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
3275
3318
  return {
3276
- results: cleanResults,
3319
+ results: budgetedResults,
3277
3320
  token_count: tokenCount,
3278
3321
  truncated,
3279
3322
  index_age_seconds: indexAgeSeconds,
3280
3323
  last_indexed_at: lastIndexedAt,
3281
- indexingInProgress,
3282
- advisoryMessage
3324
+ indexingInProgress: indexingInProgressGlobal,
3325
+ advisoryMessage: advisoryMessageGlobal
3283
3326
  };
3284
3327
  }
3285
3328
  async function searchKnowledge(query, projectFilter, options) {
@@ -3291,24 +3334,20 @@ async function searchKnowledge(query, projectFilter, options) {
3291
3334
  if (projectFilter && project.name !== projectFilter) continue;
3292
3335
  const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
3293
3336
  if (!permissions.knowledge || !project.knowledgePath) continue;
3294
- const indexingInProgress2 = indexingJobs.isRunning(project.name);
3295
- const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
3337
+ const { indexingInProgress, advisoryMessage } = getSearchAdvisory(project.name);
3296
3338
  const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
3297
3339
  const useRAG = projConfig?.semanticSearch?.enabled;
3298
3340
  if (useRAG) {
3299
- logger.info(`[RAG] Using semantic search for project '${project.name}'`);
3300
3341
  try {
3301
- const indexPath = path20.join(project.knowledgePath, "embeddings.json");
3342
+ const indexPath = path21.join(project.knowledgePath, "embeddings.json");
3302
3343
  const rag = new RAGService(indexPath, projConfig?.semanticSearch?.model);
3303
3344
  const ragResults = await rag.search(query, 5);
3304
3345
  for (const r of ragResults) {
3305
3346
  results.push({
3306
3347
  project: project.name,
3307
- file: path20.relative(project.knowledgePath, r.filePath),
3348
+ file: path21.relative(project.knowledgePath, r.filePath),
3308
3349
  matches: [r.content],
3309
- score: r.score,
3310
- indexingInProgress: indexingInProgress2 || void 0,
3311
- advisoryMessage: advisoryMessage2
3350
+ score: r.score
3312
3351
  });
3313
3352
  }
3314
3353
  continue;
@@ -3317,11 +3356,11 @@ async function searchKnowledge(query, projectFilter, options) {
3317
3356
  }
3318
3357
  }
3319
3358
  try {
3320
- const files = fs19.readdirSync(project.knowledgePath);
3359
+ const files = fs20.readdirSync(project.knowledgePath);
3321
3360
  for (const file of files) {
3322
3361
  if (!file.endsWith(".md")) continue;
3323
- const filePath = path20.join(project.knowledgePath, file);
3324
- const content = fs19.readFileSync(filePath, "utf-8");
3362
+ const filePath = path21.join(project.knowledgePath, file);
3363
+ const content = fs20.readFileSync(filePath, "utf-8");
3325
3364
  const lines = content.split("\n");
3326
3365
  const matches = [];
3327
3366
  for (const line of lines) {
@@ -3333,10 +3372,7 @@ async function searchKnowledge(query, projectFilter, options) {
3333
3372
  results.push({
3334
3373
  project: project.name,
3335
3374
  file,
3336
- matches: matches.slice(0, 5),
3337
- // Limit to 5 matches per file
3338
- indexingInProgress: indexingInProgress2 || void 0,
3339
- advisoryMessage: advisoryMessage2
3375
+ matches: matches.slice(0, 5)
3340
3376
  });
3341
3377
  }
3342
3378
  }
@@ -3349,48 +3385,31 @@ async function searchKnowledge(query, projectFilter, options) {
3349
3385
  filteredResults = results.filter((r) => (r.score ?? 1) >= options.min_score);
3350
3386
  }
3351
3387
  filteredResults.sort((a, b) => (b.score ?? 1) - (a.score ?? 1));
3352
- let truncated = false;
3353
- let tokenCount = 0;
3354
- let budgetedResults = filteredResults;
3355
- if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
3356
- budgetedResults = [];
3357
- for (const result of filteredResults) {
3358
- const resultTokens = estimateTokens(result.matches.join("\n"));
3359
- if (tokenCount + resultTokens > options.max_tokens) {
3360
- truncated = true;
3361
- break;
3362
- }
3363
- budgetedResults.push(result);
3364
- tokenCount += resultTokens;
3365
- }
3366
- } else {
3367
- tokenCount = filteredResults.reduce((sum, r) => sum + estimateTokens(r.matches.join("\n")), 0);
3368
- }
3388
+ const { budgetedResults, truncated, tokenCount } = applyTokenBudget(
3389
+ filteredResults,
3390
+ options?.max_tokens,
3391
+ (r) => r.matches.join("\n")
3392
+ );
3369
3393
  let indexAgeSeconds;
3370
3394
  let lastIndexedAt;
3371
- let indexingInProgress;
3372
- let advisoryMessage;
3395
+ let indexingInProgressGlobal;
3396
+ let advisoryMessageGlobal;
3373
3397
  if (projectFilter) {
3374
- const project = projects.find((p) => p.name === projectFilter);
3375
- if (project) {
3376
- indexingInProgress = indexingJobs.isRunning(project.name);
3377
- advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
3378
- const progress = indexingJobs.getProgress(project.name);
3379
- if (progress.completedAt) {
3380
- lastIndexedAt = new Date(progress.completedAt).toISOString();
3381
- indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
3382
- }
3383
- }
3398
+ const { indexingInProgress, advisoryMessage } = getSearchAdvisory(projectFilter);
3399
+ const freshness = getIndexFreshness(projectFilter);
3400
+ indexingInProgressGlobal = indexingInProgress;
3401
+ advisoryMessageGlobal = advisoryMessage;
3402
+ indexAgeSeconds = freshness.indexAgeSeconds;
3403
+ lastIndexedAt = freshness.lastIndexedAt;
3384
3404
  }
3385
- const cleanResults = budgetedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
3386
3405
  return {
3387
- results: cleanResults,
3406
+ results: budgetedResults,
3388
3407
  token_count: tokenCount,
3389
3408
  truncated,
3390
3409
  index_age_seconds: indexAgeSeconds,
3391
3410
  last_indexed_at: lastIndexedAt,
3392
- indexingInProgress,
3393
- advisoryMessage
3411
+ indexingInProgress: indexingInProgressGlobal,
3412
+ advisoryMessage: advisoryMessageGlobal
3394
3413
  };
3395
3414
  }
3396
3415
  async function findRelatedFiles2(filePath, projectName, options = {}) {
@@ -3408,10 +3427,10 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
3408
3427
  }
3409
3428
  const projectRoot = project.sourcePath || project.path || "";
3410
3429
  let absoluteFilePath = filePath;
3411
- if (!path20.isAbsolute(filePath)) {
3412
- absoluteFilePath = path20.resolve(projectRoot, filePath);
3430
+ if (!path21.isAbsolute(filePath)) {
3431
+ absoluteFilePath = path21.resolve(projectRoot, filePath);
3413
3432
  }
3414
- if (!fs19.existsSync(absoluteFilePath)) {
3433
+ if (!fs20.existsSync(absoluteFilePath)) {
3415
3434
  return {
3416
3435
  success: false,
3417
3436
  file: filePath,
@@ -3428,13 +3447,13 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
3428
3447
  depth: options.depth ?? 1
3429
3448
  });
3430
3449
  const relationships = related.map((r) => ({
3431
- file: path20.relative(projectRoot, r.file),
3450
+ file: path21.relative(projectRoot, r.file),
3432
3451
  relationship: r.relationship,
3433
3452
  importPath: r.importPath
3434
3453
  }));
3435
3454
  return {
3436
3455
  success: true,
3437
- file: path20.relative(projectRoot, absoluteFilePath),
3456
+ file: path21.relative(projectRoot, absoluteFilePath),
3438
3457
  project: projectName,
3439
3458
  relationships
3440
3459
  };
@@ -3462,7 +3481,7 @@ async function searchSymbols2(name, projectName, options = {}) {
3462
3481
  };
3463
3482
  }
3464
3483
  const projectRoot = project.sourcePath || project.path || "";
3465
- if (!fs19.existsSync(projectRoot)) {
3484
+ if (!fs20.existsSync(projectRoot)) {
3466
3485
  return {
3467
3486
  success: false,
3468
3487
  project: projectName,
@@ -3473,14 +3492,14 @@ async function searchSymbols2(name, projectName, options = {}) {
3473
3492
  try {
3474
3493
  const codeFiles = [];
3475
3494
  const scanDir = (dir) => {
3476
- const entries = fs19.readdirSync(dir, { withFileTypes: true });
3495
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
3477
3496
  for (const entry of entries) {
3478
- const fullPath = path20.join(dir, entry.name);
3497
+ const fullPath = path21.join(dir, entry.name);
3479
3498
  if (entry.isDirectory()) {
3480
3499
  if (SKIP_DIRS.includes(entry.name)) continue;
3481
3500
  scanDir(fullPath);
3482
3501
  } else if (entry.isFile()) {
3483
- const ext = path20.extname(entry.name).toLowerCase();
3502
+ const ext = path21.extname(entry.name).toLowerCase();
3484
3503
  if (CODE_EXTENSIONS.includes(ext)) {
3485
3504
  codeFiles.push(fullPath);
3486
3505
  }
@@ -3491,7 +3510,7 @@ async function searchSymbols2(name, projectName, options = {}) {
3491
3510
  const symbolResults = [];
3492
3511
  for (const file of codeFiles.slice(0, 500)) {
3493
3512
  try {
3494
- const content = fs19.readFileSync(file, "utf-8");
3513
+ const content = fs20.readFileSync(file, "utf-8");
3495
3514
  const result = extractSymbols(content, file);
3496
3515
  symbolResults.push(result);
3497
3516
  } catch (e) {
@@ -3506,7 +3525,7 @@ async function searchSymbols2(name, projectName, options = {}) {
3506
3525
  const results = matches.map((m) => ({
3507
3526
  name: m.name,
3508
3527
  type: m.type,
3509
- file: path20.relative(projectRoot, m.file),
3528
+ file: path21.relative(projectRoot, m.file),
3510
3529
  line: m.line,
3511
3530
  signature: m.signature,
3512
3531
  exported: m.exported,
@@ -3539,24 +3558,24 @@ async function getFileSummary(filePath, projectName) {
3539
3558
  }
3540
3559
  const projectRoot = project.sourcePath || project.path || "";
3541
3560
  let absolutePath = filePath;
3542
- if (!path20.isAbsolute(filePath)) {
3543
- absolutePath = path20.resolve(projectRoot, filePath);
3561
+ if (!path21.isAbsolute(filePath)) {
3562
+ absolutePath = path21.resolve(projectRoot, filePath);
3544
3563
  }
3545
- if (!fs19.existsSync(absolutePath)) {
3564
+ if (!fs20.existsSync(absolutePath)) {
3546
3565
  return {
3547
3566
  success: false,
3548
3567
  message: `File not found: ${filePath}`
3549
3568
  };
3550
3569
  }
3551
3570
  try {
3552
- const stat = fs19.statSync(absolutePath);
3553
- const content = fs19.readFileSync(absolutePath, "utf-8");
3571
+ const stat = fs20.statSync(absolutePath);
3572
+ const content = fs20.readFileSync(absolutePath, "utf-8");
3554
3573
  const lines = content.split("\n");
3555
3574
  const symbolResult = extractSymbols(content, absolutePath);
3556
3575
  return {
3557
3576
  success: true,
3558
3577
  summary: {
3559
- path: path20.relative(projectRoot, absolutePath),
3578
+ path: path21.relative(projectRoot, absolutePath),
3560
3579
  language: symbolResult.language,
3561
3580
  lines: lines.length,
3562
3581
  size_bytes: stat.size,
@@ -3592,12 +3611,13 @@ var init_search = __esm({
3592
3611
  init_projects();
3593
3612
  init_utils2();
3594
3613
  init_constants();
3614
+ init_search_utils();
3595
3615
  }
3596
3616
  });
3597
3617
 
3598
3618
  // src/lib/drift-service.ts
3599
- import * as fs20 from "fs";
3600
- import * as path21 from "path";
3619
+ import * as fs21 from "fs";
3620
+ import * as path22 from "path";
3601
3621
  import * as crypto2 from "crypto";
3602
3622
  var DriftService;
3603
3623
  var init_drift_service = __esm({
@@ -3606,26 +3626,26 @@ var init_drift_service = __esm({
3606
3626
  DriftService = class {
3607
3627
  static CHECKSUM_FILENAME = ".rrce-checksums.json";
3608
3628
  static calculateHash(filePath) {
3609
- const content = fs20.readFileSync(filePath);
3629
+ const content = fs21.readFileSync(filePath);
3610
3630
  return crypto2.createHash("md5").update(content).digest("hex");
3611
3631
  }
3612
3632
  static getManifestPath(projectPath) {
3613
- return path21.join(projectPath, this.CHECKSUM_FILENAME);
3633
+ return path22.join(projectPath, this.CHECKSUM_FILENAME);
3614
3634
  }
3615
3635
  static loadManifest(projectPath) {
3616
3636
  const manifestPath = this.getManifestPath(projectPath);
3617
- if (!fs20.existsSync(manifestPath)) {
3637
+ if (!fs21.existsSync(manifestPath)) {
3618
3638
  return {};
3619
3639
  }
3620
3640
  try {
3621
- return JSON.parse(fs20.readFileSync(manifestPath, "utf8"));
3641
+ return JSON.parse(fs21.readFileSync(manifestPath, "utf8"));
3622
3642
  } catch (e) {
3623
3643
  return {};
3624
3644
  }
3625
3645
  }
3626
3646
  static saveManifest(projectPath, manifest) {
3627
3647
  const manifestPath = this.getManifestPath(projectPath);
3628
- fs20.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
3648
+ fs21.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
3629
3649
  }
3630
3650
  /**
3631
3651
  * Generates a manifest for the current state of files in the project
@@ -3633,9 +3653,9 @@ var init_drift_service = __esm({
3633
3653
  static generateManifest(projectPath, files) {
3634
3654
  const manifest = {};
3635
3655
  for (const file of files) {
3636
- const fullPath = path21.join(projectPath, file);
3637
- if (fs20.existsSync(fullPath)) {
3638
- const stats = fs20.statSync(fullPath);
3656
+ const fullPath = path22.join(projectPath, file);
3657
+ if (fs21.existsSync(fullPath)) {
3658
+ const stats = fs21.statSync(fullPath);
3639
3659
  manifest[file] = {
3640
3660
  hash: this.calculateHash(fullPath),
3641
3661
  mtime: stats.mtimeMs
@@ -3652,12 +3672,12 @@ var init_drift_service = __esm({
3652
3672
  const modified = [];
3653
3673
  const deleted = [];
3654
3674
  for (const [relPath, entry] of Object.entries(manifest)) {
3655
- const fullPath = path21.join(projectPath, relPath);
3656
- if (!fs20.existsSync(fullPath)) {
3675
+ const fullPath = path22.join(projectPath, relPath);
3676
+ if (!fs21.existsSync(fullPath)) {
3657
3677
  deleted.push(relPath);
3658
3678
  continue;
3659
3679
  }
3660
- const stats = fs20.statSync(fullPath);
3680
+ const stats = fs21.statSync(fullPath);
3661
3681
  if (stats.mtimeMs === entry.mtime) {
3662
3682
  continue;
3663
3683
  }
@@ -3675,8 +3695,8 @@ var init_drift_service = __esm({
3675
3695
  const manifest = this.loadManifest(projectPath);
3676
3696
  const deleted = [];
3677
3697
  for (const relPath of Object.keys(manifest)) {
3678
- const fullPath = path21.join(projectPath, relPath);
3679
- if (!fs20.existsSync(fullPath)) {
3698
+ const fullPath = path22.join(projectPath, relPath);
3699
+ if (!fs21.existsSync(fullPath)) {
3680
3700
  deleted.push(relPath);
3681
3701
  }
3682
3702
  }
@@ -3712,8 +3732,8 @@ var init_drift_service = __esm({
3712
3732
  });
3713
3733
 
3714
3734
  // src/mcp/resources/indexing.ts
3715
- import * as fs21 from "fs";
3716
- import * as path22 from "path";
3735
+ import * as fs22 from "fs";
3736
+ import * as path23 from "path";
3717
3737
  async function indexKnowledge(projectName, force = false, clean = false) {
3718
3738
  const config = loadMCPConfig();
3719
3739
  const projects = getExposedProjects();
@@ -3743,7 +3763,7 @@ async function indexKnowledge(projectName, force = false, clean = false) {
3743
3763
  };
3744
3764
  }
3745
3765
  const scanRoot = project.sourcePath || project.path || project.dataPath;
3746
- if (!fs21.existsSync(scanRoot)) {
3766
+ if (!fs22.existsSync(scanRoot)) {
3747
3767
  return {
3748
3768
  state: "failed",
3749
3769
  status: "failed",
@@ -3756,13 +3776,13 @@ async function indexKnowledge(projectName, force = false, clean = false) {
3756
3776
  }
3757
3777
  const runIndexing = async () => {
3758
3778
  const { shouldSkipEntryDir, shouldSkipEntryFile } = getScanContext(project, scanRoot);
3759
- const knowledgeDir = project.knowledgePath || path22.join(scanRoot, ".rrce-workflow", "knowledge");
3760
- const indexPath = path22.join(knowledgeDir, "embeddings.json");
3761
- const codeIndexPath = path22.join(knowledgeDir, "code-embeddings.json");
3779
+ const knowledgeDir = project.knowledgePath || path23.join(scanRoot, ".rrce-workflow", "knowledge");
3780
+ const indexPath = path23.join(knowledgeDir, "embeddings.json");
3781
+ const codeIndexPath = path23.join(knowledgeDir, "code-embeddings.json");
3762
3782
  if (clean) {
3763
3783
  logger.info(`[RAG] Cleaning knowledge index for ${project.name}`);
3764
- if (fs21.existsSync(indexPath)) fs21.unlinkSync(indexPath);
3765
- if (fs21.existsSync(codeIndexPath)) fs21.unlinkSync(codeIndexPath);
3784
+ if (fs22.existsSync(indexPath)) fs22.unlinkSync(indexPath);
3785
+ if (fs22.existsSync(codeIndexPath)) fs22.unlinkSync(codeIndexPath);
3766
3786
  }
3767
3787
  const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
3768
3788
  const rag = new RAGService(indexPath, model);
@@ -3773,14 +3793,14 @@ async function indexKnowledge(projectName, force = false, clean = false) {
3773
3793
  let itemsTotal = 0;
3774
3794
  let itemsDone = 0;
3775
3795
  const preCount = (dir) => {
3776
- const entries = fs21.readdirSync(dir, { withFileTypes: true });
3796
+ const entries = fs22.readdirSync(dir, { withFileTypes: true });
3777
3797
  for (const entry of entries) {
3778
- const fullPath = path22.join(dir, entry.name);
3798
+ const fullPath = path23.join(dir, entry.name);
3779
3799
  if (entry.isDirectory()) {
3780
3800
  if (shouldSkipEntryDir(fullPath)) continue;
3781
3801
  preCount(fullPath);
3782
3802
  } else if (entry.isFile()) {
3783
- const ext = path22.extname(entry.name).toLowerCase();
3803
+ const ext = path23.extname(entry.name).toLowerCase();
3784
3804
  if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
3785
3805
  if (shouldSkipEntryFile(fullPath)) continue;
3786
3806
  itemsTotal++;
@@ -3797,13 +3817,13 @@ async function indexKnowledge(projectName, force = false, clean = false) {
3797
3817
  logger.info(`[RAG] ${project.name}: Detected ${deletedFiles.length} deleted files from manifest`);
3798
3818
  }
3799
3819
  for (const filePath of unique) {
3800
- if (!path22.isAbsolute(filePath)) continue;
3801
- const relFilePath = filePath.split(path22.sep).join("/");
3802
- const relScanRoot = scanRoot.split(path22.sep).join("/");
3820
+ if (!path23.isAbsolute(filePath)) continue;
3821
+ const relFilePath = filePath.split(path23.sep).join("/");
3822
+ const relScanRoot = scanRoot.split(path23.sep).join("/");
3803
3823
  const isInScanRoot = relFilePath === relScanRoot || relFilePath.startsWith(`${relScanRoot}/`);
3804
3824
  if (!isInScanRoot) continue;
3805
3825
  const isDeleted = deletedFiles.some((df) => filePath.endsWith(df));
3806
- if (shouldSkipEntryFile(filePath) || isDeleted || !fs21.existsSync(filePath)) {
3826
+ if (shouldSkipEntryFile(filePath) || isDeleted || !fs22.existsSync(filePath)) {
3807
3827
  await rag.removeFile(filePath);
3808
3828
  await codeRag.removeFile(filePath);
3809
3829
  }
@@ -3811,21 +3831,21 @@ async function indexKnowledge(projectName, force = false, clean = false) {
3811
3831
  };
3812
3832
  await cleanupIgnoredFiles();
3813
3833
  const scanDir = async (dir) => {
3814
- const entries = fs21.readdirSync(dir, { withFileTypes: true });
3834
+ const entries = fs22.readdirSync(dir, { withFileTypes: true });
3815
3835
  for (const entry of entries) {
3816
- const fullPath = path22.join(dir, entry.name);
3836
+ const fullPath = path23.join(dir, entry.name);
3817
3837
  if (entry.isDirectory()) {
3818
3838
  if (shouldSkipEntryDir(fullPath)) continue;
3819
3839
  await scanDir(fullPath);
3820
3840
  } else if (entry.isFile()) {
3821
- const ext = path22.extname(entry.name).toLowerCase();
3841
+ const ext = path23.extname(entry.name).toLowerCase();
3822
3842
  if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
3823
3843
  if (shouldSkipEntryFile(fullPath)) continue;
3824
3844
  try {
3825
3845
  indexingJobs.update(project.name, { currentItem: fullPath, itemsDone });
3826
- const stat = fs21.statSync(fullPath);
3846
+ const stat = fs22.statSync(fullPath);
3827
3847
  const mtime = force ? void 0 : stat.mtimeMs;
3828
- const content = fs21.readFileSync(fullPath, "utf-8");
3848
+ const content = fs22.readFileSync(fullPath, "utf-8");
3829
3849
  const wasIndexed = await rag.indexFile(fullPath, content, mtime);
3830
3850
  if (wasIndexed) {
3831
3851
  indexed++;
@@ -3902,7 +3922,7 @@ var init_indexing = __esm({
3902
3922
  });
3903
3923
 
3904
3924
  // src/mcp/resources/context.ts
3905
- import * as path23 from "path";
3925
+ import * as path24 from "path";
3906
3926
  import * as os4 from "os";
3907
3927
  function getContextPreamble() {
3908
3928
  const activeProject = detectActiveProject();
@@ -3918,7 +3938,7 @@ If the above tools fail, ask the user for clarification.
3918
3938
  ---
3919
3939
  `;
3920
3940
  }
3921
- const rrceHome = process.env.RRCE_HOME || path23.join(os4.homedir(), ".rrce-workflow");
3941
+ const rrceHome = process.env.RRCE_HOME || path24.join(os4.homedir(), ".rrce-workflow");
3922
3942
  const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
3923
3943
  const rrceData = activeProject.dataPath;
3924
3944
  return `## System Context
@@ -4213,8 +4233,8 @@ var init_validation = __esm({
4213
4233
  });
4214
4234
 
4215
4235
  // src/mcp/resources/sessions.ts
4216
- import * as fs22 from "fs";
4217
- import * as path24 from "path";
4236
+ import * as fs23 from "fs";
4237
+ import * as path25 from "path";
4218
4238
  function startSession(projectName, taskSlug, agent, phase) {
4219
4239
  const config = loadMCPConfig();
4220
4240
  const projects = projectService.scan();
@@ -4222,8 +4242,8 @@ function startSession(projectName, taskSlug, agent, phase) {
4222
4242
  if (!project || !project.tasksPath) {
4223
4243
  return { success: false, message: `Project '${projectName}' not found or not exposed.` };
4224
4244
  }
4225
- const taskDir = path24.join(project.tasksPath, taskSlug);
4226
- if (!fs22.existsSync(taskDir)) {
4245
+ const taskDir = path25.join(project.tasksPath, taskSlug);
4246
+ if (!fs23.existsSync(taskDir)) {
4227
4247
  return { success: false, message: `Task '${taskSlug}' not found.` };
4228
4248
  }
4229
4249
  const session = {
@@ -4233,8 +4253,8 @@ function startSession(projectName, taskSlug, agent, phase) {
4233
4253
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
4234
4254
  heartbeat: (/* @__PURE__ */ new Date()).toISOString()
4235
4255
  };
4236
- const sessionPath = path24.join(taskDir, "session.json");
4237
- fs22.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
4256
+ const sessionPath = path25.join(taskDir, "session.json");
4257
+ fs23.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
4238
4258
  return { success: true, message: `Session started for ${agent} agent on task '${taskSlug}' (phase: ${phase})` };
4239
4259
  }
4240
4260
  function endSession(projectName, taskSlug) {
@@ -4244,11 +4264,11 @@ function endSession(projectName, taskSlug) {
4244
4264
  if (!project || !project.tasksPath) {
4245
4265
  return { success: false, message: `Project '${projectName}' not found or not exposed.` };
4246
4266
  }
4247
- const sessionPath = path24.join(project.tasksPath, taskSlug, "session.json");
4248
- if (!fs22.existsSync(sessionPath)) {
4267
+ const sessionPath = path25.join(project.tasksPath, taskSlug, "session.json");
4268
+ if (!fs23.existsSync(sessionPath)) {
4249
4269
  return { success: true, message: `No active session for task '${taskSlug}'.` };
4250
4270
  }
4251
- fs22.unlinkSync(sessionPath);
4271
+ fs23.unlinkSync(sessionPath);
4252
4272
  return { success: true, message: `Session ended for task '${taskSlug}'.` };
4253
4273
  }
4254
4274
  function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
@@ -4258,9 +4278,9 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
4258
4278
  if (!project || !project.tasksPath) {
4259
4279
  return { success: false, message: `Project '${projectName}' not found or not exposed.` };
4260
4280
  }
4261
- const taskDir = path24.join(project.tasksPath, taskSlug);
4262
- if (!fs22.existsSync(taskDir)) {
4263
- fs22.mkdirSync(taskDir, { recursive: true });
4281
+ const taskDir = path25.join(project.tasksPath, taskSlug);
4282
+ if (!fs23.existsSync(taskDir)) {
4283
+ fs23.mkdirSync(taskDir, { recursive: true });
4264
4284
  }
4265
4285
  const todos = {
4266
4286
  phase,
@@ -4268,8 +4288,8 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
4268
4288
  items,
4269
4289
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
4270
4290
  };
4271
- const todosPath = path24.join(taskDir, "agent-todos.json");
4272
- fs22.writeFileSync(todosPath, JSON.stringify(todos, null, 2));
4291
+ const todosPath = path25.join(taskDir, "agent-todos.json");
4292
+ fs23.writeFileSync(todosPath, JSON.stringify(todos, null, 2));
4273
4293
  return { success: true, message: `Updated ${items.length} todo items for task '${taskSlug}'.`, count: items.length };
4274
4294
  }
4275
4295
  var init_sessions = __esm({
@@ -4389,143 +4409,69 @@ var init_resources3 = __esm({
4389
4409
  }
4390
4410
  });
4391
4411
 
4392
- // src/mcp/prompts.ts
4393
- import * as path25 from "path";
4394
- import * as fs23 from "fs";
4395
- function loadBaseProtocol2() {
4396
- if (baseProtocolCache !== null) {
4397
- return baseProtocolCache;
4398
- }
4399
- const basePath = path25.join(getAgentCorePromptsDir(), "_base.md");
4400
- if (fs23.existsSync(basePath)) {
4401
- const content = fs23.readFileSync(basePath, "utf-8");
4402
- baseProtocolCache = content.replace(/^---[\s\S]*?---\n*/, "");
4403
- return baseProtocolCache;
4412
+ // src/mcp/handlers/tools/project.ts
4413
+ async function handleProjectTool(name, args) {
4414
+ if (!args) {
4415
+ if (name === "list_projects" || name === "help_setup") {
4416
+ } else {
4417
+ return null;
4418
+ }
4404
4419
  }
4405
- baseProtocolCache = "";
4406
- return "";
4407
- }
4408
- function getAllPrompts() {
4409
- const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
4410
- return prompts.map((p) => {
4411
- const args = [];
4412
- if (p.frontmatter["required-args"]) {
4413
- args.push(...p.frontmatter["required-args"].map((a) => ({
4414
- name: a.name,
4415
- description: a.prompt || a.name,
4416
- required: true
4417
- })));
4420
+ switch (name) {
4421
+ case "resolve_path": {
4422
+ const params = args;
4423
+ const result = resolveProjectPaths(params.project, params.path);
4424
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4418
4425
  }
4419
- if (p.frontmatter["optional-args"]) {
4420
- args.push(...p.frontmatter["optional-args"].map((a) => ({
4421
- name: a.name,
4422
- description: a.prompt || a.name,
4423
- required: false
4424
- })));
4425
- }
4426
- const filename = p.filePath.split("/").pop() || "";
4427
- const id = filename.replace(/\.md$/, "");
4428
- return {
4429
- id,
4430
- name: p.frontmatter.name,
4431
- description: p.frontmatter.description,
4432
- arguments: args,
4433
- content: p.content
4434
- };
4435
- });
4436
- }
4437
- function getPromptDef(name) {
4438
- const all = getAllPrompts();
4439
- const search = name.toLowerCase();
4440
- return all.find(
4441
- (p) => p.name === name || p.id === name || p.name.toLowerCase() === search || p.id.toLowerCase() === search
4442
- );
4443
- }
4444
- function renderPromptWithContext(content, args) {
4445
- const renderArgs = { ...args };
4446
- let activeProject = detectActiveProject();
4447
- if (!activeProject) {
4448
- projectService.refresh();
4449
- activeProject = detectActiveProject();
4450
- }
4451
- const DEFAULT_RRCE_HOME = getEffectiveGlobalPath();
4452
- let resolvedRrceData = ".rrce-workflow/";
4453
- let resolvedRrceHome = DEFAULT_RRCE_HOME;
4454
- let resolvedWorkspaceRoot = process.cwd();
4455
- let resolvedWorkspaceName = "current-project";
4456
- if (activeProject) {
4457
- resolvedRrceData = activeProject.dataPath;
4458
- if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
4459
- resolvedRrceData += "/";
4460
- }
4461
- resolvedWorkspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
4462
- resolvedWorkspaceName = activeProject.name;
4463
- if (activeProject.source === "global") {
4464
- const workspacesDir = path25.dirname(activeProject.dataPath);
4465
- resolvedRrceHome = path25.dirname(workspacesDir);
4426
+ case "list_projects": {
4427
+ const projects = getExposedProjects();
4428
+ const list = projects.map((p) => ({ name: p.name, source: p.source, path: p.path }));
4429
+ return {
4430
+ content: [{
4431
+ type: "text",
4432
+ text: JSON.stringify(list, null, 2) + "\n\nTip: Use these project names for tools like `get_project_context` or `index_knowledge`."
4433
+ }]
4434
+ };
4466
4435
  }
4467
- } else {
4468
- try {
4469
- const workspaceRoot = detectWorkspaceRoot();
4470
- const workspaceName = path25.basename(workspaceRoot);
4471
- const globalWorkspacePath = path25.join(DEFAULT_RRCE_HOME, "workspaces", workspaceName);
4472
- if (fs23.existsSync(globalWorkspacePath)) {
4473
- resolvedRrceData = globalWorkspacePath;
4474
- resolvedWorkspaceRoot = workspaceRoot;
4475
- resolvedWorkspaceName = workspaceName;
4476
- if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
4477
- resolvedRrceData += "/";
4478
- }
4436
+ case "get_project_context": {
4437
+ const context = getProjectContext(args.project);
4438
+ if (!context) {
4439
+ const projects = getExposedProjects().map((p) => p.name).join(", ");
4440
+ const msg = `No project context found for "${args.project}".
4441
+ Available projects: ${projects}`;
4442
+ logger.warn(msg);
4443
+ return { content: [{ type: "text", text: msg }], isError: true };
4479
4444
  }
4480
- } catch (e) {
4445
+ return { content: [{ type: "text", text: context }] };
4481
4446
  }
4482
- }
4483
- if (!renderArgs["RRCE_DATA"]) renderArgs["RRCE_DATA"] = resolvedRrceData;
4484
- if (!renderArgs["RRCE_HOME"]) renderArgs["RRCE_HOME"] = resolvedRrceHome;
4485
- if (!renderArgs["WORKSPACE_ROOT"]) renderArgs["WORKSPACE_ROOT"] = resolvedWorkspaceRoot;
4486
- if (!renderArgs["WORKSPACE_NAME"]) renderArgs["WORKSPACE_NAME"] = resolvedWorkspaceName;
4487
- const agentContent = renderPrompt(content, renderArgs);
4488
- const baseProtocol = loadBaseProtocol2();
4489
- const rendered = baseProtocol ? `${baseProtocol}
4490
- ${agentContent}` : agentContent;
4491
- return {
4492
- rendered,
4493
- context: {
4494
- RRCE_DATA: resolvedRrceData,
4495
- RRCE_HOME: resolvedRrceHome,
4496
- WORKSPACE_ROOT: resolvedWorkspaceRoot,
4497
- WORKSPACE_NAME: resolvedWorkspaceName
4447
+ case "index_knowledge": {
4448
+ const params = args;
4449
+ const result = await indexKnowledge(params.project, params.force, params.clean);
4450
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4498
4451
  }
4499
- };
4500
- }
4501
- function renderPrompt(content, args) {
4502
- let rendered = content;
4503
- for (const [key, val] of Object.entries(args)) {
4504
- rendered = rendered.replace(new RegExp(`{{${key}}}`, "g"), val);
4452
+ case "help_setup": {
4453
+ const msg = `
4454
+ RRCE MCP Server is running, but no projects are configured/exposed.
4455
+
4456
+ To fix this:
4457
+ 1. Open a terminal.
4458
+ 2. Run: npx rrce-workflow mcp configure
4459
+ 3. Select the projects you want to expose to the AI.
4460
+ 4. Restart the MCP server (or it may pick up changes automatically).
4461
+ `;
4462
+ return { content: [{ type: "text", text: msg }] };
4463
+ }
4464
+ default:
4465
+ return null;
4505
4466
  }
4506
- return rendered;
4507
4467
  }
4508
- var baseProtocolCache;
4509
- var init_prompts2 = __esm({
4510
- "src/mcp/prompts.ts"() {
4468
+ var projectTools;
4469
+ var init_project = __esm({
4470
+ "src/mcp/handlers/tools/project.ts"() {
4511
4471
  "use strict";
4512
- init_prompts();
4513
4472
  init_resources2();
4514
- init_paths();
4515
- init_detection_service();
4516
- baseProtocolCache = null;
4517
- }
4518
- });
4519
-
4520
- // src/mcp/handlers/tools.ts
4521
- import "@modelcontextprotocol/sdk/server/index.js";
4522
- import {
4523
- ListToolsRequestSchema,
4524
- CallToolRequestSchema
4525
- } from "@modelcontextprotocol/sdk/types.js";
4526
- function registerToolHandlers(server) {
4527
- server.setRequestHandler(ListToolsRequestSchema, async () => {
4528
- const tools = [
4473
+ init_logger();
4474
+ projectTools = [
4529
4475
  {
4530
4476
  name: "resolve_path",
4531
4477
  description: "Resolve configuration paths (RRCE_DATA, etc.) for a project. Helps determine if a project is using global or local storage.",
@@ -4537,6 +4483,124 @@ function registerToolHandlers(server) {
4537
4483
  }
4538
4484
  }
4539
4485
  },
4486
+ {
4487
+ name: "list_projects",
4488
+ description: "List all projects exposed via MCP. Use these names for project-specific tools.",
4489
+ inputSchema: { type: "object", properties: {} }
4490
+ },
4491
+ {
4492
+ name: "get_project_context",
4493
+ description: "Get the project context/architecture for a specific project",
4494
+ inputSchema: {
4495
+ type: "object",
4496
+ properties: { project: { type: "string", description: "Name of the project to get context for" } },
4497
+ required: ["project"]
4498
+ }
4499
+ },
4500
+ {
4501
+ name: "index_knowledge",
4502
+ description: "Update the semantic search index for a specific project",
4503
+ inputSchema: {
4504
+ type: "object",
4505
+ properties: {
4506
+ project: { type: "string", description: "Name of the project to index" },
4507
+ force: { type: "boolean", description: "Force re-indexing of all files" },
4508
+ clean: { type: "boolean", description: "Wipe existing index and rebuild from scratch" }
4509
+ },
4510
+ required: ["project"]
4511
+ }
4512
+ }
4513
+ ];
4514
+ }
4515
+ });
4516
+
4517
+ // src/mcp/handlers/tools/search.ts
4518
+ async function handleSearchTool(name, args) {
4519
+ if (!args) return null;
4520
+ switch (name) {
4521
+ case "search_knowledge": {
4522
+ const params = args;
4523
+ const result = await searchKnowledge(params.query, params.project, {
4524
+ max_tokens: params.max_tokens,
4525
+ min_score: params.min_score
4526
+ });
4527
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4528
+ }
4529
+ case "search_code": {
4530
+ const params = args;
4531
+ const result = await searchCode(params.query, params.project, params.limit, {
4532
+ max_tokens: params.max_tokens,
4533
+ min_score: params.min_score
4534
+ });
4535
+ if (result.results.length === 0) {
4536
+ return {
4537
+ content: [{
4538
+ type: "text",
4539
+ text: "No code matches found. The code index may be empty or semantic search is not enabled.\nRun `index_knowledge` first to build the code index."
4540
+ }]
4541
+ };
4542
+ }
4543
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4544
+ }
4545
+ case "find_related_files": {
4546
+ const params = args;
4547
+ const result = await findRelatedFiles2(params.file, params.project, {
4548
+ includeImports: params.include_imports,
4549
+ includeImportedBy: params.include_imported_by,
4550
+ depth: params.depth
4551
+ });
4552
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4553
+ }
4554
+ case "search_symbols": {
4555
+ const params = args;
4556
+ const result = await searchSymbols2(params.name, params.project, {
4557
+ type: params.type,
4558
+ fuzzy: params.fuzzy,
4559
+ limit: params.limit
4560
+ });
4561
+ if (!result.success) {
4562
+ return { content: [{ type: "text", text: result.message || "Search failed" }], isError: true };
4563
+ }
4564
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4565
+ }
4566
+ case "get_file_summary": {
4567
+ const params = args;
4568
+ const result = await getFileSummary(params.file, params.project);
4569
+ if (!result.success) {
4570
+ return { content: [{ type: "text", text: result.message || "Failed to get file summary" }], isError: true };
4571
+ }
4572
+ return { content: [{ type: "text", text: JSON.stringify(result.summary, null, 2) }] };
4573
+ }
4574
+ case "get_context_bundle": {
4575
+ const params = args;
4576
+ const result = await getContextBundle(params.query, params.project, {
4577
+ task_slug: params.task_slug,
4578
+ max_tokens: params.max_tokens,
4579
+ include: params.include
4580
+ });
4581
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4582
+ }
4583
+ case "prefetch_task_context": {
4584
+ const params = args;
4585
+ const result = await prefetchTaskContext(params.project, params.task_slug, {
4586
+ max_tokens: params.max_tokens
4587
+ });
4588
+ if (!result.success) {
4589
+ return { content: [{ type: "text", text: result.message || "Failed to prefetch task context" }], isError: true };
4590
+ }
4591
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4592
+ }
4593
+ default:
4594
+ return null;
4595
+ }
4596
+ }
4597
+ var searchTools;
4598
+ var init_search2 = __esm({
4599
+ "src/mcp/handlers/tools/search.ts"() {
4600
+ "use strict";
4601
+ init_resources2();
4602
+ init_logger();
4603
+ searchTools = [
4540
4604
  {
4541
4605
  name: "search_knowledge",
4542
4606
  description: "Search across all exposed project knowledge bases. Returns results with token count and optional truncation.",
@@ -4644,140 +4708,197 @@ function registerToolHandlers(server) {
4644
4708
  },
4645
4709
  required: ["project", "task_slug"]
4646
4710
  }
4647
- },
4711
+ }
4712
+ ];
4713
+ }
4714
+ });
4715
+
4716
+ // src/mcp/handlers/tools/task.ts
4717
+ async function handleTaskTool(name, args) {
4718
+ if (!args) return null;
4719
+ switch (name) {
4720
+ case "list_tasks": {
4721
+ const params = args;
4722
+ const tasks = getProjectTasks(params.project);
4723
+ return { content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }] };
4724
+ }
4725
+ case "get_task": {
4726
+ const params = args;
4727
+ const task = getTask(params.project, params.task_slug);
4728
+ if (!task) {
4729
+ return { content: [{ type: "text", text: `Task '${params.task_slug}' not found in project '${params.project}'.` }], isError: true };
4730
+ }
4731
+ return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }] };
4732
+ }
4733
+ case "create_task": {
4734
+ const params = args;
4735
+ const taskData = {
4736
+ title: params.title || params.task_slug,
4737
+ summary: params.summary || ""
4738
+ };
4739
+ const task = await createTask(params.project, params.task_slug, taskData);
4740
+ return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' created. meta.json saved.
4741
+ ${JSON.stringify(task, null, 2)}` }] };
4742
+ }
4743
+ case "update_task": {
4744
+ const params = args;
4745
+ const task = await updateTask(params.project, params.task_slug, params.updates);
4746
+ return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' updated. meta.json saved.
4747
+ ${JSON.stringify(task, null, 2)}` }] };
4748
+ }
4749
+ case "delete_task": {
4750
+ const params = args;
4751
+ const success = deleteTask(params.project, params.task_slug);
4752
+ return { content: [{ type: "text", text: success ? `\u2713 Task '${params.task_slug}' deleted.` : `\u2717 Failed to delete '${params.task_slug}'.` }] };
4753
+ }
4754
+ case "search_tasks": {
4755
+ const params = args;
4756
+ const results = searchTasks(params.project, {
4757
+ keyword: params.keyword,
4758
+ status: params.status,
4759
+ agent: params.agent,
4760
+ since: params.since,
4761
+ limit: params.limit
4762
+ });
4763
+ return { content: [{ type: "text", text: JSON.stringify({ count: results.length, tasks: results }, null, 2) }] };
4764
+ }
4765
+ case "validate_phase": {
4766
+ const params = args;
4767
+ const result = validatePhase(params.project, params.task_slug, params.phase);
4768
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4769
+ }
4770
+ default:
4771
+ return null;
4772
+ }
4773
+ }
4774
+ var taskTools;
4775
+ var init_task = __esm({
4776
+ "src/mcp/handlers/tools/task.ts"() {
4777
+ "use strict";
4778
+ init_resources2();
4779
+ taskTools = [
4648
4780
  {
4649
- name: "search_tasks",
4650
- description: "Search across all tasks by keyword, status, agent phase, or date. Returns matching tasks sorted by relevance.",
4781
+ name: "list_tasks",
4782
+ description: "List all tasks for a project",
4651
4783
  inputSchema: {
4652
4784
  type: "object",
4653
- properties: {
4654
- project: { type: "string", description: "Name of the project" },
4655
- keyword: { type: "string", description: "Search in title/summary" },
4656
- status: { type: "string", description: "Filter by status (draft, in_progress, complete, etc.)" },
4657
- agent: { type: "string", description: "Filter by agent phase (research, planning, executor, documentation)" },
4658
- since: { type: "string", description: "ISO date - tasks updated after this date" },
4659
- limit: { type: "number", description: "Max results (default: 20)" }
4660
- },
4785
+ properties: { project: { type: "string", description: "Name of the project" } },
4661
4786
  required: ["project"]
4662
4787
  }
4663
4788
  },
4664
4789
  {
4665
- name: "validate_phase",
4666
- description: "Check if a task phase has all prerequisites complete. Returns validation result with missing items and suggestions.",
4790
+ name: "get_task",
4791
+ description: "Get details of a specific task",
4667
4792
  inputSchema: {
4668
4793
  type: "object",
4669
4794
  properties: {
4670
4795
  project: { type: "string", description: "Name of the project" },
4671
- task_slug: { type: "string", description: "The task slug" },
4672
- phase: { type: "string", enum: ["research", "planning", "execution", "documentation"], description: "Phase to validate" }
4796
+ task_slug: { type: "string", description: "The slug of the task" }
4673
4797
  },
4674
- required: ["project", "task_slug", "phase"]
4798
+ required: ["project", "task_slug"]
4675
4799
  }
4676
4800
  },
4677
4801
  {
4678
- name: "index_knowledge",
4679
- description: "Update the semantic search index for a specific project",
4802
+ name: "create_task",
4803
+ description: "Create a new task in the project",
4680
4804
  inputSchema: {
4681
4805
  type: "object",
4682
4806
  properties: {
4683
- project: { type: "string", description: "Name of the project to index" },
4684
- force: { type: "boolean", description: "Force re-indexing of all files" },
4685
- clean: { type: "boolean", description: "Wipe existing index and rebuild from scratch" }
4807
+ project: { type: "string", description: "Name of the project" },
4808
+ task_slug: { type: "string", description: "The slug for the new task (kebab-case)" },
4809
+ title: { type: "string", description: "The title of the task" },
4810
+ summary: { type: "string", description: "Brief summary of the task" }
4686
4811
  },
4687
- required: ["project"]
4812
+ required: ["project", "task_slug"]
4688
4813
  }
4689
4814
  },
4690
4815
  {
4691
- name: "list_projects",
4692
- description: "List all projects exposed via MCP. Use these names for project-specific tools.",
4693
- inputSchema: { type: "object", properties: {} }
4694
- },
4695
- {
4696
- name: "get_project_context",
4697
- description: "Get the project context/architecture for a specific project",
4816
+ name: "update_task",
4817
+ description: "Update an existing task",
4698
4818
  inputSchema: {
4699
4819
  type: "object",
4700
- properties: { project: { type: "string", description: "Name of the project to get context for" } },
4701
- required: ["project"]
4820
+ properties: {
4821
+ project: { type: "string", description: "Name of the project" },
4822
+ task_slug: { type: "string", description: "The slug of the task" },
4823
+ updates: { type: "object", description: "The fields to update in meta.json", additionalProperties: true }
4824
+ },
4825
+ required: ["project", "task_slug", "updates"]
4702
4826
  }
4703
4827
  },
4704
4828
  {
4705
- name: "list_agents",
4706
- description: "List available agents (e.g. init, plan) and their arguments. Use this to discover which agent to call.",
4707
- inputSchema: { type: "object", properties: {} }
4708
- },
4709
- {
4710
- name: "get_agent_prompt",
4711
- description: 'Get the system prompt for a specific agent. Accepts agent Name (e.g. "RRCE Init") or ID (e.g. "init").',
4712
- inputSchema: {
4713
- type: "object",
4714
- properties: {
4715
- agent: { type: "string", description: "Name of the agent (e.g. init, plan, execute)" },
4716
- args: { type: "object", description: "Arguments for the agent prompt", additionalProperties: true }
4717
- },
4718
- required: ["agent"]
4719
- }
4720
- },
4721
- {
4722
- name: "list_tasks",
4723
- description: "List all tasks for a project",
4724
- inputSchema: {
4725
- type: "object",
4726
- properties: { project: { type: "string", description: "Name of the project" } },
4727
- required: ["project"]
4728
- }
4729
- },
4730
- {
4731
- name: "get_task",
4732
- description: "Get details of a specific task",
4733
- inputSchema: {
4734
- type: "object",
4735
- properties: {
4736
- project: { type: "string", description: "Name of the project" },
4737
- task_slug: { type: "string", description: "The slug of the task" }
4738
- },
4739
- required: ["project", "task_slug"]
4740
- }
4741
- },
4742
- {
4743
- name: "create_task",
4744
- description: "Create a new task in the project",
4829
+ name: "delete_task",
4830
+ description: "Delete a task from the project",
4745
4831
  inputSchema: {
4746
4832
  type: "object",
4747
4833
  properties: {
4748
4834
  project: { type: "string", description: "Name of the project" },
4749
- task_slug: { type: "string", description: "The slug for the new task (kebab-case)" },
4750
- title: { type: "string", description: "The title of the task" },
4751
- summary: { type: "string", description: "Brief summary of the task" }
4835
+ task_slug: { type: "string", description: "The slug of the task to delete" }
4752
4836
  },
4753
4837
  required: ["project", "task_slug"]
4754
4838
  }
4755
4839
  },
4756
4840
  {
4757
- name: "update_task",
4758
- description: "Update an existing task",
4841
+ name: "search_tasks",
4842
+ description: "Search across all tasks by keyword, status, agent phase, or date. Returns matching tasks sorted by relevance.",
4759
4843
  inputSchema: {
4760
4844
  type: "object",
4761
4845
  properties: {
4762
4846
  project: { type: "string", description: "Name of the project" },
4763
- task_slug: { type: "string", description: "The slug of the task" },
4764
- updates: { type: "object", description: "The fields to update in meta.json", additionalProperties: true }
4847
+ keyword: { type: "string", description: "Search in title/summary" },
4848
+ status: { type: "string", description: "Filter by status (draft, in_progress, complete, etc.)" },
4849
+ agent: { type: "string", description: "Filter by agent phase (research, planning, executor, documentation)" },
4850
+ since: { type: "string", description: "ISO date - tasks updated after this date" },
4851
+ limit: { type: "number", description: "Max results (default: 20)" }
4765
4852
  },
4766
- required: ["project", "task_slug", "updates"]
4853
+ required: ["project"]
4767
4854
  }
4768
4855
  },
4769
4856
  {
4770
- name: "delete_task",
4771
- description: "Delete a task from the project",
4857
+ name: "validate_phase",
4858
+ description: "Check if a task phase has all prerequisites complete. Returns validation result with missing items and suggestions.",
4772
4859
  inputSchema: {
4773
4860
  type: "object",
4774
4861
  properties: {
4775
4862
  project: { type: "string", description: "Name of the project" },
4776
- task_slug: { type: "string", description: "The slug of the task to delete" }
4863
+ task_slug: { type: "string", description: "The task slug" },
4864
+ phase: { type: "string", enum: ["research", "planning", "execution", "documentation"], description: "Phase to validate" }
4777
4865
  },
4778
- required: ["project", "task_slug"]
4866
+ required: ["project", "task_slug", "phase"]
4779
4867
  }
4780
- },
4868
+ }
4869
+ ];
4870
+ }
4871
+ });
4872
+
4873
+ // src/mcp/handlers/tools/session.ts
4874
+ async function handleSessionTool(name, args) {
4875
+ if (!args) return null;
4876
+ switch (name) {
4877
+ case "start_session": {
4878
+ const params = args;
4879
+ const result = startSession(params.project, params.task_slug, params.agent, params.phase);
4880
+ return { content: [{ type: "text", text: result.message }], isError: !result.success };
4881
+ }
4882
+ case "end_session": {
4883
+ const params = args;
4884
+ const result = endSession(params.project, params.task_slug);
4885
+ return { content: [{ type: "text", text: result.message }], isError: !result.success };
4886
+ }
4887
+ case "update_agent_todos": {
4888
+ const params = args;
4889
+ const result = updateAgentTodos(params.project, params.task_slug, params.phase, params.agent, params.items);
4890
+ return { content: [{ type: "text", text: result.message }], isError: !result.success };
4891
+ }
4892
+ default:
4893
+ return null;
4894
+ }
4895
+ }
4896
+ var sessionTools;
4897
+ var init_session = __esm({
4898
+ "src/mcp/handlers/tools/session.ts"() {
4899
+ "use strict";
4900
+ init_resources2();
4901
+ sessionTools = [
4781
4902
  {
4782
4903
  name: "start_session",
4783
4904
  description: "Start an agent session for active task tracking. Call this when beginning work on a task phase.",
@@ -4833,6 +4954,219 @@ function registerToolHandlers(server) {
4833
4954
  }
4834
4955
  }
4835
4956
  ];
4957
+ }
4958
+ });
4959
+
4960
+ // src/mcp/prompts.ts
4961
+ import * as path26 from "path";
4962
+ import * as fs24 from "fs";
4963
+ function loadBaseProtocol2() {
4964
+ if (baseProtocolCache !== null) {
4965
+ return baseProtocolCache;
4966
+ }
4967
+ const basePath = path26.join(getAgentCorePromptsDir(), "_base.md");
4968
+ if (fs24.existsSync(basePath)) {
4969
+ const content = fs24.readFileSync(basePath, "utf-8");
4970
+ baseProtocolCache = content.replace(/^---[\s\S]*?---\n*/, "");
4971
+ return baseProtocolCache;
4972
+ }
4973
+ baseProtocolCache = "";
4974
+ return "";
4975
+ }
4976
+ function getAllPrompts() {
4977
+ const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
4978
+ return prompts.map((p) => {
4979
+ const args = [];
4980
+ if (p.frontmatter["required-args"]) {
4981
+ args.push(...p.frontmatter["required-args"].map((a) => ({
4982
+ name: a.name,
4983
+ description: a.prompt || a.name,
4984
+ required: true
4985
+ })));
4986
+ }
4987
+ if (p.frontmatter["optional-args"]) {
4988
+ args.push(...p.frontmatter["optional-args"].map((a) => ({
4989
+ name: a.name,
4990
+ description: a.prompt || a.name,
4991
+ required: false
4992
+ })));
4993
+ }
4994
+ const filename = p.filePath.split("/").pop() || "";
4995
+ const id = filename.replace(/\.md$/, "");
4996
+ return {
4997
+ id,
4998
+ name: p.frontmatter.name,
4999
+ description: p.frontmatter.description,
5000
+ arguments: args,
5001
+ content: p.content
5002
+ };
5003
+ });
5004
+ }
5005
+ function getPromptDef(name) {
5006
+ const all = getAllPrompts();
5007
+ const search = name.toLowerCase();
5008
+ return all.find(
5009
+ (p) => p.name === name || p.id === name || p.name.toLowerCase() === search || p.id.toLowerCase() === search
5010
+ );
5011
+ }
5012
+ function renderPromptWithContext(content, args) {
5013
+ const renderArgs = { ...args };
5014
+ let activeProject = detectActiveProject();
5015
+ if (!activeProject) {
5016
+ projectService.refresh();
5017
+ activeProject = detectActiveProject();
5018
+ }
5019
+ const DEFAULT_RRCE_HOME = getEffectiveGlobalPath();
5020
+ let resolvedRrceData = ".rrce-workflow/";
5021
+ let resolvedRrceHome = DEFAULT_RRCE_HOME;
5022
+ let resolvedWorkspaceRoot = process.cwd();
5023
+ let resolvedWorkspaceName = "current-project";
5024
+ if (activeProject) {
5025
+ resolvedRrceData = activeProject.dataPath;
5026
+ if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
5027
+ resolvedRrceData += "/";
5028
+ }
5029
+ resolvedWorkspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
5030
+ resolvedWorkspaceName = activeProject.name;
5031
+ if (activeProject.source === "global") {
5032
+ const workspacesDir = path26.dirname(activeProject.dataPath);
5033
+ resolvedRrceHome = path26.dirname(workspacesDir);
5034
+ }
5035
+ } else {
5036
+ try {
5037
+ const workspaceRoot = detectWorkspaceRoot();
5038
+ const workspaceName = path26.basename(workspaceRoot);
5039
+ const globalWorkspacePath = path26.join(DEFAULT_RRCE_HOME, "workspaces", workspaceName);
5040
+ if (fs24.existsSync(globalWorkspacePath)) {
5041
+ resolvedRrceData = globalWorkspacePath;
5042
+ resolvedWorkspaceRoot = workspaceRoot;
5043
+ resolvedWorkspaceName = workspaceName;
5044
+ if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
5045
+ resolvedRrceData += "/";
5046
+ }
5047
+ }
5048
+ } catch (e) {
5049
+ }
5050
+ }
5051
+ if (!renderArgs["RRCE_DATA"]) renderArgs["RRCE_DATA"] = resolvedRrceData;
5052
+ if (!renderArgs["RRCE_HOME"]) renderArgs["RRCE_HOME"] = resolvedRrceHome;
5053
+ if (!renderArgs["WORKSPACE_ROOT"]) renderArgs["WORKSPACE_ROOT"] = resolvedWorkspaceRoot;
5054
+ if (!renderArgs["WORKSPACE_NAME"]) renderArgs["WORKSPACE_NAME"] = resolvedWorkspaceName;
5055
+ const agentContent = renderPrompt(content, renderArgs);
5056
+ const baseProtocol = loadBaseProtocol2();
5057
+ const rendered = baseProtocol ? `${baseProtocol}
5058
+ ${agentContent}` : agentContent;
5059
+ return {
5060
+ rendered,
5061
+ context: {
5062
+ RRCE_DATA: resolvedRrceData,
5063
+ RRCE_HOME: resolvedRrceHome,
5064
+ WORKSPACE_ROOT: resolvedWorkspaceRoot,
5065
+ WORKSPACE_NAME: resolvedWorkspaceName
5066
+ }
5067
+ };
5068
+ }
5069
+ function renderPrompt(content, args) {
5070
+ let rendered = content;
5071
+ for (const [key, val] of Object.entries(args)) {
5072
+ rendered = rendered.replace(new RegExp(`{{${key}}}`, "g"), val);
5073
+ }
5074
+ return rendered;
5075
+ }
5076
+ var baseProtocolCache;
5077
+ var init_prompts2 = __esm({
5078
+ "src/mcp/prompts.ts"() {
5079
+ "use strict";
5080
+ init_prompts();
5081
+ init_resources2();
5082
+ init_paths();
5083
+ init_detection_service();
5084
+ baseProtocolCache = null;
5085
+ }
5086
+ });
5087
+
5088
+ // src/mcp/handlers/tools/agent.ts
5089
+ async function handleAgentTool(name, args) {
5090
+ if (!args && name !== "list_agents") return null;
5091
+ switch (name) {
5092
+ case "list_agents": {
5093
+ const prompts = getAllPrompts();
5094
+ return {
5095
+ content: [{
5096
+ type: "text",
5097
+ text: JSON.stringify(prompts.map((p) => ({
5098
+ name: p.name,
5099
+ id: p.id,
5100
+ description: p.description,
5101
+ arguments: p.arguments
5102
+ })), null, 2) + "\n\nTip: Retrieve the prompt for an agent using `get_agent_prompt` with its name or ID."
5103
+ }]
5104
+ };
5105
+ }
5106
+ case "get_agent_prompt": {
5107
+ const params = args;
5108
+ const agentName = params.agent;
5109
+ const promptDef = getPromptDef(agentName);
5110
+ if (!promptDef) {
5111
+ const available = getAllPrompts().map((p) => `${p.name} (id: ${p.id})`).join(", ");
5112
+ throw new Error(`Agent not found: ${agentName}. Available agents: ${available}`);
5113
+ }
5114
+ const renderArgs = params.args || {};
5115
+ const stringArgs = {};
5116
+ for (const [key, val] of Object.entries(renderArgs)) {
5117
+ stringArgs[key] = String(val);
5118
+ }
5119
+ const { rendered } = renderPromptWithContext(promptDef.content, stringArgs);
5120
+ const contextPreamble = getContextPreamble();
5121
+ return { content: [{ type: "text", text: contextPreamble + rendered }] };
5122
+ }
5123
+ default:
5124
+ return null;
5125
+ }
5126
+ }
5127
+ var agentTools;
5128
+ var init_agent = __esm({
5129
+ "src/mcp/handlers/tools/agent.ts"() {
5130
+ "use strict";
5131
+ init_resources2();
5132
+ init_prompts2();
5133
+ agentTools = [
5134
+ {
5135
+ name: "list_agents",
5136
+ description: "List available agents (e.g. init, plan) and their arguments. Use this to discover which agent to call.",
5137
+ inputSchema: { type: "object", properties: {} }
5138
+ },
5139
+ {
5140
+ name: "get_agent_prompt",
5141
+ description: 'Get the system prompt for a specific agent. Accepts agent Name (e.g. "RRCE Init") or ID (e.g. "init").',
5142
+ inputSchema: {
5143
+ type: "object",
5144
+ properties: {
5145
+ agent: { type: "string", description: "Name of the agent (e.g. init, plan, execute)" },
5146
+ args: { type: "object", description: "Arguments for the agent prompt", additionalProperties: true }
5147
+ },
5148
+ required: ["agent"]
5149
+ }
5150
+ }
5151
+ ];
5152
+ }
5153
+ });
5154
+
5155
+ // src/mcp/handlers/tools.ts
5156
+ import "@modelcontextprotocol/sdk/server/index.js";
5157
+ import {
5158
+ ListToolsRequestSchema,
5159
+ CallToolRequestSchema
5160
+ } from "@modelcontextprotocol/sdk/types.js";
5161
+ function registerToolHandlers(server) {
5162
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
5163
+ const tools = [
5164
+ ...projectTools,
5165
+ ...searchTools,
5166
+ ...taskTools,
5167
+ ...sessionTools,
5168
+ ...agentTools
5169
+ ];
4836
5170
  const projects = getExposedProjects();
4837
5171
  if (projects.length === 0) {
4838
5172
  tools.push({
@@ -4847,221 +5181,17 @@ function registerToolHandlers(server) {
4847
5181
  const { name, arguments: args } = request.params;
4848
5182
  logger.info(`Calling tool: ${name}`, args);
4849
5183
  try {
4850
- switch (name) {
4851
- case "resolve_path": {
4852
- const params = args;
4853
- const result = resolveProjectPaths(params.project, params.path);
4854
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4855
- }
4856
- case "search_knowledge": {
4857
- const params = args;
4858
- const result = await searchKnowledge(params.query, params.project, {
4859
- max_tokens: params.max_tokens,
4860
- min_score: params.min_score
4861
- });
4862
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4863
- }
4864
- case "search_code": {
4865
- const params = args;
4866
- const result = await searchCode(params.query, params.project, params.limit, {
4867
- max_tokens: params.max_tokens,
4868
- min_score: params.min_score
4869
- });
4870
- if (result.results.length === 0) {
4871
- return {
4872
- content: [{
4873
- type: "text",
4874
- text: "No code matches found. The code index may be empty or semantic search is not enabled.\nRun `index_knowledge` first to build the code index."
4875
- }]
4876
- };
4877
- }
4878
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4879
- }
4880
- case "find_related_files": {
4881
- const params = args;
4882
- const result = await findRelatedFiles2(params.file, params.project, {
4883
- includeImports: params.include_imports,
4884
- includeImportedBy: params.include_imported_by,
4885
- depth: params.depth
4886
- });
4887
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4888
- }
4889
- case "search_symbols": {
4890
- const params = args;
4891
- const result = await searchSymbols2(params.name, params.project, {
4892
- type: params.type,
4893
- fuzzy: params.fuzzy,
4894
- limit: params.limit
4895
- });
4896
- if (!result.success) {
4897
- return { content: [{ type: "text", text: result.message || "Search failed" }], isError: true };
4898
- }
4899
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4900
- }
4901
- case "get_file_summary": {
4902
- const params = args;
4903
- const result = await getFileSummary(params.file, params.project);
4904
- if (!result.success) {
4905
- return { content: [{ type: "text", text: result.message || "Failed to get file summary" }], isError: true };
4906
- }
4907
- return { content: [{ type: "text", text: JSON.stringify(result.summary, null, 2) }] };
4908
- }
4909
- case "get_context_bundle": {
4910
- const params = args;
4911
- const result = await getContextBundle(params.query, params.project, {
4912
- task_slug: params.task_slug,
4913
- max_tokens: params.max_tokens,
4914
- include: params.include
4915
- });
4916
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4917
- }
4918
- case "index_knowledge": {
4919
- const params = args;
4920
- const result = await indexKnowledge(params.project, params.force, params.clean);
4921
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
4922
- }
4923
- case "list_projects": {
4924
- const projects = getExposedProjects();
4925
- const list = projects.map((p) => ({ name: p.name, source: p.source, path: p.path }));
4926
- return {
4927
- content: [{
4928
- type: "text",
4929
- text: JSON.stringify(list, null, 2) + "\n\nTip: Use these project names for tools like `get_project_context` or `index_knowledge`."
4930
- }]
4931
- };
4932
- }
4933
- case "get_project_context": {
4934
- const context = getProjectContext(args.project);
4935
- if (!context) {
4936
- const projects = getExposedProjects().map((p) => p.name).join(", ");
4937
- const msg = `No project context found for "${args.project}".
4938
- Available projects: ${projects}`;
4939
- logger.warn(msg);
4940
- return { content: [{ type: "text", text: msg }], isError: true };
4941
- }
4942
- return { content: [{ type: "text", text: context }] };
4943
- }
4944
- case "list_agents": {
4945
- const prompts = getAllPrompts();
4946
- return {
4947
- content: [{
4948
- type: "text",
4949
- text: JSON.stringify(prompts.map((p) => ({
4950
- name: p.name,
4951
- id: p.id,
4952
- description: p.description,
4953
- arguments: p.arguments
4954
- })), null, 2) + "\n\nTip: Retrieve the prompt for an agent using `get_agent_prompt` with its name or ID."
4955
- }]
4956
- };
4957
- }
4958
- case "get_agent_prompt": {
4959
- const params = args;
4960
- const agentName = params.agent;
4961
- const promptDef = getPromptDef(agentName);
4962
- if (!promptDef) {
4963
- const available = getAllPrompts().map((p) => `${p.name} (id: ${p.id})`).join(", ");
4964
- throw new Error(`Agent not found: ${agentName}. Available agents: ${available}`);
4965
- }
4966
- const renderArgs = params.args || {};
4967
- const stringArgs = {};
4968
- for (const [key, val] of Object.entries(renderArgs)) {
4969
- stringArgs[key] = String(val);
4970
- }
4971
- const { rendered } = renderPromptWithContext(promptDef.content, stringArgs);
4972
- const contextPreamble = getContextPreamble();
4973
- return { content: [{ type: "text", text: contextPreamble + rendered }] };
4974
- }
4975
- case "list_tasks": {
4976
- const params = args;
4977
- const tasks = getProjectTasks(params.project);
4978
- return { content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }] };
4979
- }
4980
- case "get_task": {
4981
- const params = args;
4982
- const task = getTask(params.project, params.task_slug);
4983
- if (!task) {
4984
- return { content: [{ type: "text", text: `Task '${params.task_slug}' not found in project '${params.project}'.` }], isError: true };
4985
- }
4986
- return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }] };
4987
- }
4988
- case "create_task": {
4989
- const params = args;
4990
- const taskData = {
4991
- title: params.title || params.task_slug,
4992
- summary: params.summary || ""
4993
- };
4994
- const task = await createTask(params.project, params.task_slug, taskData);
4995
- return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' created. meta.json saved.
4996
- ${JSON.stringify(task, null, 2)}` }] };
4997
- }
4998
- case "update_task": {
4999
- const params = args;
5000
- const task = await updateTask(params.project, params.task_slug, params.updates);
5001
- return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' updated. meta.json saved.
5002
- ${JSON.stringify(task, null, 2)}` }] };
5003
- }
5004
- case "delete_task": {
5005
- const params = args;
5006
- const success = deleteTask(params.project, params.task_slug);
5007
- return { content: [{ type: "text", text: success ? `\u2713 Task '${params.task_slug}' deleted.` : `\u2717 Failed to delete '${params.task_slug}'.` }] };
5008
- }
5009
- case "start_session": {
5010
- const params = args;
5011
- const result = startSession(params.project, params.task_slug, params.agent, params.phase);
5012
- return { content: [{ type: "text", text: result.message }], isError: !result.success };
5013
- }
5014
- case "end_session": {
5015
- const params = args;
5016
- const result = endSession(params.project, params.task_slug);
5017
- return { content: [{ type: "text", text: result.message }], isError: !result.success };
5018
- }
5019
- case "update_agent_todos": {
5020
- const params = args;
5021
- const result = updateAgentTodos(params.project, params.task_slug, params.phase, params.agent, params.items);
5022
- return { content: [{ type: "text", text: result.message }], isError: !result.success };
5023
- }
5024
- case "prefetch_task_context": {
5025
- const params = args;
5026
- const result = await prefetchTaskContext(params.project, params.task_slug, {
5027
- max_tokens: params.max_tokens
5028
- });
5029
- if (!result.success) {
5030
- return { content: [{ type: "text", text: result.message || "Failed to prefetch task context" }], isError: true };
5031
- }
5032
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5033
- }
5034
- case "search_tasks": {
5035
- const params = args;
5036
- const results = searchTasks(params.project, {
5037
- keyword: params.keyword,
5038
- status: params.status,
5039
- agent: params.agent,
5040
- since: params.since,
5041
- limit: params.limit
5042
- });
5043
- return { content: [{ type: "text", text: JSON.stringify({ count: results.length, tasks: results }, null, 2) }] };
5044
- }
5045
- case "validate_phase": {
5046
- const params = args;
5047
- const result = validatePhase(params.project, params.task_slug, params.phase);
5048
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
5049
- }
5050
- case "help_setup": {
5051
- const msg = `
5052
- RRCE MCP Server is running, but no projects are configured/exposed.
5053
-
5054
- To fix this:
5055
- 1. Open a terminal.
5056
- 2. Run: npx rrce-workflow mcp configure
5057
- 3. Select the projects you want to expose to the AI.
5058
- 4. Restart the MCP server (or it may pick up changes automatically).
5059
- `;
5060
- return { content: [{ type: "text", text: msg }] };
5061
- }
5062
- default:
5063
- throw new Error(`Unknown tool: ${name}`);
5064
- }
5184
+ const projectResult = await handleProjectTool(name, args);
5185
+ if (projectResult) return projectResult;
5186
+ const searchResult = await handleSearchTool(name, args);
5187
+ if (searchResult) return searchResult;
5188
+ const taskResult = await handleTaskTool(name, args);
5189
+ if (taskResult) return taskResult;
5190
+ const sessionResult = await handleSessionTool(name, args);
5191
+ if (sessionResult) return sessionResult;
5192
+ const agentResult = await handleAgentTool(name, args);
5193
+ if (agentResult) return agentResult;
5194
+ throw new Error(`Unknown tool: ${name}`);
5065
5195
  } catch (error) {
5066
5196
  logger.error(`Tool execution failed: ${name}`, error);
5067
5197
  throw error;
@@ -5073,7 +5203,11 @@ var init_tools = __esm({
5073
5203
  "use strict";
5074
5204
  init_logger();
5075
5205
  init_resources2();
5076
- init_prompts2();
5206
+ init_project();
5207
+ init_search2();
5208
+ init_task();
5209
+ init_session();
5210
+ init_agent();
5077
5211
  }
5078
5212
  });
5079
5213
 
@@ -5299,8 +5433,8 @@ Hidden projects: ${projects.length - exposedCount}`,
5299
5433
  }
5300
5434
  async function handleConfigureGlobalPath() {
5301
5435
  const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
5302
- const fs33 = await import("fs");
5303
- const path32 = await import("path");
5436
+ const fs34 = await import("fs");
5437
+ const path33 = await import("path");
5304
5438
  note3(
5305
5439
  `MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
5306
5440
  and coordinate across projects.
@@ -5314,8 +5448,8 @@ locally in each project. MCP needs a central location.`,
5314
5448
  return false;
5315
5449
  }
5316
5450
  try {
5317
- if (!fs33.existsSync(resolvedPath)) {
5318
- fs33.mkdirSync(resolvedPath, { recursive: true });
5451
+ if (!fs34.existsSync(resolvedPath)) {
5452
+ fs34.mkdirSync(resolvedPath, { recursive: true });
5319
5453
  }
5320
5454
  const config = loadMCPConfig();
5321
5455
  saveMCPConfig(config);
@@ -5323,7 +5457,7 @@ locally in each project. MCP needs a central location.`,
5323
5457
  `${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
5324
5458
 
5325
5459
  MCP config will be stored at:
5326
- ${path32.join(resolvedPath, "mcp.yaml")}`,
5460
+ ${path33.join(resolvedPath, "mcp.yaml")}`,
5327
5461
  "Configuration Saved"
5328
5462
  );
5329
5463
  return true;
@@ -5427,15 +5561,15 @@ __export(ConfigContext_exports, {
5427
5561
  useConfig: () => useConfig
5428
5562
  });
5429
5563
  import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
5430
- import * as fs24 from "fs";
5431
- import * as path26 from "path";
5564
+ import * as fs25 from "fs";
5565
+ import * as path27 from "path";
5432
5566
  import { jsx as jsx2 } from "react/jsx-runtime";
5433
5567
  function getPackageVersion() {
5434
5568
  try {
5435
5569
  const agentCoreDir = getAgentCoreDir();
5436
- const packageJsonPath = path26.join(path26.dirname(agentCoreDir), "package.json");
5437
- if (fs24.existsSync(packageJsonPath)) {
5438
- return JSON.parse(fs24.readFileSync(packageJsonPath, "utf8")).version;
5570
+ const packageJsonPath = path27.join(path27.dirname(agentCoreDir), "package.json");
5571
+ if (fs25.existsSync(packageJsonPath)) {
5572
+ return JSON.parse(fs25.readFileSync(packageJsonPath, "utf8")).version;
5439
5573
  }
5440
5574
  } catch (e) {
5441
5575
  }
@@ -5502,16 +5636,16 @@ var init_ConfigContext = __esm({
5502
5636
  });
5503
5637
 
5504
5638
  // src/mcp/ui/lib/tasks-fs.ts
5505
- import * as fs25 from "fs";
5506
- import * as path27 from "path";
5639
+ import * as fs26 from "fs";
5640
+ import * as path28 from "path";
5507
5641
  function readSession(project, taskSlug) {
5508
5642
  const rrceData = getProjectRRCEData(project);
5509
- const sessionPath = path27.join(rrceData, "tasks", taskSlug, "session.json");
5510
- if (!fs25.existsSync(sessionPath)) {
5643
+ const sessionPath = path28.join(rrceData, "tasks", taskSlug, "session.json");
5644
+ if (!fs26.existsSync(sessionPath)) {
5511
5645
  return null;
5512
5646
  }
5513
5647
  try {
5514
- const raw = fs25.readFileSync(sessionPath, "utf-8");
5648
+ const raw = fs26.readFileSync(sessionPath, "utf-8");
5515
5649
  return JSON.parse(raw);
5516
5650
  } catch {
5517
5651
  return null;
@@ -5524,12 +5658,12 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
5524
5658
  }
5525
5659
  function readAgentTodos(project, taskSlug) {
5526
5660
  const rrceData = getProjectRRCEData(project);
5527
- const todosPath = path27.join(rrceData, "tasks", taskSlug, "agent-todos.json");
5528
- if (!fs25.existsSync(todosPath)) {
5661
+ const todosPath = path28.join(rrceData, "tasks", taskSlug, "agent-todos.json");
5662
+ if (!fs26.existsSync(todosPath)) {
5529
5663
  return null;
5530
5664
  }
5531
5665
  try {
5532
- const raw = fs25.readFileSync(todosPath, "utf-8");
5666
+ const raw = fs26.readFileSync(todosPath, "utf-8");
5533
5667
  return JSON.parse(raw);
5534
5668
  } catch {
5535
5669
  return null;
@@ -5542,8 +5676,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
5542
5676
  if (configPath.startsWith(rrceHome)) {
5543
5677
  return "global";
5544
5678
  }
5545
- if (fs25.existsSync(configPath)) {
5546
- const content = fs25.readFileSync(configPath, "utf-8");
5679
+ if (fs26.existsSync(configPath)) {
5680
+ const content = fs26.readFileSync(configPath, "utf-8");
5547
5681
  if (content.includes("mode: workspace")) return "workspace";
5548
5682
  if (content.includes("mode: global")) return "global";
5549
5683
  }
@@ -5553,7 +5687,7 @@ function detectStorageModeFromConfig(workspaceRoot) {
5553
5687
  }
5554
5688
  function getEffectiveGlobalBase() {
5555
5689
  const dummy = resolveDataPath("global", "__rrce_dummy__", "");
5556
- return path27.dirname(path27.dirname(dummy));
5690
+ return path28.dirname(path28.dirname(dummy));
5557
5691
  }
5558
5692
  function getProjectRRCEData(project) {
5559
5693
  const workspaceRoot = project.sourcePath || project.path;
@@ -5562,19 +5696,19 @@ function getProjectRRCEData(project) {
5562
5696
  }
5563
5697
  function listProjectTasks(project) {
5564
5698
  const rrceData = getProjectRRCEData(project);
5565
- const tasksPath = path27.join(rrceData, "tasks");
5566
- if (!fs25.existsSync(tasksPath)) {
5699
+ const tasksPath = path28.join(rrceData, "tasks");
5700
+ if (!fs26.existsSync(tasksPath)) {
5567
5701
  return { projectName: project.name, tasksPath, tasks: [] };
5568
5702
  }
5569
5703
  const tasks = [];
5570
5704
  try {
5571
- const entries = fs25.readdirSync(tasksPath, { withFileTypes: true });
5705
+ const entries = fs26.readdirSync(tasksPath, { withFileTypes: true });
5572
5706
  for (const entry of entries) {
5573
5707
  if (!entry.isDirectory()) continue;
5574
- const metaPath = path27.join(tasksPath, entry.name, "meta.json");
5575
- if (!fs25.existsSync(metaPath)) continue;
5708
+ const metaPath = path28.join(tasksPath, entry.name, "meta.json");
5709
+ if (!fs26.existsSync(metaPath)) continue;
5576
5710
  try {
5577
- const raw = fs25.readFileSync(metaPath, "utf-8");
5711
+ const raw = fs26.readFileSync(metaPath, "utf-8");
5578
5712
  const meta = JSON.parse(raw);
5579
5713
  if (!meta.task_slug) meta.task_slug = entry.name;
5580
5714
  tasks.push(meta);
@@ -5593,18 +5727,18 @@ function listProjectTasks(project) {
5593
5727
  }
5594
5728
  function updateTaskStatus(project, taskSlug, status) {
5595
5729
  const rrceData = getProjectRRCEData(project);
5596
- const metaPath = path27.join(rrceData, "tasks", taskSlug, "meta.json");
5597
- if (!fs25.existsSync(metaPath)) {
5730
+ const metaPath = path28.join(rrceData, "tasks", taskSlug, "meta.json");
5731
+ if (!fs26.existsSync(metaPath)) {
5598
5732
  return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
5599
5733
  }
5600
5734
  try {
5601
- const meta = JSON.parse(fs25.readFileSync(metaPath, "utf-8"));
5735
+ const meta = JSON.parse(fs26.readFileSync(metaPath, "utf-8"));
5602
5736
  const next = {
5603
5737
  ...meta,
5604
5738
  status,
5605
5739
  updated_at: (/* @__PURE__ */ new Date()).toISOString()
5606
5740
  };
5607
- fs25.writeFileSync(metaPath, JSON.stringify(next, null, 2));
5741
+ fs26.writeFileSync(metaPath, JSON.stringify(next, null, 2));
5608
5742
  return { ok: true, meta: next };
5609
5743
  } catch (e) {
5610
5744
  return { ok: false, error: String(e) };
@@ -5962,31 +6096,54 @@ var init_project_utils = __esm({
5962
6096
  }
5963
6097
  });
5964
6098
 
5965
- // src/mcp/ui/ProjectsView.tsx
5966
- import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
5967
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
5968
- import * as fs26 from "fs";
5969
- import * as path28 from "path";
6099
+ // src/mcp/ui/components/ProjectViews.tsx
6100
+ import "react";
6101
+ import { Box as Box4, Text as Text4 } from "ink";
5970
6102
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
6103
+ var ProjectsHeader, ProjectsFooter;
6104
+ var init_ProjectViews = __esm({
6105
+ "src/mcp/ui/components/ProjectViews.tsx"() {
6106
+ "use strict";
6107
+ ProjectsHeader = ({ autoExpose }) => /* @__PURE__ */ jsxs3(Box4, { paddingX: 1, justifyContent: "space-between", borderBottom: true, children: [
6108
+ /* @__PURE__ */ jsxs3(Box4, { children: [
6109
+ /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: "Projects" }),
6110
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: " \u2502 " }),
6111
+ /* @__PURE__ */ jsxs3(Text4, { color: autoExpose ? "green" : "red", children: [
6112
+ "Auto-expose: ",
6113
+ autoExpose ? "ON" : "OFF"
6114
+ ] })
6115
+ ] }),
6116
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "v0.3.14" })
6117
+ ] });
6118
+ ProjectsFooter = () => /* @__PURE__ */ jsxs3(Box4, { paddingX: 1, justifyContent: "space-between", borderTop: true, children: [
6119
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Space:Select Enter:Save a:Toggle Auto u:Refresh Drift" }),
6120
+ /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Use 'rrce-workflow wizard' for advanced config" })
6121
+ ] });
6122
+ }
6123
+ });
6124
+
6125
+ // src/mcp/ui/lib/projects.ts
6126
+ import * as fs27 from "fs";
6127
+ import * as path29 from "path";
5971
6128
  function getIndexStats(project) {
5972
6129
  const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
5973
6130
  try {
5974
6131
  const knowledgePath = project.knowledgePath;
5975
6132
  if (knowledgePath) {
5976
- const embPath = path28.join(knowledgePath, "embeddings.json");
5977
- const codeEmbPath = path28.join(knowledgePath, "code-embeddings.json");
5978
- if (fs26.existsSync(embPath)) {
5979
- const stat = fs26.statSync(embPath);
6133
+ const embPath = path29.join(knowledgePath, "embeddings.json");
6134
+ const codeEmbPath = path29.join(knowledgePath, "code-embeddings.json");
6135
+ if (fs27.existsSync(embPath)) {
6136
+ const stat = fs27.statSync(embPath);
5980
6137
  stats.lastIndexed = stat.mtime.toISOString();
5981
6138
  try {
5982
- const data = JSON.parse(fs26.readFileSync(embPath, "utf-8"));
6139
+ const data = JSON.parse(fs27.readFileSync(embPath, "utf-8"));
5983
6140
  stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
5984
6141
  } catch {
5985
6142
  }
5986
6143
  }
5987
- if (fs26.existsSync(codeEmbPath)) {
6144
+ if (fs27.existsSync(codeEmbPath)) {
5988
6145
  try {
5989
- const data = JSON.parse(fs26.readFileSync(codeEmbPath, "utf-8"));
6146
+ const data = JSON.parse(fs27.readFileSync(codeEmbPath, "utf-8"));
5990
6147
  stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
5991
6148
  } catch {
5992
6149
  }
@@ -5996,6 +6153,16 @@ function getIndexStats(project) {
5996
6153
  }
5997
6154
  return stats;
5998
6155
  }
6156
+ var init_projects2 = __esm({
6157
+ "src/mcp/ui/lib/projects.ts"() {
6158
+ "use strict";
6159
+ }
6160
+ });
6161
+
6162
+ // src/mcp/ui/ProjectsView.tsx
6163
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
6164
+ import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
6165
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
5999
6166
  var ProjectsView;
6000
6167
  var init_ProjectsView = __esm({
6001
6168
  "src/mcp/ui/ProjectsView.tsx"() {
@@ -6007,6 +6174,8 @@ var init_ProjectsView = __esm({
6007
6174
  init_config_utils();
6008
6175
  init_project_utils();
6009
6176
  init_ui_helpers();
6177
+ init_ProjectViews();
6178
+ init_projects2();
6010
6179
  ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
6011
6180
  const { driftReports, checkAllDrift } = useConfig();
6012
6181
  const [config, setConfig] = useState3(initialConfig);
@@ -6112,20 +6281,10 @@ ${statsRow}` : label;
6112
6281
  setConfig(newConfig);
6113
6282
  onConfigChange?.();
6114
6283
  };
6115
- return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
6116
- /* @__PURE__ */ jsxs3(Box4, { paddingX: 1, justifyContent: "space-between", borderBottom: true, children: [
6117
- /* @__PURE__ */ jsxs3(Box4, { children: [
6118
- /* @__PURE__ */ jsx5(Text4, { bold: true, color: "cyan", children: "Projects" }),
6119
- /* @__PURE__ */ jsx5(Text4, { color: "dim", children: " \u2502 " }),
6120
- /* @__PURE__ */ jsxs3(Text4, { color: config.defaults.includeNew ? "green" : "red", children: [
6121
- "Auto-expose: ",
6122
- config.defaults.includeNew ? "ON" : "OFF"
6123
- ] })
6124
- ] }),
6125
- /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "v0.3.14" })
6126
- ] }),
6127
- /* @__PURE__ */ jsx5(Box4, { marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Manage which projects are exposed to the MCP server." }) }),
6128
- /* @__PURE__ */ jsx5(Box4, { marginTop: 1, paddingX: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx5(
6284
+ return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
6285
+ /* @__PURE__ */ jsx6(ProjectsHeader, { autoExpose: config.defaults.includeNew }),
6286
+ /* @__PURE__ */ jsx6(Box5, { marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsx6(Text5, { color: "dim", children: "Manage which projects are exposed to the MCP server." }) }),
6287
+ /* @__PURE__ */ jsx6(Box5, { marginTop: 1, paddingX: 1, flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx6(
6129
6288
  SimpleSelect,
6130
6289
  {
6131
6290
  message: "",
@@ -6140,111 +6299,7 @@ ${statsRow}` : label;
6140
6299
  },
6141
6300
  JSON.stringify(initialSelected) + config.defaults.includeNew + JSON.stringify(indexingStats)
6142
6301
  ) }),
6143
- /* @__PURE__ */ jsxs3(Box4, { paddingX: 1, justifyContent: "space-between", borderTop: true, children: [
6144
- /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Space:Select Enter:Save a:Toggle Auto u:Refresh Drift" }),
6145
- /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Use 'rrce-workflow wizard' for advanced config" })
6146
- ] })
6147
- ] });
6148
- };
6149
- }
6150
- });
6151
-
6152
- // src/mcp/ui/components/TaskRow.tsx
6153
- import "react";
6154
- import { Box as Box5, Text as Text5 } from "ink";
6155
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
6156
- function getActiveAgent(task) {
6157
- if (!task.agents) return null;
6158
- for (const [agent, info] of Object.entries(task.agents)) {
6159
- if (info?.status === "in_progress") {
6160
- return { agent, status: "in_progress" };
6161
- }
6162
- }
6163
- const agentOrder = ["documentation", "executor", "planning", "research"];
6164
- for (const agent of agentOrder) {
6165
- if (task.agents[agent]?.status === "complete") {
6166
- return { agent, status: "complete" };
6167
- }
6168
- }
6169
- return null;
6170
- }
6171
- var TaskRow;
6172
- var init_TaskRow = __esm({
6173
- "src/mcp/ui/components/TaskRow.tsx"() {
6174
- "use strict";
6175
- init_ui_helpers();
6176
- init_project_utils();
6177
- TaskRow = ({
6178
- row,
6179
- isSelected,
6180
- isExpanded,
6181
- taskCount,
6182
- hasDrift,
6183
- isCurrentProject = false,
6184
- isLastTask = false
6185
- }) => {
6186
- if (row.kind === "project") {
6187
- const projectColor = isSelected ? "cyan" : isCurrentProject ? "yellow" : "white";
6188
- const isBold = isSelected || isCurrentProject;
6189
- return /* @__PURE__ */ jsxs4(Box5, { children: [
6190
- /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
6191
- /* @__PURE__ */ jsxs4(Text5, { bold: isBold, color: projectColor, children: [
6192
- getFolderIcon(isExpanded),
6193
- " ",
6194
- formatProjectLabel(row.project)
6195
- ] }),
6196
- isCurrentProject && /* @__PURE__ */ jsx6(Text5, { color: "yellow", dimColor: true, children: " (current)" }),
6197
- hasDrift && /* @__PURE__ */ jsx6(Text5, { color: "magenta", children: " \u26A0" }),
6198
- /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
6199
- " ",
6200
- taskCount > 0 ? `[${taskCount}]` : ""
6201
- ] })
6202
- ] });
6203
- }
6204
- const task = row.task;
6205
- const taskLabel = task.title || task.task_slug;
6206
- const status = task.status || "";
6207
- const isPlaceholder = task.task_slug === "__none__";
6208
- const branch = getTreeBranch(isLastTask);
6209
- const activeAgent = getActiveAgent(task);
6210
- const progress = getChecklistProgress(task.checklist || []);
6211
- const relativeTime = task.updated_at ? formatRelativeTime(task.updated_at) : "";
6212
- if (isPlaceholder) {
6213
- return /* @__PURE__ */ jsxs4(Box5, { children: [
6214
- /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
6215
- " ",
6216
- branch,
6217
- " "
6218
- ] }),
6219
- /* @__PURE__ */ jsx6(Text5, { color: "dim", italic: true, children: taskLabel })
6220
- ] });
6221
- }
6222
- return /* @__PURE__ */ jsxs4(Box5, { children: [
6223
- /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
6224
- /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
6225
- branch,
6226
- " "
6227
- ] }),
6228
- /* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: "\u{1F4CB} " }),
6229
- /* @__PURE__ */ jsx6(Box5, { flexGrow: 1, children: /* @__PURE__ */ jsx6(Text5, { bold: isSelected, color: isSelected ? "cyan" : "white", children: taskLabel.length > 25 ? taskLabel.substring(0, 22) + "..." : taskLabel }) }),
6230
- activeAgent && /* @__PURE__ */ jsxs4(Text5, { children: [
6231
- /* @__PURE__ */ jsx6(Text5, { color: "dim", children: " " }),
6232
- /* @__PURE__ */ jsx6(Text5, { children: getPhaseIcon(activeAgent.agent) }),
6233
- /* @__PURE__ */ jsx6(Text5, { color: activeAgent.status === "in_progress" ? "yellow" : "green", children: getAgentStatusIcon(activeAgent.status) })
6234
- ] }),
6235
- progress.total > 0 && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
6236
- " ",
6237
- getProgressBar(progress.percentage, 6),
6238
- " ",
6239
- progress.completed,
6240
- "/",
6241
- progress.total
6242
- ] }),
6243
- relativeTime && relativeTime !== "\u2014" && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
6244
- " ",
6245
- relativeTime
6246
- ] }),
6247
- !activeAgent && status && /* @__PURE__ */ jsx6(Text5, { color: getStatusColor(status), children: ` ${getStatusIcon(status)} ${status}` })
6302
+ /* @__PURE__ */ jsx6(ProjectsFooter, {})
6248
6303
  ] });
6249
6304
  };
6250
6305
  }
@@ -6374,10 +6429,184 @@ var init_TaskDetails = __esm({
6374
6429
  }
6375
6430
  });
6376
6431
 
6432
+ // src/mcp/ui/components/TaskRow.tsx
6433
+ import "react";
6434
+ import { Box as Box7, Text as Text7 } from "ink";
6435
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6436
+ function getActiveAgent(task) {
6437
+ if (!task.agents) return null;
6438
+ for (const [agent, info] of Object.entries(task.agents)) {
6439
+ if (info?.status === "in_progress") {
6440
+ return { agent, status: "in_progress" };
6441
+ }
6442
+ }
6443
+ const agentOrder = ["documentation", "executor", "planning", "research"];
6444
+ for (const agent of agentOrder) {
6445
+ if (task.agents[agent]?.status === "complete") {
6446
+ return { agent, status: "complete" };
6447
+ }
6448
+ }
6449
+ return null;
6450
+ }
6451
+ var TaskRow;
6452
+ var init_TaskRow = __esm({
6453
+ "src/mcp/ui/components/TaskRow.tsx"() {
6454
+ "use strict";
6455
+ init_ui_helpers();
6456
+ init_project_utils();
6457
+ TaskRow = ({
6458
+ row,
6459
+ isSelected,
6460
+ isExpanded,
6461
+ taskCount,
6462
+ hasDrift,
6463
+ isCurrentProject = false,
6464
+ isLastTask = false
6465
+ }) => {
6466
+ if (row.kind === "project") {
6467
+ const projectColor = isSelected ? "cyan" : isCurrentProject ? "yellow" : "white";
6468
+ const isBold = isSelected || isCurrentProject;
6469
+ return /* @__PURE__ */ jsxs6(Box7, { children: [
6470
+ /* @__PURE__ */ jsx8(Text7, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
6471
+ /* @__PURE__ */ jsxs6(Text7, { bold: isBold, color: projectColor, children: [
6472
+ getFolderIcon(isExpanded),
6473
+ " ",
6474
+ formatProjectLabel(row.project)
6475
+ ] }),
6476
+ isCurrentProject && /* @__PURE__ */ jsx8(Text7, { color: "yellow", dimColor: true, children: " (current)" }),
6477
+ hasDrift && /* @__PURE__ */ jsx8(Text7, { color: "magenta", children: " \u26A0" }),
6478
+ /* @__PURE__ */ jsxs6(Text7, { color: "dim", children: [
6479
+ " ",
6480
+ taskCount > 0 ? `[${taskCount}]` : ""
6481
+ ] })
6482
+ ] });
6483
+ }
6484
+ const task = row.task;
6485
+ const taskLabel = task.title || task.task_slug;
6486
+ const status = task.status || "";
6487
+ const isPlaceholder = task.task_slug === "__none__";
6488
+ const branch = getTreeBranch(isLastTask);
6489
+ const activeAgent = getActiveAgent(task);
6490
+ const progress = getChecklistProgress(task.checklist || []);
6491
+ const relativeTime = task.updated_at ? formatRelativeTime(task.updated_at) : "";
6492
+ if (isPlaceholder) {
6493
+ return /* @__PURE__ */ jsxs6(Box7, { children: [
6494
+ /* @__PURE__ */ jsxs6(Text7, { color: "dim", children: [
6495
+ " ",
6496
+ branch,
6497
+ " "
6498
+ ] }),
6499
+ /* @__PURE__ */ jsx8(Text7, { color: "dim", italic: true, children: taskLabel })
6500
+ ] });
6501
+ }
6502
+ return /* @__PURE__ */ jsxs6(Box7, { children: [
6503
+ /* @__PURE__ */ jsx8(Text7, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
6504
+ /* @__PURE__ */ jsxs6(Text7, { color: "dim", children: [
6505
+ branch,
6506
+ " "
6507
+ ] }),
6508
+ /* @__PURE__ */ jsx8(Text7, { color: isSelected ? "cyan" : "white", children: "\u{1F4CB} " }),
6509
+ /* @__PURE__ */ jsx8(Box7, { flexGrow: 1, children: /* @__PURE__ */ jsx8(Text7, { bold: isSelected, color: isSelected ? "cyan" : "white", children: taskLabel.length > 25 ? taskLabel.substring(0, 22) + "..." : taskLabel }) }),
6510
+ activeAgent && /* @__PURE__ */ jsxs6(Text7, { children: [
6511
+ /* @__PURE__ */ jsx8(Text7, { color: "dim", children: " " }),
6512
+ /* @__PURE__ */ jsx8(Text7, { children: getPhaseIcon(activeAgent.agent) }),
6513
+ /* @__PURE__ */ jsx8(Text7, { color: activeAgent.status === "in_progress" ? "yellow" : "green", children: getAgentStatusIcon(activeAgent.status) })
6514
+ ] }),
6515
+ progress.total > 0 && /* @__PURE__ */ jsxs6(Text7, { color: "dim", children: [
6516
+ " ",
6517
+ getProgressBar(progress.percentage, 6),
6518
+ " ",
6519
+ progress.completed,
6520
+ "/",
6521
+ progress.total
6522
+ ] }),
6523
+ relativeTime && relativeTime !== "\u2014" && /* @__PURE__ */ jsxs6(Text7, { color: "dim", children: [
6524
+ " ",
6525
+ relativeTime
6526
+ ] }),
6527
+ !activeAgent && status && /* @__PURE__ */ jsx8(Text7, { color: getStatusColor(status), children: ` ${getStatusIcon(status)} ${status}` })
6528
+ ] });
6529
+ };
6530
+ }
6531
+ });
6532
+
6533
+ // src/mcp/ui/components/TaskTree.tsx
6534
+ import "react";
6535
+ import { Box as Box8, Text as Text8 } from "ink";
6536
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6537
+ var TaskTree;
6538
+ var init_TaskTree = __esm({
6539
+ "src/mcp/ui/components/TaskTree.tsx"() {
6540
+ "use strict";
6541
+ init_project_utils();
6542
+ init_TaskRow();
6543
+ TaskTree = ({
6544
+ flattenedRows,
6545
+ selectedIndex,
6546
+ expanded,
6547
+ taskCache,
6548
+ driftReports
6549
+ }) => {
6550
+ return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", width: "50%", borderStyle: "single", borderColor: "dim", borderRight: true, paddingX: 1, children: flattenedRows.length === 0 ? /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: [
6551
+ /* @__PURE__ */ jsx9(Text8, { color: "dim", children: "No projects detected." }),
6552
+ /* @__PURE__ */ jsx9(Text8, { color: "dim", children: "Run the wizard to set up projects." })
6553
+ ] }) : /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: flattenedRows.map((row, idx) => {
6554
+ const k = projectKey(row.project);
6555
+ const isCurrentProject = row.kind === "project" ? row.isCurrentProject : false;
6556
+ const isLastTask = row.kind === "task" ? row.isLastTask : false;
6557
+ return /* @__PURE__ */ jsx9(
6558
+ TaskRow,
6559
+ {
6560
+ row,
6561
+ isSelected: idx === selectedIndex,
6562
+ isExpanded: expanded.has(k),
6563
+ taskCount: (taskCache[k] || []).length,
6564
+ hasDrift: !!driftReports[row.project.path]?.hasDrift,
6565
+ isCurrentProject,
6566
+ isLastTask
6567
+ },
6568
+ row.kind === "project" ? `p:${k}` : `t:${k}:${row.task.task_slug}`
6569
+ );
6570
+ }) }) });
6571
+ };
6572
+ }
6573
+ });
6574
+
6575
+ // src/mcp/ui/components/TaskViews.tsx
6576
+ import "react";
6577
+ import { Box as Box9, Text as Text9 } from "ink";
6578
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
6579
+ var TasksHeader, TasksFooter;
6580
+ var init_TaskViews = __esm({
6581
+ "src/mcp/ui/components/TaskViews.tsx"() {
6582
+ "use strict";
6583
+ TasksHeader = ({ projectCount, taskCount }) => /* @__PURE__ */ jsxs8(Box9, { paddingX: 1, justifyContent: "space-between", borderBottom: true, children: [
6584
+ /* @__PURE__ */ jsxs8(Box9, { children: [
6585
+ /* @__PURE__ */ jsx10(Text9, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
6586
+ /* @__PURE__ */ jsx10(Text9, { color: "dim", children: " \u2502 " }),
6587
+ /* @__PURE__ */ jsxs8(Text9, { children: [
6588
+ projectCount,
6589
+ " projects"
6590
+ ] }),
6591
+ /* @__PURE__ */ jsx10(Text9, { color: "dim", children: " \u2022 " }),
6592
+ /* @__PURE__ */ jsxs8(Text9, { children: [
6593
+ taskCount,
6594
+ " tasks"
6595
+ ] })
6596
+ ] }),
6597
+ /* @__PURE__ */ jsx10(Text9, { color: "dim", children: "v0.3.14" })
6598
+ ] });
6599
+ TasksFooter = () => /* @__PURE__ */ jsxs8(Box9, { paddingX: 1, justifyContent: "space-between", borderTop: true, children: [
6600
+ /* @__PURE__ */ jsx10(Text9, { color: "dim", children: "\u2191\u2193:Nav Enter:Expand s:Cycle Status R:Refresh" }),
6601
+ /* @__PURE__ */ jsx10(Text9, { color: "dim", children: "Press 'q' to exit" })
6602
+ ] });
6603
+ }
6604
+ });
6605
+
6377
6606
  // src/mcp/ui/TasksView.tsx
6378
6607
  import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
6379
- import { Box as Box7, Text as Text7, useInput as useInput3 } from "ink";
6380
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6608
+ import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
6609
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
6381
6610
  function nextStatus(current) {
6382
6611
  const idx = STATUS_CYCLE.indexOf(current || "");
6383
6612
  if (idx === -1) return STATUS_CYCLE[0];
@@ -6390,8 +6619,9 @@ var init_TasksView = __esm({
6390
6619
  init_tasks_fs();
6391
6620
  init_ConfigContext();
6392
6621
  init_project_utils();
6393
- init_TaskRow();
6394
6622
  init_TaskDetails();
6623
+ init_TaskTree();
6624
+ init_TaskViews();
6395
6625
  STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
6396
6626
  TasksView = ({ projects: allProjects, workspacePath }) => {
6397
6627
  const { driftReports } = useConfig();
@@ -6512,55 +6742,26 @@ var init_TasksView = __esm({
6512
6742
  const selectedRow = flattenedRows[selectedIndex];
6513
6743
  const selectedTask = selectedRow?.kind === "task" && selectedRow.task.task_slug !== "__none__" ? selectedRow.task : null;
6514
6744
  const totalTasks = Object.values(taskCache).flat().length;
6515
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
6516
- /* @__PURE__ */ jsxs6(Box7, { paddingX: 1, justifyContent: "space-between", borderBottom: true, children: [
6517
- /* @__PURE__ */ jsxs6(Box7, { children: [
6518
- /* @__PURE__ */ jsx8(Text7, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
6519
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2502 " }),
6520
- /* @__PURE__ */ jsxs6(Text7, { children: [
6521
- sortedProjects.length,
6522
- " projects"
6523
- ] }),
6524
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2022 " }),
6525
- /* @__PURE__ */ jsxs6(Text7, { children: [
6526
- totalTasks,
6527
- " tasks"
6528
- ] })
6529
- ] }),
6530
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "v0.3.14" })
6531
- ] }),
6532
- errorLine && /* @__PURE__ */ jsx8(Box7, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
6745
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
6746
+ /* @__PURE__ */ jsx11(TasksHeader, { projectCount: sortedProjects.length, taskCount: totalTasks }),
6747
+ errorLine && /* @__PURE__ */ jsx11(Box10, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
6533
6748
  "\u26A0 ",
6534
6749
  errorLine
6535
6750
  ] }) }),
6536
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", flexGrow: 1, children: [
6537
- /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", width: "50%", borderStyle: "single", borderColor: "dim", borderRight: true, paddingX: 1, children: flattenedRows.length === 0 ? /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: [
6538
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "No projects detected." }),
6539
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "Run the wizard to set up projects." })
6540
- ] }) : /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", marginTop: 1, children: flattenedRows.map((row, idx) => {
6541
- const k = projectKey(row.project);
6542
- const isCurrentProject = row.kind === "project" ? row.isCurrentProject : false;
6543
- const isLastTask = row.kind === "task" ? row.isLastTask : false;
6544
- return /* @__PURE__ */ jsx8(
6545
- TaskRow,
6546
- {
6547
- row,
6548
- isSelected: idx === selectedIndex,
6549
- isExpanded: expanded.has(k),
6550
- taskCount: (taskCache[k] || []).length,
6551
- hasDrift: !!driftReports[row.project.path]?.hasDrift,
6552
- isCurrentProject,
6553
- isLastTask
6554
- },
6555
- row.kind === "project" ? `p:${k}` : `t:${k}:${row.task.task_slug}`
6556
- );
6557
- }) }) }),
6558
- /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", width: "50%", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx8(TaskDetails, { task: selectedTask }) })
6751
+ /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", flexGrow: 1, children: [
6752
+ /* @__PURE__ */ jsx11(
6753
+ TaskTree,
6754
+ {
6755
+ flattenedRows,
6756
+ selectedIndex,
6757
+ expanded,
6758
+ taskCache,
6759
+ driftReports
6760
+ }
6761
+ ),
6762
+ /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", width: "50%", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx11(TaskDetails, { task: selectedTask }) })
6559
6763
  ] }),
6560
- /* @__PURE__ */ jsxs6(Box7, { paddingX: 1, justifyContent: "space-between", borderTop: true, children: [
6561
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "\u2191\u2193:Nav Enter:Expand s:Cycle Status R:Refresh" }),
6562
- /* @__PURE__ */ jsx8(Text7, { color: "dim", children: "Press 'q' to exit" })
6563
- ] })
6764
+ /* @__PURE__ */ jsx11(TasksFooter, {})
6564
6765
  ] });
6565
6766
  };
6566
6767
  }
@@ -6568,8 +6769,8 @@ var init_TasksView = __esm({
6568
6769
 
6569
6770
  // src/mcp/ui/LogViewer.tsx
6570
6771
  import "react";
6571
- import { Box as Box8, Text as Text8 } from "ink";
6572
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6772
+ import { Box as Box11, Text as Text11 } from "ink";
6773
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
6573
6774
  var LogViewer;
6574
6775
  var init_LogViewer = __esm({
6575
6776
  "src/mcp/ui/LogViewer.tsx"() {
@@ -6579,16 +6780,16 @@ var init_LogViewer = __esm({
6579
6780
  const emptyLines = Math.max(0, height - visibleLogs.length);
6580
6781
  const padding = Array(emptyLines).fill("");
6581
6782
  const formatLog = (log) => {
6582
- if (log.includes("[RAG]")) return /* @__PURE__ */ jsx9(Text8, { color: "cyan", children: log });
6583
- if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx9(Text8, { color: "red", children: log });
6584
- if (log.includes("[WARN]")) return /* @__PURE__ */ jsx9(Text8, { color: "yellow", children: log });
6585
- if (log.includes("[INFO]")) return /* @__PURE__ */ jsx9(Text8, { color: "green", children: log });
6586
- if (log.includes("Success")) return /* @__PURE__ */ jsx9(Text8, { color: "green", children: log });
6587
- return /* @__PURE__ */ jsx9(Text8, { children: log });
6783
+ if (log.includes("[RAG]")) return /* @__PURE__ */ jsx12(Text11, { color: "cyan", children: log });
6784
+ if (log.includes("[ERROR]")) return /* @__PURE__ */ jsx12(Text11, { color: "red", children: log });
6785
+ if (log.includes("[WARN]")) return /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: log });
6786
+ if (log.includes("[INFO]")) return /* @__PURE__ */ jsx12(Text11, { color: "green", children: log });
6787
+ if (log.includes("Success")) return /* @__PURE__ */ jsx12(Text11, { color: "green", children: log });
6788
+ return /* @__PURE__ */ jsx12(Text11, { children: log });
6588
6789
  };
6589
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "white", paddingX: 1, height: height + 2, flexGrow: 1, children: [
6590
- padding.map((_, i) => /* @__PURE__ */ jsx9(Text8, { children: " " }, `empty-${i}`)),
6591
- visibleLogs.map((log, i) => /* @__PURE__ */ jsx9(Box8, { children: formatLog(log) }, `log-${i}`))
6790
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "white", paddingX: 1, height: height + 2, flexGrow: 1, children: [
6791
+ padding.map((_, i) => /* @__PURE__ */ jsx12(Text11, { children: " " }, `empty-${i}`)),
6792
+ visibleLogs.map((log, i) => /* @__PURE__ */ jsx12(Box11, { children: formatLog(log) }, `log-${i}`))
6592
6793
  ] });
6593
6794
  };
6594
6795
  }
@@ -6596,28 +6797,28 @@ var init_LogViewer = __esm({
6596
6797
 
6597
6798
  // src/mcp/ui/StatusBoard.tsx
6598
6799
  import "react";
6599
- import { Box as Box9, Text as Text9 } from "ink";
6600
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
6800
+ import { Box as Box12, Text as Text12 } from "ink";
6801
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
6601
6802
  var StatusBoard;
6602
6803
  var init_StatusBoard = __esm({
6603
6804
  "src/mcp/ui/StatusBoard.tsx"() {
6604
6805
  "use strict";
6605
6806
  StatusBoard = ({ exposedLabel, port, pid, running, hasDrift }) => {
6606
- return /* @__PURE__ */ jsx10(Box9, { borderStyle: "single", borderColor: "white", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs8(Text9, { children: [
6607
- running ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx10(Text9, { color: "red", children: "\u25CF STOPPED" }),
6807
+ return /* @__PURE__ */ jsx13(Box12, { borderStyle: "single", borderColor: "white", paddingX: 1, flexGrow: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
6808
+ running ? /* @__PURE__ */ jsx13(Text12, { color: "green", children: "\u25CF RUNNING" }) : /* @__PURE__ */ jsx13(Text12, { color: "red", children: "\u25CF STOPPED" }),
6608
6809
  " ",
6609
6810
  "\u2502",
6610
6811
  " \u{1F4CB} ",
6611
- /* @__PURE__ */ jsx10(Text9, { color: "yellow", children: exposedLabel }),
6812
+ /* @__PURE__ */ jsx13(Text12, { color: "yellow", children: exposedLabel }),
6612
6813
  " ",
6613
6814
  "\u2502",
6614
6815
  " Port: ",
6615
- /* @__PURE__ */ jsx10(Text9, { color: "green", children: port }),
6816
+ /* @__PURE__ */ jsx13(Text12, { color: "green", children: port }),
6616
6817
  " ",
6617
6818
  "\u2502",
6618
6819
  " PID: ",
6619
- /* @__PURE__ */ jsx10(Text9, { color: "green", children: pid }),
6620
- hasDrift && /* @__PURE__ */ jsxs8(Text9, { color: "magenta", bold: true, children: [
6820
+ /* @__PURE__ */ jsx13(Text12, { color: "green", children: pid }),
6821
+ hasDrift && /* @__PURE__ */ jsxs11(Text12, { color: "magenta", bold: true, children: [
6621
6822
  " ",
6622
6823
  "\u2502",
6623
6824
  " \u2B06 UPDATE AVAILABLE"
@@ -6629,8 +6830,8 @@ var init_StatusBoard = __esm({
6629
6830
 
6630
6831
  // src/mcp/ui/components/TabBar.tsx
6631
6832
  import "react";
6632
- import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
6633
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
6833
+ import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
6834
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
6634
6835
  var TabBar;
6635
6836
  var init_TabBar = __esm({
6636
6837
  "src/mcp/ui/components/TabBar.tsx"() {
@@ -6660,11 +6861,11 @@ var init_TabBar = __esm({
6660
6861
  if (tab) onChange(tab.id);
6661
6862
  }
6662
6863
  });
6663
- return /* @__PURE__ */ jsxs9(Box10, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
6864
+ return /* @__PURE__ */ jsxs12(Box13, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
6664
6865
  tabs.map((tab, index) => {
6665
6866
  const isActive = tab.id === activeTab;
6666
- return /* @__PURE__ */ jsx11(Box10, { marginRight: 2, children: /* @__PURE__ */ jsx11(
6667
- Text10,
6867
+ return /* @__PURE__ */ jsx14(Box13, { marginRight: 2, children: /* @__PURE__ */ jsx14(
6868
+ Text13,
6668
6869
  {
6669
6870
  color: isActive ? "cyan" : "white",
6670
6871
  bold: isActive,
@@ -6673,8 +6874,8 @@ var init_TabBar = __esm({
6673
6874
  }
6674
6875
  ) }, tab.id);
6675
6876
  }),
6676
- /* @__PURE__ */ jsx11(Box10, { flexGrow: 1 }),
6677
- /* @__PURE__ */ jsx11(Text10, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
6877
+ /* @__PURE__ */ jsx14(Box13, { flexGrow: 1 }),
6878
+ /* @__PURE__ */ jsx14(Text13, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
6678
6879
  ] });
6679
6880
  };
6680
6881
  }
@@ -6686,9 +6887,9 @@ __export(App_exports, {
6686
6887
  App: () => App
6687
6888
  });
6688
6889
  import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
6689
- import { Box as Box11, useInput as useInput5, useApp } from "ink";
6690
- import fs27 from "fs";
6691
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
6890
+ import { Box as Box14, useInput as useInput5, useApp } from "ink";
6891
+ import fs28 from "fs";
6892
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6692
6893
  var App;
6693
6894
  var init_App = __esm({
6694
6895
  "src/mcp/ui/App.tsx"() {
@@ -6760,18 +6961,18 @@ var init_App = __esm({
6760
6961
  useEffect6(() => {
6761
6962
  const logPath = getLogFilePath();
6762
6963
  let lastSize = 0;
6763
- if (fs27.existsSync(logPath)) {
6764
- const stats = fs27.statSync(logPath);
6964
+ if (fs28.existsSync(logPath)) {
6965
+ const stats = fs28.statSync(logPath);
6765
6966
  lastSize = stats.size;
6766
6967
  }
6767
6968
  const interval = setInterval(() => {
6768
- if (fs27.existsSync(logPath)) {
6769
- const stats = fs27.statSync(logPath);
6969
+ if (fs28.existsSync(logPath)) {
6970
+ const stats = fs28.statSync(logPath);
6770
6971
  if (stats.size > lastSize) {
6771
6972
  const buffer = Buffer.alloc(stats.size - lastSize);
6772
- const fd = fs27.openSync(logPath, "r");
6773
- fs27.readSync(fd, buffer, 0, buffer.length, lastSize);
6774
- fs27.closeSync(fd);
6973
+ const fd = fs28.openSync(logPath, "r");
6974
+ fs28.readSync(fd, buffer, 0, buffer.length, lastSize);
6975
+ fs28.closeSync(fd);
6775
6976
  const newContent = buffer.toString("utf-8");
6776
6977
  const newLines = newContent.split("\n").filter((l) => l.trim());
6777
6978
  setLogs((prev) => {
@@ -6808,10 +7009,10 @@ var init_App = __esm({
6808
7009
  const handleConfigChange = useCallback3(() => {
6809
7010
  refreshData();
6810
7011
  }, [refreshData]);
6811
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", padding: 0, height: termHeight, children: [
6812
- /* @__PURE__ */ jsx12(TabBar, { tabs, activeTab, onChange: setActiveTab }),
6813
- /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexGrow: 1, children: [
6814
- activeTab === "overview" && /* @__PURE__ */ jsx12(
7012
+ return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", padding: 0, height: termHeight, children: [
7013
+ /* @__PURE__ */ jsx15(TabBar, { tabs, activeTab, onChange: setActiveTab }),
7014
+ /* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexGrow: 1, children: [
7015
+ activeTab === "overview" && /* @__PURE__ */ jsx15(
6815
7016
  Overview,
6816
7017
  {
6817
7018
  serverStatus: serverInfo,
@@ -6823,11 +7024,11 @@ var init_App = __esm({
6823
7024
  logs
6824
7025
  }
6825
7026
  ),
6826
- activeTab === "logs" && /* @__PURE__ */ jsx12(LogViewer, { logs, height: contentHeight }),
6827
- activeTab === "tasks" && /* @__PURE__ */ jsx12(TasksView, { projects, workspacePath }),
6828
- activeTab === "projects" && /* @__PURE__ */ jsx12(ProjectsView, { config, projects, onConfigChange: handleConfigChange, workspacePath })
7027
+ activeTab === "logs" && /* @__PURE__ */ jsx15(LogViewer, { logs, height: contentHeight }),
7028
+ activeTab === "tasks" && /* @__PURE__ */ jsx15(TasksView, { projects, workspacePath }),
7029
+ activeTab === "projects" && /* @__PURE__ */ jsx15(ProjectsView, { config, projects, onConfigChange: handleConfigChange, workspacePath })
6829
7030
  ] }),
6830
- /* @__PURE__ */ jsx12(Box11, { marginTop: 0, children: /* @__PURE__ */ jsx12(
7031
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 0, children: /* @__PURE__ */ jsx15(
6831
7032
  StatusBoard,
6832
7033
  {
6833
7034
  exposedLabel: `${exposedProjects.length} / ${projects.length} projects`,
@@ -6845,7 +7046,7 @@ var init_App = __esm({
6845
7046
  // src/mcp/commands/start.ts
6846
7047
  import { confirm as confirm3, isCancel as isCancel5, text } from "@clack/prompts";
6847
7048
  async function handleStartServer() {
6848
- const React13 = await import("react");
7049
+ const React16 = await import("react");
6849
7050
  const { render } = await import("ink");
6850
7051
  const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
6851
7052
  const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
@@ -6888,10 +7089,10 @@ async function handleStartServer() {
6888
7089
  }
6889
7090
  process.stdin.resume();
6890
7091
  const app = render(
6891
- React13.createElement(
7092
+ React16.createElement(
6892
7093
  ConfigProvider2,
6893
7094
  null,
6894
- React13.createElement(App2, {
7095
+ React16.createElement(App2, {
6895
7096
  initialPort,
6896
7097
  onExit: () => {
6897
7098
  }
@@ -6996,15 +7197,15 @@ __export(update_flow_exports, {
6996
7197
  });
6997
7198
  import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
6998
7199
  import pc8 from "picocolors";
6999
- import * as fs28 from "fs";
7000
- import * as path29 from "path";
7200
+ import * as fs29 from "fs";
7201
+ import * as path30 from "path";
7001
7202
  import { stringify as stringify2, parse } from "yaml";
7002
7203
  function backupFile(filePath) {
7003
- if (!fs28.existsSync(filePath)) return null;
7204
+ if (!fs29.existsSync(filePath)) return null;
7004
7205
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
7005
7206
  const backupPath = `${filePath}.${timestamp}.bak`;
7006
7207
  try {
7007
- fs28.copyFileSync(filePath, backupPath);
7208
+ fs29.copyFileSync(filePath, backupPath);
7008
7209
  return backupPath;
7009
7210
  } catch (e) {
7010
7211
  console.error(`Failed to backup ${filePath}:`, e);
@@ -7014,9 +7215,9 @@ function backupFile(filePath) {
7014
7215
  function getPackageVersion2() {
7015
7216
  try {
7016
7217
  const agentCoreDir = getAgentCoreDir();
7017
- const packageJsonPath = path29.join(path29.dirname(agentCoreDir), "package.json");
7018
- if (fs28.existsSync(packageJsonPath)) {
7019
- return JSON.parse(fs28.readFileSync(packageJsonPath, "utf8")).version;
7218
+ const packageJsonPath = path30.join(path30.dirname(agentCoreDir), "package.json");
7219
+ if (fs29.existsSync(packageJsonPath)) {
7220
+ return JSON.parse(fs29.readFileSync(packageJsonPath, "utf8")).version;
7020
7221
  }
7021
7222
  } catch (e) {
7022
7223
  }
@@ -7031,9 +7232,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7031
7232
  const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
7032
7233
  const configFilePath = getConfigPath(workspacePath);
7033
7234
  let currentSyncedVersion;
7034
- if (fs28.existsSync(configFilePath)) {
7235
+ if (fs29.existsSync(configFilePath)) {
7035
7236
  try {
7036
- const content = fs28.readFileSync(configFilePath, "utf-8");
7237
+ const content = fs29.readFileSync(configFilePath, "utf-8");
7037
7238
  const config = parse(content);
7038
7239
  currentSyncedVersion = config.last_synced_version;
7039
7240
  } catch (e) {
@@ -7041,8 +7242,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7041
7242
  }
7042
7243
  const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
7043
7244
  const ideTargets = [];
7044
- if (fs28.existsSync(configFilePath)) {
7045
- const configContent = fs28.readFileSync(configFilePath, "utf-8");
7245
+ if (fs29.existsSync(configFilePath)) {
7246
+ const configContent = fs29.readFileSync(configFilePath, "utf-8");
7046
7247
  if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
7047
7248
  if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
7048
7249
  if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
@@ -7051,14 +7252,14 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7051
7252
  const dirs = ["templates", "prompts", "docs"];
7052
7253
  const updatedFiles = [];
7053
7254
  for (const dir of dirs) {
7054
- const srcDir = path29.join(agentCoreDir, dir);
7055
- if (!fs28.existsSync(srcDir)) continue;
7255
+ const srcDir = path30.join(agentCoreDir, dir);
7256
+ if (!fs29.existsSync(srcDir)) continue;
7056
7257
  const syncFiles = (src, rel) => {
7057
- const entries = fs28.readdirSync(src, { withFileTypes: true });
7258
+ const entries = fs29.readdirSync(src, { withFileTypes: true });
7058
7259
  for (const entry of entries) {
7059
- const entrySrc = path29.join(src, entry.name);
7060
- const entryRel = path29.join(rel, entry.name);
7061
- const entryDest = path29.join(dataPath, entryRel);
7260
+ const entrySrc = path30.join(src, entry.name);
7261
+ const entryRel = path30.join(rel, entry.name);
7262
+ const entryDest = path30.join(dataPath, entryRel);
7062
7263
  if (entry.isDirectory()) {
7063
7264
  ensureDir(entryDest);
7064
7265
  syncFiles(entrySrc, entryRel);
@@ -7066,8 +7267,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7066
7267
  if (driftReport.modifiedFiles.includes(entryRel)) {
7067
7268
  backupFile(entryDest);
7068
7269
  }
7069
- ensureDir(path29.dirname(entryDest));
7070
- fs28.copyFileSync(entrySrc, entryDest);
7270
+ ensureDir(path30.dirname(entryDest));
7271
+ fs29.copyFileSync(entrySrc, entryDest);
7071
7272
  updatedFiles.push(entryRel);
7072
7273
  }
7073
7274
  }
@@ -7078,12 +7279,12 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7078
7279
  DriftService.saveManifest(dataPath, manifest);
7079
7280
  }
7080
7281
  const rrceHome = customGlobalPath || getDefaultRRCEHome2();
7081
- ensureDir(path29.join(rrceHome, "templates"));
7082
- ensureDir(path29.join(rrceHome, "docs"));
7083
- copyDirRecursive(path29.join(agentCoreDir, "templates"), path29.join(rrceHome, "templates"));
7084
- copyDirRecursive(path29.join(agentCoreDir, "docs"), path29.join(rrceHome, "docs"));
7085
- if (fs28.existsSync(configFilePath)) {
7086
- const configContent = fs28.readFileSync(configFilePath, "utf-8");
7282
+ ensureDir(path30.join(rrceHome, "templates"));
7283
+ ensureDir(path30.join(rrceHome, "docs"));
7284
+ copyDirRecursive(path30.join(agentCoreDir, "templates"), path30.join(rrceHome, "templates"));
7285
+ copyDirRecursive(path30.join(agentCoreDir, "docs"), path30.join(rrceHome, "docs"));
7286
+ if (fs29.existsSync(configFilePath)) {
7287
+ const configContent = fs29.readFileSync(configFilePath, "utf-8");
7087
7288
  if (configContent.includes("copilot: true")) {
7088
7289
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
7089
7290
  ensureDir(copilotPath);
@@ -7105,21 +7306,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
7105
7306
  try {
7106
7307
  const yaml = parse(configContent);
7107
7308
  yaml.last_synced_version = runningVersion;
7108
- fs28.writeFileSync(configFilePath, stringify2(yaml));
7309
+ fs29.writeFileSync(configFilePath, stringify2(yaml));
7109
7310
  } catch (e) {
7110
7311
  console.error("Failed to update config.yaml version:", e);
7111
7312
  }
7112
7313
  }
7113
- const mcpPath = path29.join(rrceHome, "mcp.yaml");
7114
- if (fs28.existsSync(mcpPath)) {
7314
+ const mcpPath = path30.join(rrceHome, "mcp.yaml");
7315
+ if (fs29.existsSync(mcpPath)) {
7115
7316
  try {
7116
- const content = fs28.readFileSync(mcpPath, "utf-8");
7317
+ const content = fs29.readFileSync(mcpPath, "utf-8");
7117
7318
  const yaml = parse(content);
7118
7319
  if (yaml.projects) {
7119
7320
  const project = yaml.projects.find((p) => p.name === workspaceName);
7120
7321
  if (project) {
7121
7322
  project.last_synced_version = runningVersion;
7122
- fs28.writeFileSync(mcpPath, stringify2(yaml));
7323
+ fs29.writeFileSync(mcpPath, stringify2(yaml));
7123
7324
  }
7124
7325
  }
7125
7326
  } catch (e) {
@@ -7155,9 +7356,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
7155
7356
  const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
7156
7357
  const configFilePath = getConfigPath(workspacePath);
7157
7358
  let currentSyncedVersion;
7158
- if (fs28.existsSync(configFilePath)) {
7359
+ if (fs29.existsSync(configFilePath)) {
7159
7360
  try {
7160
- const content = fs28.readFileSync(configFilePath, "utf-8");
7361
+ const content = fs29.readFileSync(configFilePath, "utf-8");
7161
7362
  const config = parse(content);
7162
7363
  currentSyncedVersion = config.last_synced_version;
7163
7364
  } catch (e) {
@@ -7181,8 +7382,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
7181
7382
  ` \u2022 docs/ (documentation)`
7182
7383
  ];
7183
7384
  const ideTargets = [];
7184
- if (fs28.existsSync(configFilePath)) {
7185
- const configContent = fs28.readFileSync(configFilePath, "utf-8");
7385
+ if (fs29.existsSync(configFilePath)) {
7386
+ const configContent = fs29.readFileSync(configFilePath, "utf-8");
7186
7387
  if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
7187
7388
  if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
7188
7389
  if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
@@ -7211,14 +7412,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
7211
7412
  const dirs = ["templates", "prompts", "docs"];
7212
7413
  const updatedFiles = [];
7213
7414
  for (const dir of dirs) {
7214
- const srcDir = path29.join(agentCoreDir, dir);
7215
- if (!fs28.existsSync(srcDir)) continue;
7415
+ const srcDir = path30.join(agentCoreDir, dir);
7416
+ if (!fs29.existsSync(srcDir)) continue;
7216
7417
  const syncFiles = (src, rel) => {
7217
- const entries = fs28.readdirSync(src, { withFileTypes: true });
7418
+ const entries = fs29.readdirSync(src, { withFileTypes: true });
7218
7419
  for (const entry of entries) {
7219
- const entrySrc = path29.join(src, entry.name);
7220
- const entryRel = path29.join(rel, entry.name);
7221
- const entryDest = path29.join(dataPath, entryRel);
7420
+ const entrySrc = path30.join(src, entry.name);
7421
+ const entryRel = path30.join(rel, entry.name);
7422
+ const entryDest = path30.join(dataPath, entryRel);
7222
7423
  if (entry.isDirectory()) {
7223
7424
  ensureDir(entryDest);
7224
7425
  syncFiles(entrySrc, entryRel);
@@ -7226,8 +7427,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
7226
7427
  if (driftReport.modifiedFiles.includes(entryRel)) {
7227
7428
  backupFile(entryDest);
7228
7429
  }
7229
- ensureDir(path29.dirname(entryDest));
7230
- fs28.copyFileSync(entrySrc, entryDest);
7430
+ ensureDir(path30.dirname(entryDest));
7431
+ fs29.copyFileSync(entrySrc, entryDest);
7231
7432
  updatedFiles.push(entryRel);
7232
7433
  }
7233
7434
  }
@@ -7238,12 +7439,12 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
7238
7439
  DriftService.saveManifest(dataPath, manifest);
7239
7440
  }
7240
7441
  const rrceHome = customGlobalPath || getDefaultRRCEHome2();
7241
- ensureDir(path29.join(rrceHome, "templates"));
7242
- ensureDir(path29.join(rrceHome, "docs"));
7243
- copyDirRecursive(path29.join(agentCoreDir, "templates"), path29.join(rrceHome, "templates"));
7244
- copyDirRecursive(path29.join(agentCoreDir, "docs"), path29.join(rrceHome, "docs"));
7245
- if (fs28.existsSync(configFilePath)) {
7246
- const configContent = fs28.readFileSync(configFilePath, "utf-8");
7442
+ ensureDir(path30.join(rrceHome, "templates"));
7443
+ ensureDir(path30.join(rrceHome, "docs"));
7444
+ copyDirRecursive(path30.join(agentCoreDir, "templates"), path30.join(rrceHome, "templates"));
7445
+ copyDirRecursive(path30.join(agentCoreDir, "docs"), path30.join(rrceHome, "docs"));
7446
+ if (fs29.existsSync(configFilePath)) {
7447
+ const configContent = fs29.readFileSync(configFilePath, "utf-8");
7247
7448
  if (configContent.includes("copilot: true")) {
7248
7449
  const copilotPath = getAgentPromptPath(workspacePath, "copilot");
7249
7450
  ensureDir(copilotPath);
@@ -7265,21 +7466,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
7265
7466
  try {
7266
7467
  const yaml = parse(configContent);
7267
7468
  yaml.last_synced_version = runningVersion;
7268
- fs28.writeFileSync(configFilePath, stringify2(yaml));
7469
+ fs29.writeFileSync(configFilePath, stringify2(yaml));
7269
7470
  } catch (e) {
7270
7471
  console.error("Failed to update config.yaml version:", e);
7271
7472
  }
7272
7473
  }
7273
- const mcpPath = path29.join(rrceHome, "mcp.yaml");
7274
- if (fs28.existsSync(mcpPath)) {
7474
+ const mcpPath = path30.join(rrceHome, "mcp.yaml");
7475
+ if (fs29.existsSync(mcpPath)) {
7275
7476
  try {
7276
- const content = fs28.readFileSync(mcpPath, "utf-8");
7477
+ const content = fs29.readFileSync(mcpPath, "utf-8");
7277
7478
  const yaml = parse(content);
7278
7479
  if (yaml.projects) {
7279
7480
  const project = yaml.projects.find((p) => p.name === workspaceName);
7280
7481
  if (project) {
7281
7482
  project.last_synced_version = runningVersion;
7282
- fs28.writeFileSync(mcpPath, stringify2(yaml));
7483
+ fs29.writeFileSync(mcpPath, stringify2(yaml));
7283
7484
  }
7284
7485
  }
7285
7486
  } catch (e) {
@@ -7314,8 +7515,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
7314
7515
  }
7315
7516
  }
7316
7517
  function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
7317
- const globalPath = path29.join(customGlobalPath, "workspaces", workspaceName);
7318
- const workspacePath = path29.join(workspaceRoot, ".rrce-workflow");
7518
+ const globalPath = path30.join(customGlobalPath, "workspaces", workspaceName);
7519
+ const workspacePath = path30.join(workspaceRoot, ".rrce-workflow");
7319
7520
  switch (mode) {
7320
7521
  case "global":
7321
7522
  return [globalPath];
@@ -7338,8 +7539,8 @@ var init_update_flow = __esm({
7338
7539
  // src/commands/wizard/index.ts
7339
7540
  import { intro as intro2, select as select5, spinner as spinner7, note as note11, outro as outro7, isCancel as isCancel12, confirm as confirm10 } from "@clack/prompts";
7340
7541
  import pc13 from "picocolors";
7341
- import * as fs32 from "fs";
7342
- import * as path31 from "path";
7542
+ import * as fs33 from "fs";
7543
+ import * as path32 from "path";
7343
7544
  import { parse as parse2 } from "yaml";
7344
7545
 
7345
7546
  // src/lib/git.ts
@@ -7481,22 +7682,22 @@ async function promptConfirmation() {
7481
7682
  init_paths();
7482
7683
  init_prompts();
7483
7684
  init_utils();
7484
- import * as fs10 from "fs";
7485
- import * as path12 from "path";
7685
+ import * as fs11 from "fs";
7686
+ import * as path13 from "path";
7486
7687
  import pc4 from "picocolors";
7487
7688
  import { note as note2 } from "@clack/prompts";
7488
7689
 
7489
7690
  // src/commands/wizard/vscode.ts
7490
7691
  init_paths();
7491
7692
  init_detection();
7492
- import * as fs8 from "fs";
7493
- import * as path9 from "path";
7693
+ import * as fs9 from "fs";
7694
+ import * as path10 from "path";
7494
7695
  function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
7495
- const workspaceFilePath = path9.join(workspacePath, `${workspaceName}.code-workspace`);
7696
+ const workspaceFilePath = path10.join(workspacePath, `${workspaceName}.code-workspace`);
7496
7697
  let workspace;
7497
- if (fs8.existsSync(workspaceFilePath)) {
7698
+ if (fs9.existsSync(workspaceFilePath)) {
7498
7699
  try {
7499
- const content = fs8.readFileSync(workspaceFilePath, "utf-8");
7700
+ const content = fs9.readFileSync(workspaceFilePath, "utf-8");
7500
7701
  workspace = JSON.parse(content);
7501
7702
  } catch {
7502
7703
  workspace = { folders: [], settings: {} };
@@ -7529,7 +7730,7 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
7529
7730
  for (const project of projects) {
7530
7731
  const sourceLabel = project.source === "global" ? "global" : "local";
7531
7732
  const folderPath = project.dataPath;
7532
- if (fs8.existsSync(folderPath)) {
7733
+ if (fs9.existsSync(folderPath)) {
7533
7734
  referenceFolderPaths.push(folderPath);
7534
7735
  workspace.folders.push({
7535
7736
  path: folderPath,
@@ -7541,8 +7742,8 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
7541
7742
  const projectNames = linkedProjects;
7542
7743
  const rrceHome = customGlobalPath || getRRCEHome();
7543
7744
  for (const projectName of projectNames) {
7544
- const folderPath = path9.join(rrceHome, "workspaces", projectName);
7545
- if (fs8.existsSync(folderPath)) {
7745
+ const folderPath = path10.join(rrceHome, "workspaces", projectName);
7746
+ if (fs9.existsSync(folderPath)) {
7546
7747
  referenceFolderPaths.push(folderPath);
7547
7748
  workspace.folders.push({
7548
7749
  path: folderPath,
@@ -7568,23 +7769,23 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
7568
7769
  ...readonlyPatterns
7569
7770
  };
7570
7771
  }
7571
- fs8.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
7772
+ fs9.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
7572
7773
  }
7573
7774
 
7574
7775
  // src/commands/wizard/setup-actions.ts
7575
7776
  init_install();
7576
7777
  function detectExistingProject(workspacePath, workspaceName, globalPath) {
7577
7778
  const rrceHome = globalPath || getDefaultRRCEHome2();
7578
- const globalConfigPath = path12.join(rrceHome, "workspaces", workspaceName, "config.yaml");
7579
- const workspaceConfigPath = path12.join(workspacePath, ".rrce-workflow", "config.yaml");
7779
+ const globalConfigPath = path13.join(rrceHome, "workspaces", workspaceName, "config.yaml");
7780
+ const workspaceConfigPath = path13.join(workspacePath, ".rrce-workflow", "config.yaml");
7580
7781
  const hasOpenCodeAgents = checkForRRCEAgents();
7581
- if (fs10.existsSync(globalConfigPath)) {
7782
+ if (fs11.existsSync(globalConfigPath)) {
7582
7783
  return {
7583
7784
  isExisting: true,
7584
7785
  currentMode: "global",
7585
7786
  configPath: globalConfigPath
7586
7787
  };
7587
- } else if (fs10.existsSync(workspaceConfigPath)) {
7788
+ } else if (fs11.existsSync(workspaceConfigPath)) {
7588
7789
  return {
7589
7790
  isExisting: true,
7590
7791
  currentMode: "workspace",
@@ -7604,9 +7805,9 @@ function detectExistingProject(workspacePath, workspaceName, globalPath) {
7604
7805
  };
7605
7806
  }
7606
7807
  function checkForRRCEAgents() {
7607
- if (!fs10.existsSync(OPENCODE_CONFIG)) return false;
7808
+ if (!fs11.existsSync(OPENCODE_CONFIG)) return false;
7608
7809
  try {
7609
- const config = JSON.parse(fs10.readFileSync(OPENCODE_CONFIG, "utf8"));
7810
+ const config = JSON.parse(fs11.readFileSync(OPENCODE_CONFIG, "utf8"));
7610
7811
  const agentKeys = Object.keys(config.agent || config.agents || {});
7611
7812
  return agentKeys.some((key) => key.startsWith("rrce_"));
7612
7813
  } catch {
@@ -7616,23 +7817,23 @@ function checkForRRCEAgents() {
7616
7817
  function createDirectoryStructure(dataPaths) {
7617
7818
  for (const dataPath of dataPaths) {
7618
7819
  ensureDir(dataPath);
7619
- ensureDir(path12.join(dataPath, "knowledge"));
7620
- ensureDir(path12.join(dataPath, "refs"));
7621
- ensureDir(path12.join(dataPath, "tasks"));
7622
- ensureDir(path12.join(dataPath, "templates"));
7820
+ ensureDir(path13.join(dataPath, "knowledge"));
7821
+ ensureDir(path13.join(dataPath, "refs"));
7822
+ ensureDir(path13.join(dataPath, "tasks"));
7823
+ ensureDir(path13.join(dataPath, "templates"));
7623
7824
  }
7624
7825
  }
7625
7826
  async function installAgentPrompts(config, workspacePath, dataPaths) {
7626
7827
  const agentCoreDir = getAgentCoreDir();
7627
7828
  syncMetadataToAll(agentCoreDir, dataPaths);
7628
- copyDirToAllStoragePaths(path12.join(agentCoreDir, "templates"), "templates", dataPaths);
7629
- copyDirToAllStoragePaths(path12.join(agentCoreDir, "prompts"), "prompts", dataPaths);
7630
- copyDirToAllStoragePaths(path12.join(agentCoreDir, "docs"), "docs", dataPaths);
7829
+ copyDirToAllStoragePaths(path13.join(agentCoreDir, "templates"), "templates", dataPaths);
7830
+ copyDirToAllStoragePaths(path13.join(agentCoreDir, "prompts"), "prompts", dataPaths);
7831
+ copyDirToAllStoragePaths(path13.join(agentCoreDir, "docs"), "docs", dataPaths);
7631
7832
  const rrceHome = config.globalPath || getDefaultRRCEHome2();
7632
- ensureDir(path12.join(rrceHome, "templates"));
7633
- ensureDir(path12.join(rrceHome, "docs"));
7634
- copyDirRecursive(path12.join(agentCoreDir, "templates"), path12.join(rrceHome, "templates"));
7635
- copyDirRecursive(path12.join(agentCoreDir, "docs"), path12.join(rrceHome, "docs"));
7833
+ ensureDir(path13.join(rrceHome, "templates"));
7834
+ ensureDir(path13.join(rrceHome, "docs"));
7835
+ copyDirRecursive(path13.join(agentCoreDir, "templates"), path13.join(rrceHome, "templates"));
7836
+ copyDirRecursive(path13.join(agentCoreDir, "docs"), path13.join(rrceHome, "docs"));
7636
7837
  const needsIDEPrompts = config.storageMode === "workspace" && (config.tools.includes("copilot") || config.tools.includes("antigravity")) || config.tools.includes("opencode");
7637
7838
  if (needsIDEPrompts) {
7638
7839
  const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
@@ -7661,11 +7862,11 @@ function createWorkspaceConfig(config, workspacePath, workspaceName) {
7661
7862
  let configPath;
7662
7863
  if (config.storageMode === "global") {
7663
7864
  const rrceHome = config.globalPath || getDefaultRRCEHome2();
7664
- configPath = path12.join(rrceHome, "workspaces", workspaceName, "config.yaml");
7865
+ configPath = path13.join(rrceHome, "workspaces", workspaceName, "config.yaml");
7665
7866
  } else {
7666
- configPath = path12.join(workspacePath, ".rrce-workflow", "config.yaml");
7867
+ configPath = path13.join(workspacePath, ".rrce-workflow", "config.yaml");
7667
7868
  }
7668
- ensureDir(path12.dirname(configPath));
7869
+ ensureDir(path13.dirname(configPath));
7669
7870
  let content = `# RRCE-Workflow Configuration
7670
7871
  version: 1
7671
7872
 
@@ -7701,7 +7902,7 @@ linked_projects:
7701
7902
  `;
7702
7903
  });
7703
7904
  }
7704
- fs10.writeFileSync(configPath, content);
7905
+ fs11.writeFileSync(configPath, content);
7705
7906
  }
7706
7907
  async function registerWithMCP(config, workspacePath, workspaceName) {
7707
7908
  if (!config.exposeToMCP) return;
@@ -7728,8 +7929,8 @@ You can configure MCP later: ${pc4.cyan("npx rrce-workflow mcp")}`,
7728
7929
  }
7729
7930
  }
7730
7931
  function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
7731
- const globalPath = path12.join(customGlobalPath || getDefaultRRCEHome2(), "workspaces", workspaceName);
7732
- const workspacePath = path12.join(workspaceRoot, ".rrce-workflow");
7932
+ const globalPath = path13.join(customGlobalPath || getDefaultRRCEHome2(), "workspaces", workspaceName);
7933
+ const workspacePath = path13.join(workspaceRoot, ".rrce-workflow");
7733
7934
  switch (mode) {
7734
7935
  case "global":
7735
7936
  return [globalPath];
@@ -8016,7 +8217,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
8016
8217
  init_paths();
8017
8218
  import { multiselect as multiselect4, spinner as spinner4, note as note8, outro as outro4, cancel as cancel4, isCancel as isCancel9, confirm as confirm7 } from "@clack/prompts";
8018
8219
  import pc10 from "picocolors";
8019
- import * as fs29 from "fs";
8220
+ import * as fs30 from "fs";
8020
8221
  init_detection();
8021
8222
  async function runLinkProjectsFlow(workspacePath, workspaceName) {
8022
8223
  const projects = scanForProjects({
@@ -8055,7 +8256,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
8055
8256
  const s = spinner4();
8056
8257
  s.start("Linking projects");
8057
8258
  const configFilePath = getConfigPath(workspacePath);
8058
- let configContent = fs29.readFileSync(configFilePath, "utf-8");
8259
+ let configContent = fs30.readFileSync(configFilePath, "utf-8");
8059
8260
  if (configContent.includes("linked_projects:")) {
8060
8261
  const lines = configContent.split("\n");
8061
8262
  const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
@@ -8082,7 +8283,7 @@ linked_projects:
8082
8283
  `;
8083
8284
  });
8084
8285
  }
8085
- fs29.writeFileSync(configFilePath, configContent);
8286
+ fs30.writeFileSync(configFilePath, configContent);
8086
8287
  generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
8087
8288
  s.stop("Projects linked");
8088
8289
  const workspaceFile = `${workspaceName}.code-workspace`;
@@ -8118,15 +8319,15 @@ init_paths();
8118
8319
  init_utils();
8119
8320
  import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
8120
8321
  import pc11 from "picocolors";
8121
- import * as fs30 from "fs";
8122
- import * as path30 from "path";
8322
+ import * as fs31 from "fs";
8323
+ import * as path31 from "path";
8123
8324
  async function runSyncToGlobalFlow(workspacePath, workspaceName) {
8124
8325
  const localPath = getLocalWorkspacePath(workspacePath);
8125
8326
  const customGlobalPath = getEffectiveRRCEHome(workspacePath);
8126
- const globalPath = path30.join(customGlobalPath, "workspaces", workspaceName);
8327
+ const globalPath = path31.join(customGlobalPath, "workspaces", workspaceName);
8127
8328
  const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
8128
8329
  const existingDirs = subdirs.filter(
8129
- (dir) => fs30.existsSync(path30.join(localPath, dir))
8330
+ (dir) => fs31.existsSync(path31.join(localPath, dir))
8130
8331
  );
8131
8332
  if (existingDirs.length === 0) {
8132
8333
  outro5(pc11.yellow("No data found in workspace storage to sync."));
@@ -8152,8 +8353,8 @@ Destination: ${pc11.cyan(globalPath)}`,
8152
8353
  try {
8153
8354
  ensureDir(globalPath);
8154
8355
  for (const dir of existingDirs) {
8155
- const srcDir = path30.join(localPath, dir);
8156
- const destDir = path30.join(globalPath, dir);
8356
+ const srcDir = path31.join(localPath, dir);
8357
+ const destDir = path31.join(globalPath, dir);
8157
8358
  ensureDir(destDir);
8158
8359
  copyDirRecursive(srcDir, destDir);
8159
8360
  }
@@ -8181,7 +8382,7 @@ init_update_flow();
8181
8382
  // src/commands/wizard/delete-flow.ts
8182
8383
  import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
8183
8384
  import pc12 from "picocolors";
8184
- import * as fs31 from "fs";
8385
+ import * as fs32 from "fs";
8185
8386
  init_detection();
8186
8387
  init_config();
8187
8388
  async function runDeleteGlobalProjectFlow(availableProjects) {
@@ -8225,8 +8426,8 @@ Are you sure?`,
8225
8426
  for (const projectName of projectsToDelete) {
8226
8427
  const project = globalProjects.find((p) => p.name === projectName);
8227
8428
  if (!project) continue;
8228
- if (fs31.existsSync(project.dataPath)) {
8229
- fs31.rmSync(project.dataPath, { recursive: true, force: true });
8429
+ if (fs32.existsSync(project.dataPath)) {
8430
+ fs32.rmSync(project.dataPath, { recursive: true, force: true });
8230
8431
  }
8231
8432
  const newConfig = removeProjectConfig(mcpConfig, projectName);
8232
8433
  configChanged = true;
@@ -8248,9 +8449,9 @@ init_config();
8248
8449
  function getPackageVersion3() {
8249
8450
  try {
8250
8451
  const agentCoreDir = getAgentCoreDir();
8251
- const packageJsonPath = path31.join(path31.dirname(agentCoreDir), "package.json");
8252
- if (fs32.existsSync(packageJsonPath)) {
8253
- return JSON.parse(fs32.readFileSync(packageJsonPath, "utf8")).version;
8452
+ const packageJsonPath = path32.join(path32.dirname(agentCoreDir), "package.json");
8453
+ if (fs33.existsSync(packageJsonPath)) {
8454
+ return JSON.parse(fs33.readFileSync(packageJsonPath, "utf8")).version;
8254
8455
  }
8255
8456
  } catch (e) {
8256
8457
  }
@@ -8258,9 +8459,9 @@ function getPackageVersion3() {
8258
8459
  }
8259
8460
  function getLastSyncedVersion(workspacePath, workspaceName) {
8260
8461
  const configFilePath = getConfigPath(workspacePath);
8261
- if (fs32.existsSync(configFilePath)) {
8462
+ if (fs33.existsSync(configFilePath)) {
8262
8463
  try {
8263
- const content = fs32.readFileSync(configFilePath, "utf-8");
8464
+ const content = fs33.readFileSync(configFilePath, "utf-8");
8264
8465
  const config = parse2(content);
8265
8466
  if (config.last_synced_version) {
8266
8467
  return config.last_synced_version;
@@ -8269,10 +8470,10 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
8269
8470
  }
8270
8471
  }
8271
8472
  const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
8272
- const mcpPath = path31.join(rrceHome, "mcp.yaml");
8273
- if (fs32.existsSync(mcpPath)) {
8473
+ const mcpPath = path32.join(rrceHome, "mcp.yaml");
8474
+ if (fs33.existsSync(mcpPath)) {
8274
8475
  try {
8275
- const content = fs32.readFileSync(mcpPath, "utf-8");
8476
+ const content = fs33.readFileSync(mcpPath, "utf-8");
8276
8477
  const config = parse2(content);
8277
8478
  const project = config.projects?.find((p) => p.name === workspaceName);
8278
8479
  if (project?.last_synced_version) {
@@ -8342,11 +8543,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
8342
8543
  workspacePath
8343
8544
  });
8344
8545
  const configFilePath = getConfigPath(workspacePath);
8345
- let isAlreadyConfigured = fs32.existsSync(configFilePath);
8546
+ let isAlreadyConfigured = fs33.existsSync(configFilePath);
8346
8547
  let currentStorageMode = null;
8347
8548
  if (isAlreadyConfigured) {
8348
8549
  try {
8349
- const configContent = fs32.readFileSync(configFilePath, "utf-8");
8550
+ const configContent = fs33.readFileSync(configFilePath, "utf-8");
8350
8551
  const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
8351
8552
  currentStorageMode = modeMatch?.[1] ?? null;
8352
8553
  } catch {
@@ -8363,7 +8564,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
8363
8564
  }
8364
8565
  }
8365
8566
  const localDataPath = getLocalWorkspacePath(workspacePath);
8366
- const hasLocalData = fs32.existsSync(localDataPath);
8567
+ const hasLocalData = fs33.existsSync(localDataPath);
8367
8568
  if (isAlreadyConfigured) {
8368
8569
  const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
8369
8570
  if (!continueToMenu) {