rrce-workflow 0.3.20 → 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/agent-core/prompts/_base.md +18 -24
- package/agent-core/prompts/design.md +19 -47
- package/agent-core/prompts/develop.md +10 -22
- package/agent-core/prompts/doctor.md +1 -1
- package/agent-core/prompts/documentation.md +1 -0
- package/agent-core/prompts/init.md +26 -25
- package/agent-core/prompts/orchestrator.md +36 -4
- package/agent-core/prompts/sync.md +9 -8
- package/dist/index.js +1518 -1289
- package/package.json +1 -1
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
|
|
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
|
|
1082
|
+
return path9.join(os2.homedir(), ".config", "opencode", "command");
|
|
1042
1083
|
}
|
|
1043
|
-
return
|
|
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 =
|
|
1058
|
-
if (
|
|
1059
|
-
return
|
|
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 (
|
|
1077
|
-
const entries =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
1144
|
+
if (!fs8.existsSync(opencodePath)) {
|
|
1140
1145
|
return;
|
|
1141
1146
|
}
|
|
1142
1147
|
try {
|
|
1143
|
-
const config = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
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
|
|
1213
|
-
const promptsDir =
|
|
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 =
|
|
1200
|
+
const baseName = path9.basename(prompt.filePath, ".md");
|
|
1219
1201
|
const agentId = `rrce_${baseName}`;
|
|
1220
1202
|
const promptFileName = `rrce-${baseName}.md`;
|
|
1221
|
-
const promptFilePath =
|
|
1203
|
+
const promptFilePath = path9.join(promptsDir, promptFileName);
|
|
1222
1204
|
const fullContent = baseProtocol ? `${baseProtocol}
|
|
1223
1205
|
${prompt.content}` : prompt.content;
|
|
1224
|
-
|
|
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,24 +1211,22 @@ ${prompt.content}` : prompt.content;
|
|
|
1229
1211
|
newAgents[agentId] = agentConfig;
|
|
1230
1212
|
}
|
|
1231
1213
|
updateOpenCodeConfig(newAgents);
|
|
1232
|
-
if (
|
|
1233
|
-
const config = JSON.parse(
|
|
1214
|
+
if (fs8.existsSync(openCodeConfigPath)) {
|
|
1215
|
+
const config = JSON.parse(fs8.readFileSync(openCodeConfigPath, "utf8"));
|
|
1234
1216
|
if (!config.agent) config.agent = {};
|
|
1235
|
-
|
|
1236
|
-
config.agent.plan.disable = true;
|
|
1237
|
-
fs7.writeFileSync(openCodeConfig, JSON.stringify(config, null, 2));
|
|
1217
|
+
fs8.writeFileSync(openCodeConfigPath, JSON.stringify(config, null, 2));
|
|
1238
1218
|
}
|
|
1239
1219
|
} catch (e) {
|
|
1240
1220
|
console.error("Failed to update global OpenCode config with agents:", e);
|
|
1241
1221
|
throw e;
|
|
1242
1222
|
}
|
|
1243
1223
|
} else {
|
|
1244
|
-
const opencodeBaseDir =
|
|
1224
|
+
const opencodeBaseDir = path9.join(dataPath, ".opencode", "agent");
|
|
1245
1225
|
ensureDir(opencodeBaseDir);
|
|
1246
1226
|
clearDirectory(opencodeBaseDir);
|
|
1247
1227
|
const baseProtocol = loadBaseProtocol();
|
|
1248
1228
|
for (const prompt of agentPrompts) {
|
|
1249
|
-
const baseName =
|
|
1229
|
+
const baseName = path9.basename(prompt.filePath, ".md");
|
|
1250
1230
|
const agentId = `rrce_${baseName}`;
|
|
1251
1231
|
const agentConfig = convertToOpenCodeAgent(prompt);
|
|
1252
1232
|
if (baseName === "develop") {
|
|
@@ -1261,16 +1241,53 @@ ${stringify({
|
|
|
1261
1241
|
tools: agentConfig.tools
|
|
1262
1242
|
})}---
|
|
1263
1243
|
${fullContent}`;
|
|
1264
|
-
|
|
1244
|
+
fs8.writeFileSync(path9.join(opencodeBaseDir, `${agentId}.md`), content);
|
|
1265
1245
|
}
|
|
1266
1246
|
}
|
|
1267
1247
|
generateOpenCodeCommands(prompts, mode, dataPath);
|
|
1268
1248
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
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"() {
|
|
1271
1278
|
"use strict";
|
|
1272
1279
|
init_paths();
|
|
1273
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();
|
|
1274
1291
|
}
|
|
1275
1292
|
});
|
|
1276
1293
|
|
|
@@ -1303,14 +1320,14 @@ var init_types = __esm({
|
|
|
1303
1320
|
});
|
|
1304
1321
|
|
|
1305
1322
|
// src/mcp/config-utils.ts
|
|
1306
|
-
import * as
|
|
1323
|
+
import * as path11 from "path";
|
|
1307
1324
|
function normalizeProjectPath(projectPath) {
|
|
1308
1325
|
let normalized = projectPath;
|
|
1309
1326
|
while (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) {
|
|
1310
1327
|
normalized = normalized.slice(0, -1);
|
|
1311
1328
|
}
|
|
1312
1329
|
if (normalized.endsWith(".rrce-workflow")) {
|
|
1313
|
-
return
|
|
1330
|
+
return path11.dirname(normalized);
|
|
1314
1331
|
}
|
|
1315
1332
|
return normalized;
|
|
1316
1333
|
}
|
|
@@ -1349,8 +1366,8 @@ __export(config_exports, {
|
|
|
1349
1366
|
saveMCPConfig: () => saveMCPConfig,
|
|
1350
1367
|
setProjectConfig: () => setProjectConfig
|
|
1351
1368
|
});
|
|
1352
|
-
import * as
|
|
1353
|
-
import * as
|
|
1369
|
+
import * as fs10 from "fs";
|
|
1370
|
+
import * as path12 from "path";
|
|
1354
1371
|
import YAML from "yaml";
|
|
1355
1372
|
function migrateConfig(config) {
|
|
1356
1373
|
let changed = false;
|
|
@@ -1369,15 +1386,15 @@ function migrateConfig(config) {
|
|
|
1369
1386
|
function getMCPConfigPath() {
|
|
1370
1387
|
const workspaceRoot = detectWorkspaceRoot();
|
|
1371
1388
|
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
1372
|
-
return
|
|
1389
|
+
return path12.join(rrceHome, "mcp.yaml");
|
|
1373
1390
|
}
|
|
1374
1391
|
function loadMCPConfig() {
|
|
1375
1392
|
const configPath = getMCPConfigPath();
|
|
1376
|
-
if (!
|
|
1393
|
+
if (!fs10.existsSync(configPath)) {
|
|
1377
1394
|
return { ...DEFAULT_MCP_CONFIG };
|
|
1378
1395
|
}
|
|
1379
1396
|
try {
|
|
1380
|
-
const content =
|
|
1397
|
+
const content = fs10.readFileSync(configPath, "utf-8");
|
|
1381
1398
|
let config = parseMCPConfig(content);
|
|
1382
1399
|
config = migrateConfig(config);
|
|
1383
1400
|
return config;
|
|
@@ -1389,9 +1406,9 @@ function ensureMCPGlobalPath() {
|
|
|
1389
1406
|
const workspaceRoot = detectWorkspaceRoot();
|
|
1390
1407
|
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
1391
1408
|
if (rrceHome.startsWith(".") || rrceHome.includes(".rrce-workflow/")) {
|
|
1392
|
-
const configPath =
|
|
1393
|
-
if (
|
|
1394
|
-
const content =
|
|
1409
|
+
const configPath = path12.join(workspaceRoot, ".rrce-workflow", "config.yaml");
|
|
1410
|
+
if (fs10.existsSync(configPath)) {
|
|
1411
|
+
const content = fs10.readFileSync(configPath, "utf-8");
|
|
1395
1412
|
const modeMatch = content.match(/mode:\s*(global|workspace)/);
|
|
1396
1413
|
if (modeMatch?.[1] === "workspace") {
|
|
1397
1414
|
return {
|
|
@@ -1409,12 +1426,12 @@ function ensureMCPGlobalPath() {
|
|
|
1409
1426
|
}
|
|
1410
1427
|
function saveMCPConfig(config) {
|
|
1411
1428
|
const configPath = getMCPConfigPath();
|
|
1412
|
-
const dir =
|
|
1413
|
-
if (!
|
|
1414
|
-
|
|
1429
|
+
const dir = path12.dirname(configPath);
|
|
1430
|
+
if (!fs10.existsSync(dir)) {
|
|
1431
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
1415
1432
|
}
|
|
1416
1433
|
const content = serializeMCPConfig(config);
|
|
1417
|
-
|
|
1434
|
+
fs10.writeFileSync(configPath, content);
|
|
1418
1435
|
}
|
|
1419
1436
|
function parseMCPConfig(content) {
|
|
1420
1437
|
try {
|
|
@@ -1503,16 +1520,16 @@ function getProjectPermissions(config, name, projectPath) {
|
|
|
1503
1520
|
}
|
|
1504
1521
|
function cleanStaleProjects(config) {
|
|
1505
1522
|
const rrceHome = getEffectiveGlobalPath();
|
|
1506
|
-
const globalWorkspacesDir =
|
|
1523
|
+
const globalWorkspacesDir = path12.join(rrceHome, "workspaces");
|
|
1507
1524
|
const validProjects = [];
|
|
1508
1525
|
const removed = [];
|
|
1509
1526
|
for (const project of config.projects) {
|
|
1510
1527
|
let exists = false;
|
|
1511
1528
|
if (project.path) {
|
|
1512
|
-
exists =
|
|
1529
|
+
exists = fs10.existsSync(project.path);
|
|
1513
1530
|
} else {
|
|
1514
|
-
const globalPath =
|
|
1515
|
-
exists =
|
|
1531
|
+
const globalPath = path12.join(globalWorkspacesDir, project.name);
|
|
1532
|
+
exists = fs10.existsSync(globalPath);
|
|
1516
1533
|
}
|
|
1517
1534
|
if (exists) {
|
|
1518
1535
|
validProjects.push(project);
|
|
@@ -1539,10 +1556,10 @@ var gitignore_exports = {};
|
|
|
1539
1556
|
__export(gitignore_exports, {
|
|
1540
1557
|
updateGitignore: () => updateGitignore
|
|
1541
1558
|
});
|
|
1542
|
-
import * as
|
|
1543
|
-
import * as
|
|
1559
|
+
import * as fs12 from "fs";
|
|
1560
|
+
import * as path14 from "path";
|
|
1544
1561
|
function updateGitignore(workspacePath, storageMode, tools) {
|
|
1545
|
-
const gitignorePath =
|
|
1562
|
+
const gitignorePath = path14.join(workspacePath, ".gitignore");
|
|
1546
1563
|
const entries = [];
|
|
1547
1564
|
if (storageMode === "workspace") {
|
|
1548
1565
|
entries.push(".rrce-workflow/");
|
|
@@ -1561,8 +1578,8 @@ function updateGitignore(workspacePath, storageMode, tools) {
|
|
|
1561
1578
|
return false;
|
|
1562
1579
|
}
|
|
1563
1580
|
let existingContent = "";
|
|
1564
|
-
if (
|
|
1565
|
-
existingContent =
|
|
1581
|
+
if (fs12.existsSync(gitignorePath)) {
|
|
1582
|
+
existingContent = fs12.readFileSync(gitignorePath, "utf-8");
|
|
1566
1583
|
}
|
|
1567
1584
|
const sectionMarker = "# RRCE-Workflow Generated";
|
|
1568
1585
|
if (existingContent.includes(sectionMarker)) {
|
|
@@ -1573,7 +1590,7 @@ ${sectionMarker}
|
|
|
1573
1590
|
${entries.join("\n")}
|
|
1574
1591
|
`;
|
|
1575
1592
|
const updatedContent = existingContent.trimEnd() + newSection;
|
|
1576
|
-
|
|
1593
|
+
fs12.writeFileSync(gitignorePath, updatedContent);
|
|
1577
1594
|
return true;
|
|
1578
1595
|
}
|
|
1579
1596
|
var init_gitignore = __esm({
|
|
@@ -1583,12 +1600,12 @@ var init_gitignore = __esm({
|
|
|
1583
1600
|
});
|
|
1584
1601
|
|
|
1585
1602
|
// src/mcp/logger.ts
|
|
1586
|
-
import * as
|
|
1587
|
-
import * as
|
|
1603
|
+
import * as fs13 from "fs";
|
|
1604
|
+
import * as path15 from "path";
|
|
1588
1605
|
function getLogFilePath() {
|
|
1589
1606
|
const workspaceRoot = detectWorkspaceRoot();
|
|
1590
1607
|
const rrceHome = getEffectiveRRCEHome(workspaceRoot);
|
|
1591
|
-
return
|
|
1608
|
+
return path15.join(rrceHome, "mcp-server.log");
|
|
1592
1609
|
}
|
|
1593
1610
|
var Logger, logger;
|
|
1594
1611
|
var init_logger = __esm({
|
|
@@ -1619,11 +1636,11 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
1619
1636
|
}
|
|
1620
1637
|
logMessage += "\n";
|
|
1621
1638
|
try {
|
|
1622
|
-
const dir =
|
|
1623
|
-
if (!
|
|
1624
|
-
|
|
1639
|
+
const dir = path15.dirname(this.logPath);
|
|
1640
|
+
if (!fs13.existsSync(dir)) {
|
|
1641
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
1625
1642
|
}
|
|
1626
|
-
|
|
1643
|
+
fs13.appendFileSync(this.logPath, logMessage);
|
|
1627
1644
|
} catch (e) {
|
|
1628
1645
|
console.error(`[Logger Failure] Could not write to ${this.logPath}`, e);
|
|
1629
1646
|
console.error(logMessage);
|
|
@@ -1739,18 +1756,18 @@ var init_constants = __esm({
|
|
|
1739
1756
|
});
|
|
1740
1757
|
|
|
1741
1758
|
// src/mcp/resources/utils.ts
|
|
1742
|
-
import * as
|
|
1743
|
-
import * as
|
|
1759
|
+
import * as fs14 from "fs";
|
|
1760
|
+
import * as path16 from "path";
|
|
1744
1761
|
import ignore from "ignore";
|
|
1745
1762
|
function estimateTokens(text2) {
|
|
1746
1763
|
return Math.ceil(text2.length / 4);
|
|
1747
1764
|
}
|
|
1748
1765
|
function getScanContext(project, scanRoot) {
|
|
1749
|
-
const gitignorePath =
|
|
1750
|
-
const ig =
|
|
1766
|
+
const gitignorePath = path16.join(scanRoot, ".gitignore");
|
|
1767
|
+
const ig = fs14.existsSync(gitignorePath) ? ignore().add(fs14.readFileSync(gitignorePath, "utf-8")) : null;
|
|
1751
1768
|
const toPosixRelativePath = (absolutePath) => {
|
|
1752
|
-
const rel =
|
|
1753
|
-
return rel.split(
|
|
1769
|
+
const rel = path16.relative(scanRoot, absolutePath);
|
|
1770
|
+
return rel.split(path16.sep).join("/");
|
|
1754
1771
|
};
|
|
1755
1772
|
const isUnderGitDir = (absolutePath) => {
|
|
1756
1773
|
const rel = toPosixRelativePath(absolutePath);
|
|
@@ -1762,7 +1779,7 @@ function getScanContext(project, scanRoot) {
|
|
|
1762
1779
|
return ig.ignores(isDir ? `${rel}/` : rel);
|
|
1763
1780
|
};
|
|
1764
1781
|
const shouldSkipEntryDir = (absolutePath) => {
|
|
1765
|
-
const dirName =
|
|
1782
|
+
const dirName = path16.basename(absolutePath);
|
|
1766
1783
|
if (dirName === ".git") return true;
|
|
1767
1784
|
if (SKIP_DIRS.includes(dirName)) return true;
|
|
1768
1785
|
if (isIgnoredByGitignore(absolutePath, true)) return true;
|
|
@@ -1783,7 +1800,7 @@ var init_utils2 = __esm({
|
|
|
1783
1800
|
});
|
|
1784
1801
|
|
|
1785
1802
|
// src/mcp/resources/paths.ts
|
|
1786
|
-
import * as
|
|
1803
|
+
import * as fs15 from "fs";
|
|
1787
1804
|
function resolveProjectPaths(project, pathInput) {
|
|
1788
1805
|
const config = loadMCPConfig();
|
|
1789
1806
|
let workspaceRoot = pathInput;
|
|
@@ -1811,8 +1828,8 @@ function resolveProjectPaths(project, pathInput) {
|
|
|
1811
1828
|
mode = "global";
|
|
1812
1829
|
} else {
|
|
1813
1830
|
mode = "workspace";
|
|
1814
|
-
if (
|
|
1815
|
-
const content =
|
|
1831
|
+
if (fs15.existsSync(configFilePath)) {
|
|
1832
|
+
const content = fs15.readFileSync(configFilePath, "utf-8");
|
|
1816
1833
|
if (content.includes("mode: global")) mode = "global";
|
|
1817
1834
|
if (content.includes("mode: workspace")) mode = "workspace";
|
|
1818
1835
|
}
|
|
@@ -1840,8 +1857,8 @@ var init_paths2 = __esm({
|
|
|
1840
1857
|
});
|
|
1841
1858
|
|
|
1842
1859
|
// src/mcp/resources/projects.ts
|
|
1843
|
-
import * as
|
|
1844
|
-
import * as
|
|
1860
|
+
import * as fs16 from "fs";
|
|
1861
|
+
import * as path17 from "path";
|
|
1845
1862
|
function getExposedProjects() {
|
|
1846
1863
|
const config = loadMCPConfig();
|
|
1847
1864
|
const knownProjects = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
@@ -1850,10 +1867,10 @@ function getExposedProjects() {
|
|
|
1850
1867
|
const potentialProjects = [...allProjects];
|
|
1851
1868
|
if (activeProject) {
|
|
1852
1869
|
let cfgContent = null;
|
|
1853
|
-
if (
|
|
1854
|
-
cfgContent =
|
|
1855
|
-
} else if (
|
|
1856
|
-
cfgContent =
|
|
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");
|
|
1857
1874
|
}
|
|
1858
1875
|
if (cfgContent) {
|
|
1859
1876
|
if (cfgContent.includes("linked_projects:")) {
|
|
@@ -1911,15 +1928,15 @@ function getProjectContext(projectName) {
|
|
|
1911
1928
|
if (!project.knowledgePath) {
|
|
1912
1929
|
return null;
|
|
1913
1930
|
}
|
|
1914
|
-
const contextPath =
|
|
1915
|
-
if (!
|
|
1931
|
+
const contextPath = path17.join(project.knowledgePath, "project-context.md");
|
|
1932
|
+
if (!fs16.existsSync(contextPath)) {
|
|
1916
1933
|
return null;
|
|
1917
1934
|
}
|
|
1918
|
-
return
|
|
1935
|
+
return fs16.readFileSync(contextPath, "utf-8");
|
|
1919
1936
|
}
|
|
1920
1937
|
function getCodeIndexPath(project) {
|
|
1921
1938
|
const scanRoot = project.path || project.dataPath;
|
|
1922
|
-
return
|
|
1939
|
+
return path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
|
|
1923
1940
|
}
|
|
1924
1941
|
var init_projects = __esm({
|
|
1925
1942
|
"src/mcp/resources/projects.ts"() {
|
|
@@ -1932,8 +1949,8 @@ var init_projects = __esm({
|
|
|
1932
1949
|
});
|
|
1933
1950
|
|
|
1934
1951
|
// src/mcp/resources/tasks.ts
|
|
1935
|
-
import * as
|
|
1936
|
-
import * as
|
|
1952
|
+
import * as fs17 from "fs";
|
|
1953
|
+
import * as path18 from "path";
|
|
1937
1954
|
import * as os3 from "os";
|
|
1938
1955
|
import * as crypto from "crypto";
|
|
1939
1956
|
function getProjectTasks(projectName) {
|
|
@@ -1947,18 +1964,18 @@ function getProjectTasks(projectName) {
|
|
|
1947
1964
|
if (!permissions.tasks) {
|
|
1948
1965
|
return [];
|
|
1949
1966
|
}
|
|
1950
|
-
if (!project.tasksPath || !
|
|
1967
|
+
if (!project.tasksPath || !fs17.existsSync(project.tasksPath)) {
|
|
1951
1968
|
return [];
|
|
1952
1969
|
}
|
|
1953
1970
|
const tasks = [];
|
|
1954
1971
|
try {
|
|
1955
|
-
const taskDirs =
|
|
1972
|
+
const taskDirs = fs17.readdirSync(project.tasksPath, { withFileTypes: true });
|
|
1956
1973
|
for (const dir of taskDirs) {
|
|
1957
1974
|
if (!dir.isDirectory()) continue;
|
|
1958
|
-
const metaPath =
|
|
1959
|
-
if (
|
|
1975
|
+
const metaPath = path18.join(project.tasksPath, dir.name, "meta.json");
|
|
1976
|
+
if (fs17.existsSync(metaPath)) {
|
|
1960
1977
|
try {
|
|
1961
|
-
const meta = JSON.parse(
|
|
1978
|
+
const meta = JSON.parse(fs17.readFileSync(metaPath, "utf-8"));
|
|
1962
1979
|
tasks.push(meta);
|
|
1963
1980
|
} catch (err) {
|
|
1964
1981
|
logger.error(`[getProjectTasks] Failed to parse meta.json in ${dir.name}`, err);
|
|
@@ -1975,10 +1992,10 @@ function getTask(projectName, taskSlug) {
|
|
|
1975
1992
|
const projects = projectService.scan();
|
|
1976
1993
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1977
1994
|
if (!project || !project.tasksPath) return null;
|
|
1978
|
-
const metaPath =
|
|
1979
|
-
if (!
|
|
1995
|
+
const metaPath = path18.join(project.tasksPath, taskSlug, "meta.json");
|
|
1996
|
+
if (!fs17.existsSync(metaPath)) return null;
|
|
1980
1997
|
try {
|
|
1981
|
-
return JSON.parse(
|
|
1998
|
+
return JSON.parse(fs17.readFileSync(metaPath, "utf-8"));
|
|
1982
1999
|
} catch (err) {
|
|
1983
2000
|
logger.error(`[getTask] Failed to parse meta.json for task ${taskSlug}`, err);
|
|
1984
2001
|
return null;
|
|
@@ -1991,26 +2008,26 @@ async function createTask(projectName, taskSlug, taskData) {
|
|
|
1991
2008
|
if (!project || !project.tasksPath) {
|
|
1992
2009
|
throw new Error(`Project '${projectName}' not found or not configured with a tasks path.`);
|
|
1993
2010
|
}
|
|
1994
|
-
const taskDir =
|
|
1995
|
-
if (
|
|
2011
|
+
const taskDir = path18.join(project.tasksPath, taskSlug);
|
|
2012
|
+
if (fs17.existsSync(taskDir)) {
|
|
1996
2013
|
throw new Error(`Task with slug '${taskSlug}' already exists.`);
|
|
1997
2014
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
const rrceHome = process.env.RRCE_HOME ||
|
|
2004
|
-
const templatePath =
|
|
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");
|
|
2005
2022
|
let meta = {
|
|
2006
2023
|
task_id: crypto.randomUUID(),
|
|
2007
2024
|
task_slug: taskSlug,
|
|
2008
2025
|
status: "draft",
|
|
2009
2026
|
agents: {}
|
|
2010
2027
|
};
|
|
2011
|
-
if (
|
|
2028
|
+
if (fs17.existsSync(templatePath)) {
|
|
2012
2029
|
try {
|
|
2013
|
-
const template = JSON.parse(
|
|
2030
|
+
const template = JSON.parse(fs17.readFileSync(templatePath, "utf-8"));
|
|
2014
2031
|
meta = { ...template, ...meta };
|
|
2015
2032
|
} catch (e) {
|
|
2016
2033
|
logger.error("Failed to load meta template", e);
|
|
@@ -2024,8 +2041,8 @@ async function createTask(projectName, taskSlug, taskData) {
|
|
|
2024
2041
|
hash: project.name
|
|
2025
2042
|
};
|
|
2026
2043
|
Object.assign(meta, taskData);
|
|
2027
|
-
const metaPath =
|
|
2028
|
-
|
|
2044
|
+
const metaPath = path18.join(taskDir, "meta.json");
|
|
2045
|
+
fs17.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
2029
2046
|
return meta;
|
|
2030
2047
|
}
|
|
2031
2048
|
async function updateTask(projectName, taskSlug, taskData) {
|
|
@@ -2044,8 +2061,8 @@ async function updateTask(projectName, taskSlug, taskData) {
|
|
|
2044
2061
|
const projects = projectService.scan();
|
|
2045
2062
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2046
2063
|
if (!project || !project.tasksPath) return null;
|
|
2047
|
-
const metaPath =
|
|
2048
|
-
|
|
2064
|
+
const metaPath = path18.join(project.tasksPath, taskSlug, "meta.json");
|
|
2065
|
+
fs17.writeFileSync(metaPath, JSON.stringify(updatedMeta, null, 2));
|
|
2049
2066
|
return updatedMeta;
|
|
2050
2067
|
}
|
|
2051
2068
|
function deleteTask(projectName, taskSlug) {
|
|
@@ -2053,12 +2070,12 @@ function deleteTask(projectName, taskSlug) {
|
|
|
2053
2070
|
const projects = projectService.scan();
|
|
2054
2071
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2055
2072
|
if (!project || !project.tasksPath) return false;
|
|
2056
|
-
const taskDir =
|
|
2057
|
-
if (!
|
|
2058
|
-
if (
|
|
2059
|
-
|
|
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 });
|
|
2060
2077
|
} else {
|
|
2061
|
-
|
|
2078
|
+
fs17.rmdirSync(taskDir, { recursive: true });
|
|
2062
2079
|
}
|
|
2063
2080
|
return true;
|
|
2064
2081
|
}
|
|
@@ -2072,8 +2089,8 @@ var init_tasks = __esm({
|
|
|
2072
2089
|
});
|
|
2073
2090
|
|
|
2074
2091
|
// src/mcp/services/rag.ts
|
|
2075
|
-
import * as
|
|
2076
|
-
import * as
|
|
2092
|
+
import * as fs18 from "fs";
|
|
2093
|
+
import * as path19 from "path";
|
|
2077
2094
|
var INDEX_VERSION, DEFAULT_MODEL, RAGService;
|
|
2078
2095
|
var init_rag = __esm({
|
|
2079
2096
|
"src/mcp/services/rag.ts"() {
|
|
@@ -2127,9 +2144,9 @@ var init_rag = __esm({
|
|
|
2127
2144
|
*/
|
|
2128
2145
|
loadIndex() {
|
|
2129
2146
|
if (this.index) return;
|
|
2130
|
-
if (
|
|
2147
|
+
if (fs18.existsSync(this.indexPath)) {
|
|
2131
2148
|
try {
|
|
2132
|
-
const data =
|
|
2149
|
+
const data = fs18.readFileSync(this.indexPath, "utf-8");
|
|
2133
2150
|
this.index = JSON.parse(data);
|
|
2134
2151
|
logger.info(`[RAG] Loaded index from ${this.indexPath} with ${this.index?.chunks.length} chunks.`);
|
|
2135
2152
|
} catch (error) {
|
|
@@ -2155,11 +2172,11 @@ var init_rag = __esm({
|
|
|
2155
2172
|
saveIndex() {
|
|
2156
2173
|
if (!this.index) return;
|
|
2157
2174
|
try {
|
|
2158
|
-
const dir =
|
|
2159
|
-
if (!
|
|
2160
|
-
|
|
2175
|
+
const dir = path19.dirname(this.indexPath);
|
|
2176
|
+
if (!fs18.existsSync(dir)) {
|
|
2177
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
2161
2178
|
}
|
|
2162
|
-
|
|
2179
|
+
fs18.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2));
|
|
2163
2180
|
logger.info(`[RAG] Saved index to ${this.indexPath} with ${this.index.chunks.length} chunks.`);
|
|
2164
2181
|
} catch (error) {
|
|
2165
2182
|
logger.error(`[RAG] Failed to save index to ${this.indexPath}`, error);
|
|
@@ -2170,13 +2187,13 @@ var init_rag = __esm({
|
|
|
2170
2187
|
*/
|
|
2171
2188
|
saveIndexAtomic() {
|
|
2172
2189
|
if (!this.index) return;
|
|
2173
|
-
const dir =
|
|
2174
|
-
if (!
|
|
2175
|
-
|
|
2190
|
+
const dir = path19.dirname(this.indexPath);
|
|
2191
|
+
if (!fs18.existsSync(dir)) {
|
|
2192
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
2176
2193
|
}
|
|
2177
2194
|
const tmpPath = `${this.indexPath}.tmp`;
|
|
2178
|
-
|
|
2179
|
-
|
|
2195
|
+
fs18.writeFileSync(tmpPath, JSON.stringify(this.index, null, 2));
|
|
2196
|
+
fs18.renameSync(tmpPath, this.indexPath);
|
|
2180
2197
|
}
|
|
2181
2198
|
/**
|
|
2182
2199
|
* Save index only if enough time passed since last save
|
|
@@ -2709,10 +2726,10 @@ var init_context_extractor = __esm({
|
|
|
2709
2726
|
});
|
|
2710
2727
|
|
|
2711
2728
|
// src/mcp/services/dependency-graph.ts
|
|
2712
|
-
import * as
|
|
2713
|
-
import * as
|
|
2729
|
+
import * as fs19 from "fs";
|
|
2730
|
+
import * as path20 from "path";
|
|
2714
2731
|
function parseImports(filePath, content) {
|
|
2715
|
-
const ext =
|
|
2732
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
2716
2733
|
const language = getLanguageFromExtension(ext);
|
|
2717
2734
|
const edges = [];
|
|
2718
2735
|
const patterns = IMPORT_PATTERNS[language] || IMPORT_PATTERNS.javascript || [];
|
|
@@ -2742,20 +2759,20 @@ function parseImports(filePath, content) {
|
|
|
2742
2759
|
return edges;
|
|
2743
2760
|
}
|
|
2744
2761
|
function resolveImport(fromFile, importPath, language) {
|
|
2745
|
-
const fromDir =
|
|
2762
|
+
const fromDir = path20.dirname(fromFile);
|
|
2746
2763
|
if (importPath.startsWith(".")) {
|
|
2747
|
-
const candidates = generateCandidates(
|
|
2764
|
+
const candidates = generateCandidates(path20.resolve(fromDir, importPath), language);
|
|
2748
2765
|
for (const candidate of candidates) {
|
|
2749
|
-
if (
|
|
2766
|
+
if (fs19.existsSync(candidate)) {
|
|
2750
2767
|
return { path: candidate, isResolved: true };
|
|
2751
2768
|
}
|
|
2752
2769
|
}
|
|
2753
|
-
return { path:
|
|
2770
|
+
return { path: path20.resolve(fromDir, importPath), isResolved: false };
|
|
2754
2771
|
}
|
|
2755
2772
|
if (importPath.startsWith("/")) {
|
|
2756
2773
|
const candidates = generateCandidates(importPath, language);
|
|
2757
2774
|
for (const candidate of candidates) {
|
|
2758
|
-
if (
|
|
2775
|
+
if (fs19.existsSync(candidate)) {
|
|
2759
2776
|
return { path: candidate, isResolved: true };
|
|
2760
2777
|
}
|
|
2761
2778
|
}
|
|
@@ -2765,7 +2782,7 @@ function resolveImport(fromFile, importPath, language) {
|
|
|
2765
2782
|
}
|
|
2766
2783
|
function generateCandidates(basePath, language) {
|
|
2767
2784
|
const candidates = [];
|
|
2768
|
-
if (
|
|
2785
|
+
if (path20.extname(basePath)) {
|
|
2769
2786
|
candidates.push(basePath);
|
|
2770
2787
|
}
|
|
2771
2788
|
switch (language) {
|
|
@@ -2880,18 +2897,18 @@ async function scanProjectDependencies(projectRoot, options = {}) {
|
|
|
2880
2897
|
const files = [];
|
|
2881
2898
|
function scanDir(dir) {
|
|
2882
2899
|
try {
|
|
2883
|
-
const entries =
|
|
2900
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
2884
2901
|
for (const entry of entries) {
|
|
2885
|
-
const fullPath =
|
|
2902
|
+
const fullPath = path20.join(dir, entry.name);
|
|
2886
2903
|
if (entry.isDirectory()) {
|
|
2887
2904
|
if (!skipDirs.includes(entry.name) && !entry.name.startsWith(".")) {
|
|
2888
2905
|
scanDir(fullPath);
|
|
2889
2906
|
}
|
|
2890
2907
|
} else if (entry.isFile()) {
|
|
2891
|
-
const ext =
|
|
2908
|
+
const ext = path20.extname(entry.name).toLowerCase();
|
|
2892
2909
|
if (extensions.includes(ext)) {
|
|
2893
2910
|
try {
|
|
2894
|
-
const content =
|
|
2911
|
+
const content = fs19.readFileSync(fullPath, "utf-8");
|
|
2895
2912
|
files.push({ path: fullPath, content });
|
|
2896
2913
|
} catch {
|
|
2897
2914
|
}
|
|
@@ -3188,9 +3205,53 @@ var init_symbol_extractor = __esm({
|
|
|
3188
3205
|
}
|
|
3189
3206
|
});
|
|
3190
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
|
+
|
|
3191
3252
|
// src/mcp/resources/search.ts
|
|
3192
|
-
import * as
|
|
3193
|
-
import * as
|
|
3253
|
+
import * as fs20 from "fs";
|
|
3254
|
+
import * as path21 from "path";
|
|
3194
3255
|
async function searchCode(query, projectFilter, limit = 10, options) {
|
|
3195
3256
|
const config = loadMCPConfig();
|
|
3196
3257
|
const projects = getExposedProjects();
|
|
@@ -3199,8 +3260,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
3199
3260
|
if (projectFilter && project.name !== projectFilter) continue;
|
|
3200
3261
|
const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
|
|
3201
3262
|
if (!permissions.knowledge || !project.knowledgePath) continue;
|
|
3202
|
-
const
|
|
3203
|
-
const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
3263
|
+
const { indexingInProgress, advisoryMessage } = getSearchAdvisory(project.name);
|
|
3204
3264
|
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
|
|
3205
3265
|
const useRAG = projConfig?.semanticSearch?.enabled;
|
|
3206
3266
|
if (!useRAG) {
|
|
@@ -3209,7 +3269,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
3209
3269
|
}
|
|
3210
3270
|
try {
|
|
3211
3271
|
const codeIndexPath = getCodeIndexPath(project);
|
|
3212
|
-
if (!
|
|
3272
|
+
if (!fs20.existsSync(codeIndexPath)) {
|
|
3213
3273
|
logger.debug(`[searchCode] Code index not found for project '${project.name}'`);
|
|
3214
3274
|
continue;
|
|
3215
3275
|
}
|
|
@@ -3219,15 +3279,13 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
3219
3279
|
const codeChunk = r;
|
|
3220
3280
|
results.push({
|
|
3221
3281
|
project: project.name,
|
|
3222
|
-
file:
|
|
3282
|
+
file: path21.relative(project.sourcePath || project.path || "", codeChunk.filePath),
|
|
3223
3283
|
snippet: codeChunk.content,
|
|
3224
3284
|
lineStart: codeChunk.lineStart ?? 1,
|
|
3225
3285
|
lineEnd: codeChunk.lineEnd ?? 1,
|
|
3226
3286
|
context: codeChunk.context,
|
|
3227
3287
|
language: codeChunk.language,
|
|
3228
|
-
score: codeChunk.score
|
|
3229
|
-
indexingInProgress: indexingInProgress2 || void 0,
|
|
3230
|
-
advisoryMessage: advisoryMessage2
|
|
3288
|
+
score: codeChunk.score
|
|
3231
3289
|
});
|
|
3232
3290
|
}
|
|
3233
3291
|
} catch (e) {
|
|
@@ -3239,49 +3297,32 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
3239
3297
|
if (options?.min_score !== void 0 && options.min_score > 0) {
|
|
3240
3298
|
filteredResults = results.filter((r) => r.score >= options.min_score);
|
|
3241
3299
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
const resultTokens = estimateTokens(result.snippet + (result.context || ""));
|
|
3249
|
-
if (tokenCount + resultTokens > options.max_tokens) {
|
|
3250
|
-
truncated = true;
|
|
3251
|
-
break;
|
|
3252
|
-
}
|
|
3253
|
-
budgetedResults.push(result);
|
|
3254
|
-
tokenCount += resultTokens;
|
|
3255
|
-
}
|
|
3256
|
-
limitedResults = budgetedResults;
|
|
3257
|
-
} else {
|
|
3258
|
-
tokenCount = limitedResults.reduce((sum, r) => sum + estimateTokens(r.snippet + (r.context || "")), 0);
|
|
3259
|
-
}
|
|
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
|
+
);
|
|
3260
3306
|
let indexAgeSeconds;
|
|
3261
3307
|
let lastIndexedAt;
|
|
3262
|
-
let
|
|
3263
|
-
let
|
|
3308
|
+
let indexingInProgressGlobal;
|
|
3309
|
+
let advisoryMessageGlobal;
|
|
3264
3310
|
if (projectFilter) {
|
|
3265
|
-
const
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3272
|
-
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3273
|
-
}
|
|
3274
|
-
}
|
|
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;
|
|
3275
3317
|
}
|
|
3276
|
-
const cleanResults = limitedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3277
3318
|
return {
|
|
3278
|
-
results:
|
|
3319
|
+
results: budgetedResults,
|
|
3279
3320
|
token_count: tokenCount,
|
|
3280
3321
|
truncated,
|
|
3281
3322
|
index_age_seconds: indexAgeSeconds,
|
|
3282
3323
|
last_indexed_at: lastIndexedAt,
|
|
3283
|
-
indexingInProgress,
|
|
3284
|
-
advisoryMessage
|
|
3324
|
+
indexingInProgress: indexingInProgressGlobal,
|
|
3325
|
+
advisoryMessage: advisoryMessageGlobal
|
|
3285
3326
|
};
|
|
3286
3327
|
}
|
|
3287
3328
|
async function searchKnowledge(query, projectFilter, options) {
|
|
@@ -3293,24 +3334,20 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3293
3334
|
if (projectFilter && project.name !== projectFilter) continue;
|
|
3294
3335
|
const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
|
|
3295
3336
|
if (!permissions.knowledge || !project.knowledgePath) continue;
|
|
3296
|
-
const
|
|
3297
|
-
const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
3337
|
+
const { indexingInProgress, advisoryMessage } = getSearchAdvisory(project.name);
|
|
3298
3338
|
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
|
|
3299
3339
|
const useRAG = projConfig?.semanticSearch?.enabled;
|
|
3300
3340
|
if (useRAG) {
|
|
3301
|
-
logger.info(`[RAG] Using semantic search for project '${project.name}'`);
|
|
3302
3341
|
try {
|
|
3303
|
-
const indexPath =
|
|
3342
|
+
const indexPath = path21.join(project.knowledgePath, "embeddings.json");
|
|
3304
3343
|
const rag = new RAGService(indexPath, projConfig?.semanticSearch?.model);
|
|
3305
3344
|
const ragResults = await rag.search(query, 5);
|
|
3306
3345
|
for (const r of ragResults) {
|
|
3307
3346
|
results.push({
|
|
3308
3347
|
project: project.name,
|
|
3309
|
-
file:
|
|
3348
|
+
file: path21.relative(project.knowledgePath, r.filePath),
|
|
3310
3349
|
matches: [r.content],
|
|
3311
|
-
score: r.score
|
|
3312
|
-
indexingInProgress: indexingInProgress2 || void 0,
|
|
3313
|
-
advisoryMessage: advisoryMessage2
|
|
3350
|
+
score: r.score
|
|
3314
3351
|
});
|
|
3315
3352
|
}
|
|
3316
3353
|
continue;
|
|
@@ -3319,11 +3356,11 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3319
3356
|
}
|
|
3320
3357
|
}
|
|
3321
3358
|
try {
|
|
3322
|
-
const files =
|
|
3359
|
+
const files = fs20.readdirSync(project.knowledgePath);
|
|
3323
3360
|
for (const file of files) {
|
|
3324
3361
|
if (!file.endsWith(".md")) continue;
|
|
3325
|
-
const filePath =
|
|
3326
|
-
const content =
|
|
3362
|
+
const filePath = path21.join(project.knowledgePath, file);
|
|
3363
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
3327
3364
|
const lines = content.split("\n");
|
|
3328
3365
|
const matches = [];
|
|
3329
3366
|
for (const line of lines) {
|
|
@@ -3335,10 +3372,7 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3335
3372
|
results.push({
|
|
3336
3373
|
project: project.name,
|
|
3337
3374
|
file,
|
|
3338
|
-
matches: matches.slice(0, 5)
|
|
3339
|
-
// Limit to 5 matches per file
|
|
3340
|
-
indexingInProgress: indexingInProgress2 || void 0,
|
|
3341
|
-
advisoryMessage: advisoryMessage2
|
|
3375
|
+
matches: matches.slice(0, 5)
|
|
3342
3376
|
});
|
|
3343
3377
|
}
|
|
3344
3378
|
}
|
|
@@ -3351,48 +3385,31 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3351
3385
|
filteredResults = results.filter((r) => (r.score ?? 1) >= options.min_score);
|
|
3352
3386
|
}
|
|
3353
3387
|
filteredResults.sort((a, b) => (b.score ?? 1) - (a.score ?? 1));
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
for (const result of filteredResults) {
|
|
3360
|
-
const resultTokens = estimateTokens(result.matches.join("\n"));
|
|
3361
|
-
if (tokenCount + resultTokens > options.max_tokens) {
|
|
3362
|
-
truncated = true;
|
|
3363
|
-
break;
|
|
3364
|
-
}
|
|
3365
|
-
budgetedResults.push(result);
|
|
3366
|
-
tokenCount += resultTokens;
|
|
3367
|
-
}
|
|
3368
|
-
} else {
|
|
3369
|
-
tokenCount = filteredResults.reduce((sum, r) => sum + estimateTokens(r.matches.join("\n")), 0);
|
|
3370
|
-
}
|
|
3388
|
+
const { budgetedResults, truncated, tokenCount } = applyTokenBudget(
|
|
3389
|
+
filteredResults,
|
|
3390
|
+
options?.max_tokens,
|
|
3391
|
+
(r) => r.matches.join("\n")
|
|
3392
|
+
);
|
|
3371
3393
|
let indexAgeSeconds;
|
|
3372
3394
|
let lastIndexedAt;
|
|
3373
|
-
let
|
|
3374
|
-
let
|
|
3395
|
+
let indexingInProgressGlobal;
|
|
3396
|
+
let advisoryMessageGlobal;
|
|
3375
3397
|
if (projectFilter) {
|
|
3376
|
-
const
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3383
|
-
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3384
|
-
}
|
|
3385
|
-
}
|
|
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;
|
|
3386
3404
|
}
|
|
3387
|
-
const cleanResults = budgetedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3388
3405
|
return {
|
|
3389
|
-
results:
|
|
3406
|
+
results: budgetedResults,
|
|
3390
3407
|
token_count: tokenCount,
|
|
3391
3408
|
truncated,
|
|
3392
3409
|
index_age_seconds: indexAgeSeconds,
|
|
3393
3410
|
last_indexed_at: lastIndexedAt,
|
|
3394
|
-
indexingInProgress,
|
|
3395
|
-
advisoryMessage
|
|
3411
|
+
indexingInProgress: indexingInProgressGlobal,
|
|
3412
|
+
advisoryMessage: advisoryMessageGlobal
|
|
3396
3413
|
};
|
|
3397
3414
|
}
|
|
3398
3415
|
async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
@@ -3410,10 +3427,10 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3410
3427
|
}
|
|
3411
3428
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3412
3429
|
let absoluteFilePath = filePath;
|
|
3413
|
-
if (!
|
|
3414
|
-
absoluteFilePath =
|
|
3430
|
+
if (!path21.isAbsolute(filePath)) {
|
|
3431
|
+
absoluteFilePath = path21.resolve(projectRoot, filePath);
|
|
3415
3432
|
}
|
|
3416
|
-
if (!
|
|
3433
|
+
if (!fs20.existsSync(absoluteFilePath)) {
|
|
3417
3434
|
return {
|
|
3418
3435
|
success: false,
|
|
3419
3436
|
file: filePath,
|
|
@@ -3430,13 +3447,13 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3430
3447
|
depth: options.depth ?? 1
|
|
3431
3448
|
});
|
|
3432
3449
|
const relationships = related.map((r) => ({
|
|
3433
|
-
file:
|
|
3450
|
+
file: path21.relative(projectRoot, r.file),
|
|
3434
3451
|
relationship: r.relationship,
|
|
3435
3452
|
importPath: r.importPath
|
|
3436
3453
|
}));
|
|
3437
3454
|
return {
|
|
3438
3455
|
success: true,
|
|
3439
|
-
file:
|
|
3456
|
+
file: path21.relative(projectRoot, absoluteFilePath),
|
|
3440
3457
|
project: projectName,
|
|
3441
3458
|
relationships
|
|
3442
3459
|
};
|
|
@@ -3464,7 +3481,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3464
3481
|
};
|
|
3465
3482
|
}
|
|
3466
3483
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3467
|
-
if (!
|
|
3484
|
+
if (!fs20.existsSync(projectRoot)) {
|
|
3468
3485
|
return {
|
|
3469
3486
|
success: false,
|
|
3470
3487
|
project: projectName,
|
|
@@ -3475,14 +3492,14 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3475
3492
|
try {
|
|
3476
3493
|
const codeFiles = [];
|
|
3477
3494
|
const scanDir = (dir) => {
|
|
3478
|
-
const entries =
|
|
3495
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
3479
3496
|
for (const entry of entries) {
|
|
3480
|
-
const fullPath =
|
|
3497
|
+
const fullPath = path21.join(dir, entry.name);
|
|
3481
3498
|
if (entry.isDirectory()) {
|
|
3482
3499
|
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
3483
3500
|
scanDir(fullPath);
|
|
3484
3501
|
} else if (entry.isFile()) {
|
|
3485
|
-
const ext =
|
|
3502
|
+
const ext = path21.extname(entry.name).toLowerCase();
|
|
3486
3503
|
if (CODE_EXTENSIONS.includes(ext)) {
|
|
3487
3504
|
codeFiles.push(fullPath);
|
|
3488
3505
|
}
|
|
@@ -3493,7 +3510,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3493
3510
|
const symbolResults = [];
|
|
3494
3511
|
for (const file of codeFiles.slice(0, 500)) {
|
|
3495
3512
|
try {
|
|
3496
|
-
const content =
|
|
3513
|
+
const content = fs20.readFileSync(file, "utf-8");
|
|
3497
3514
|
const result = extractSymbols(content, file);
|
|
3498
3515
|
symbolResults.push(result);
|
|
3499
3516
|
} catch (e) {
|
|
@@ -3508,7 +3525,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3508
3525
|
const results = matches.map((m) => ({
|
|
3509
3526
|
name: m.name,
|
|
3510
3527
|
type: m.type,
|
|
3511
|
-
file:
|
|
3528
|
+
file: path21.relative(projectRoot, m.file),
|
|
3512
3529
|
line: m.line,
|
|
3513
3530
|
signature: m.signature,
|
|
3514
3531
|
exported: m.exported,
|
|
@@ -3541,24 +3558,24 @@ async function getFileSummary(filePath, projectName) {
|
|
|
3541
3558
|
}
|
|
3542
3559
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3543
3560
|
let absolutePath = filePath;
|
|
3544
|
-
if (!
|
|
3545
|
-
absolutePath =
|
|
3561
|
+
if (!path21.isAbsolute(filePath)) {
|
|
3562
|
+
absolutePath = path21.resolve(projectRoot, filePath);
|
|
3546
3563
|
}
|
|
3547
|
-
if (!
|
|
3564
|
+
if (!fs20.existsSync(absolutePath)) {
|
|
3548
3565
|
return {
|
|
3549
3566
|
success: false,
|
|
3550
3567
|
message: `File not found: ${filePath}`
|
|
3551
3568
|
};
|
|
3552
3569
|
}
|
|
3553
3570
|
try {
|
|
3554
|
-
const stat =
|
|
3555
|
-
const content =
|
|
3571
|
+
const stat = fs20.statSync(absolutePath);
|
|
3572
|
+
const content = fs20.readFileSync(absolutePath, "utf-8");
|
|
3556
3573
|
const lines = content.split("\n");
|
|
3557
3574
|
const symbolResult = extractSymbols(content, absolutePath);
|
|
3558
3575
|
return {
|
|
3559
3576
|
success: true,
|
|
3560
3577
|
summary: {
|
|
3561
|
-
path:
|
|
3578
|
+
path: path21.relative(projectRoot, absolutePath),
|
|
3562
3579
|
language: symbolResult.language,
|
|
3563
3580
|
lines: lines.length,
|
|
3564
3581
|
size_bytes: stat.size,
|
|
@@ -3594,47 +3611,164 @@ var init_search = __esm({
|
|
|
3594
3611
|
init_projects();
|
|
3595
3612
|
init_utils2();
|
|
3596
3613
|
init_constants();
|
|
3614
|
+
init_search_utils();
|
|
3597
3615
|
}
|
|
3598
3616
|
});
|
|
3599
3617
|
|
|
3600
|
-
// src/
|
|
3601
|
-
import * as
|
|
3602
|
-
import * as
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3618
|
+
// src/lib/drift-service.ts
|
|
3619
|
+
import * as fs21 from "fs";
|
|
3620
|
+
import * as path22 from "path";
|
|
3621
|
+
import * as crypto2 from "crypto";
|
|
3622
|
+
var DriftService;
|
|
3623
|
+
var init_drift_service = __esm({
|
|
3624
|
+
"src/lib/drift-service.ts"() {
|
|
3625
|
+
"use strict";
|
|
3626
|
+
DriftService = class {
|
|
3627
|
+
static CHECKSUM_FILENAME = ".rrce-checksums.json";
|
|
3628
|
+
static calculateHash(filePath) {
|
|
3629
|
+
const content = fs21.readFileSync(filePath);
|
|
3630
|
+
return crypto2.createHash("md5").update(content).digest("hex");
|
|
3631
|
+
}
|
|
3632
|
+
static getManifestPath(projectPath) {
|
|
3633
|
+
return path22.join(projectPath, this.CHECKSUM_FILENAME);
|
|
3634
|
+
}
|
|
3635
|
+
static loadManifest(projectPath) {
|
|
3636
|
+
const manifestPath = this.getManifestPath(projectPath);
|
|
3637
|
+
if (!fs21.existsSync(manifestPath)) {
|
|
3638
|
+
return {};
|
|
3639
|
+
}
|
|
3640
|
+
try {
|
|
3641
|
+
return JSON.parse(fs21.readFileSync(manifestPath, "utf8"));
|
|
3642
|
+
} catch (e) {
|
|
3643
|
+
return {};
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
static saveManifest(projectPath, manifest) {
|
|
3647
|
+
const manifestPath = this.getManifestPath(projectPath);
|
|
3648
|
+
fs21.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
3649
|
+
}
|
|
3650
|
+
/**
|
|
3651
|
+
* Generates a manifest for the current state of files in the project
|
|
3652
|
+
*/
|
|
3653
|
+
static generateManifest(projectPath, files) {
|
|
3654
|
+
const manifest = {};
|
|
3655
|
+
for (const file of files) {
|
|
3656
|
+
const fullPath = path22.join(projectPath, file);
|
|
3657
|
+
if (fs21.existsSync(fullPath)) {
|
|
3658
|
+
const stats = fs21.statSync(fullPath);
|
|
3659
|
+
manifest[file] = {
|
|
3660
|
+
hash: this.calculateHash(fullPath),
|
|
3661
|
+
mtime: stats.mtimeMs
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
return manifest;
|
|
3666
|
+
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Compares current files against the manifest to detect modifications and deletions
|
|
3669
|
+
*/
|
|
3670
|
+
static detectModifiedFiles(projectPath) {
|
|
3671
|
+
const manifest = this.loadManifest(projectPath);
|
|
3672
|
+
const modified = [];
|
|
3673
|
+
const deleted = [];
|
|
3674
|
+
for (const [relPath, entry] of Object.entries(manifest)) {
|
|
3675
|
+
const fullPath = path22.join(projectPath, relPath);
|
|
3676
|
+
if (!fs21.existsSync(fullPath)) {
|
|
3677
|
+
deleted.push(relPath);
|
|
3678
|
+
continue;
|
|
3679
|
+
}
|
|
3680
|
+
const stats = fs21.statSync(fullPath);
|
|
3681
|
+
if (stats.mtimeMs === entry.mtime) {
|
|
3682
|
+
continue;
|
|
3683
|
+
}
|
|
3684
|
+
const currentHash = this.calculateHash(fullPath);
|
|
3685
|
+
if (currentHash !== entry.hash) {
|
|
3686
|
+
modified.push(relPath);
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
return { modified, deleted };
|
|
3690
|
+
}
|
|
3691
|
+
/**
|
|
3692
|
+
* Returns array of deleted file paths from manifest
|
|
3693
|
+
*/
|
|
3694
|
+
static detectDeletedFiles(projectPath) {
|
|
3695
|
+
const manifest = this.loadManifest(projectPath);
|
|
3696
|
+
const deleted = [];
|
|
3697
|
+
for (const relPath of Object.keys(manifest)) {
|
|
3698
|
+
const fullPath = path22.join(projectPath, relPath);
|
|
3699
|
+
if (!fs21.existsSync(fullPath)) {
|
|
3700
|
+
deleted.push(relPath);
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
return deleted;
|
|
3704
|
+
}
|
|
3705
|
+
/**
|
|
3706
|
+
* Full drift check: version + modifications
|
|
3707
|
+
*/
|
|
3708
|
+
static checkDrift(projectPath, currentVersion, runningVersion) {
|
|
3709
|
+
const { modified, deleted } = this.detectModifiedFiles(projectPath);
|
|
3710
|
+
let type = "none";
|
|
3711
|
+
let hasDrift = false;
|
|
3712
|
+
if (currentVersion !== runningVersion) {
|
|
3713
|
+
hasDrift = true;
|
|
3714
|
+
type = "version";
|
|
3715
|
+
} else if (modified.length > 0 || deleted.length > 0) {
|
|
3716
|
+
hasDrift = true;
|
|
3717
|
+
type = "modified";
|
|
3718
|
+
}
|
|
3719
|
+
return {
|
|
3720
|
+
hasDrift,
|
|
3721
|
+
type,
|
|
3722
|
+
modifiedFiles: modified,
|
|
3723
|
+
deletedFiles: deleted,
|
|
3724
|
+
version: {
|
|
3725
|
+
current: currentVersion || "0.0.0",
|
|
3726
|
+
running: runningVersion
|
|
3727
|
+
}
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
});
|
|
3733
|
+
|
|
3734
|
+
// src/mcp/resources/indexing.ts
|
|
3735
|
+
import * as fs22 from "fs";
|
|
3736
|
+
import * as path23 from "path";
|
|
3737
|
+
async function indexKnowledge(projectName, force = false, clean = false) {
|
|
3738
|
+
const config = loadMCPConfig();
|
|
3739
|
+
const projects = getExposedProjects();
|
|
3740
|
+
const project = projects.find((p2) => p2.name === projectName || p2.path && p2.path === projectName);
|
|
3741
|
+
if (!project) {
|
|
3742
|
+
return {
|
|
3743
|
+
state: "failed",
|
|
3744
|
+
status: "failed",
|
|
3745
|
+
success: false,
|
|
3746
|
+
message: `Project '${projectName}' not found`,
|
|
3747
|
+
filesIndexed: 0,
|
|
3748
|
+
filesSkipped: 0,
|
|
3749
|
+
progress: { itemsDone: 0 }
|
|
3750
|
+
};
|
|
3751
|
+
}
|
|
3752
|
+
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path }) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
|
|
3753
|
+
const isEnabled = projConfig?.semanticSearch?.enabled || project.semanticSearchEnabled;
|
|
3754
|
+
if (!isEnabled) {
|
|
3755
|
+
return {
|
|
3756
|
+
state: "failed",
|
|
3757
|
+
status: "failed",
|
|
3758
|
+
success: false,
|
|
3759
|
+
message: "Semantic Search is not enabled for this project",
|
|
3760
|
+
filesIndexed: 0,
|
|
3761
|
+
filesSkipped: 0,
|
|
3762
|
+
progress: { itemsDone: 0 }
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
const scanRoot = project.sourcePath || project.path || project.dataPath;
|
|
3766
|
+
if (!fs22.existsSync(scanRoot)) {
|
|
3767
|
+
return {
|
|
3768
|
+
state: "failed",
|
|
3769
|
+
status: "failed",
|
|
3770
|
+
success: false,
|
|
3771
|
+
message: "Project root not found",
|
|
3638
3772
|
filesIndexed: 0,
|
|
3639
3773
|
filesSkipped: 0,
|
|
3640
3774
|
progress: { itemsDone: 0 }
|
|
@@ -3642,8 +3776,14 @@ async function indexKnowledge(projectName, force = false) {
|
|
|
3642
3776
|
}
|
|
3643
3777
|
const runIndexing = async () => {
|
|
3644
3778
|
const { shouldSkipEntryDir, shouldSkipEntryFile } = getScanContext(project, scanRoot);
|
|
3645
|
-
const
|
|
3646
|
-
const
|
|
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");
|
|
3782
|
+
if (clean) {
|
|
3783
|
+
logger.info(`[RAG] Cleaning knowledge index for ${project.name}`);
|
|
3784
|
+
if (fs22.existsSync(indexPath)) fs22.unlinkSync(indexPath);
|
|
3785
|
+
if (fs22.existsSync(codeIndexPath)) fs22.unlinkSync(codeIndexPath);
|
|
3786
|
+
}
|
|
3647
3787
|
const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
|
|
3648
3788
|
const rag = new RAGService(indexPath, model);
|
|
3649
3789
|
const codeRag = new RAGService(codeIndexPath, model);
|
|
@@ -3653,14 +3793,14 @@ async function indexKnowledge(projectName, force = false) {
|
|
|
3653
3793
|
let itemsTotal = 0;
|
|
3654
3794
|
let itemsDone = 0;
|
|
3655
3795
|
const preCount = (dir) => {
|
|
3656
|
-
const entries =
|
|
3796
|
+
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
3657
3797
|
for (const entry of entries) {
|
|
3658
|
-
const fullPath =
|
|
3798
|
+
const fullPath = path23.join(dir, entry.name);
|
|
3659
3799
|
if (entry.isDirectory()) {
|
|
3660
3800
|
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3661
3801
|
preCount(fullPath);
|
|
3662
3802
|
} else if (entry.isFile()) {
|
|
3663
|
-
const ext =
|
|
3803
|
+
const ext = path23.extname(entry.name).toLowerCase();
|
|
3664
3804
|
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3665
3805
|
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3666
3806
|
itemsTotal++;
|
|
@@ -3672,13 +3812,18 @@ async function indexKnowledge(projectName, force = false) {
|
|
|
3672
3812
|
const cleanupIgnoredFiles = async () => {
|
|
3673
3813
|
const indexedFiles = [...rag.getIndexedFiles(), ...codeRag.getIndexedFiles()];
|
|
3674
3814
|
const unique = Array.from(new Set(indexedFiles));
|
|
3815
|
+
const deletedFiles = project.dataPath ? DriftService.detectDeletedFiles(project.dataPath) : [];
|
|
3816
|
+
if (deletedFiles.length > 0) {
|
|
3817
|
+
logger.info(`[RAG] ${project.name}: Detected ${deletedFiles.length} deleted files from manifest`);
|
|
3818
|
+
}
|
|
3675
3819
|
for (const filePath of unique) {
|
|
3676
|
-
if (!
|
|
3677
|
-
const relFilePath = filePath.split(
|
|
3678
|
-
const relScanRoot = scanRoot.split(
|
|
3820
|
+
if (!path23.isAbsolute(filePath)) continue;
|
|
3821
|
+
const relFilePath = filePath.split(path23.sep).join("/");
|
|
3822
|
+
const relScanRoot = scanRoot.split(path23.sep).join("/");
|
|
3679
3823
|
const isInScanRoot = relFilePath === relScanRoot || relFilePath.startsWith(`${relScanRoot}/`);
|
|
3680
3824
|
if (!isInScanRoot) continue;
|
|
3681
|
-
|
|
3825
|
+
const isDeleted = deletedFiles.some((df) => filePath.endsWith(df));
|
|
3826
|
+
if (shouldSkipEntryFile(filePath) || isDeleted || !fs22.existsSync(filePath)) {
|
|
3682
3827
|
await rag.removeFile(filePath);
|
|
3683
3828
|
await codeRag.removeFile(filePath);
|
|
3684
3829
|
}
|
|
@@ -3686,21 +3831,21 @@ async function indexKnowledge(projectName, force = false) {
|
|
|
3686
3831
|
};
|
|
3687
3832
|
await cleanupIgnoredFiles();
|
|
3688
3833
|
const scanDir = async (dir) => {
|
|
3689
|
-
const entries =
|
|
3834
|
+
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
3690
3835
|
for (const entry of entries) {
|
|
3691
|
-
const fullPath =
|
|
3836
|
+
const fullPath = path23.join(dir, entry.name);
|
|
3692
3837
|
if (entry.isDirectory()) {
|
|
3693
3838
|
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3694
3839
|
await scanDir(fullPath);
|
|
3695
3840
|
} else if (entry.isFile()) {
|
|
3696
|
-
const ext =
|
|
3841
|
+
const ext = path23.extname(entry.name).toLowerCase();
|
|
3697
3842
|
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3698
3843
|
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3699
3844
|
try {
|
|
3700
3845
|
indexingJobs.update(project.name, { currentItem: fullPath, itemsDone });
|
|
3701
|
-
const stat =
|
|
3846
|
+
const stat = fs22.statSync(fullPath);
|
|
3702
3847
|
const mtime = force ? void 0 : stat.mtimeMs;
|
|
3703
|
-
const content =
|
|
3848
|
+
const content = fs22.readFileSync(fullPath, "utf-8");
|
|
3704
3849
|
const wasIndexed = await rag.indexFile(fullPath, content, mtime);
|
|
3705
3850
|
if (wasIndexed) {
|
|
3706
3851
|
indexed++;
|
|
@@ -3772,11 +3917,12 @@ var init_indexing = __esm({
|
|
|
3772
3917
|
init_projects();
|
|
3773
3918
|
init_utils2();
|
|
3774
3919
|
init_constants();
|
|
3920
|
+
init_drift_service();
|
|
3775
3921
|
}
|
|
3776
3922
|
});
|
|
3777
3923
|
|
|
3778
3924
|
// src/mcp/resources/context.ts
|
|
3779
|
-
import * as
|
|
3925
|
+
import * as path24 from "path";
|
|
3780
3926
|
import * as os4 from "os";
|
|
3781
3927
|
function getContextPreamble() {
|
|
3782
3928
|
const activeProject = detectActiveProject();
|
|
@@ -3792,7 +3938,7 @@ If the above tools fail, ask the user for clarification.
|
|
|
3792
3938
|
---
|
|
3793
3939
|
`;
|
|
3794
3940
|
}
|
|
3795
|
-
const rrceHome = process.env.RRCE_HOME ||
|
|
3941
|
+
const rrceHome = process.env.RRCE_HOME || path24.join(os4.homedir(), ".rrce-workflow");
|
|
3796
3942
|
const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
|
|
3797
3943
|
const rrceData = activeProject.dataPath;
|
|
3798
3944
|
return `## System Context
|
|
@@ -4087,8 +4233,8 @@ var init_validation = __esm({
|
|
|
4087
4233
|
});
|
|
4088
4234
|
|
|
4089
4235
|
// src/mcp/resources/sessions.ts
|
|
4090
|
-
import * as
|
|
4091
|
-
import * as
|
|
4236
|
+
import * as fs23 from "fs";
|
|
4237
|
+
import * as path25 from "path";
|
|
4092
4238
|
function startSession(projectName, taskSlug, agent, phase) {
|
|
4093
4239
|
const config = loadMCPConfig();
|
|
4094
4240
|
const projects = projectService.scan();
|
|
@@ -4096,8 +4242,8 @@ function startSession(projectName, taskSlug, agent, phase) {
|
|
|
4096
4242
|
if (!project || !project.tasksPath) {
|
|
4097
4243
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
4098
4244
|
}
|
|
4099
|
-
const taskDir =
|
|
4100
|
-
if (!
|
|
4245
|
+
const taskDir = path25.join(project.tasksPath, taskSlug);
|
|
4246
|
+
if (!fs23.existsSync(taskDir)) {
|
|
4101
4247
|
return { success: false, message: `Task '${taskSlug}' not found.` };
|
|
4102
4248
|
}
|
|
4103
4249
|
const session = {
|
|
@@ -4107,8 +4253,8 @@ function startSession(projectName, taskSlug, agent, phase) {
|
|
|
4107
4253
|
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4108
4254
|
heartbeat: (/* @__PURE__ */ new Date()).toISOString()
|
|
4109
4255
|
};
|
|
4110
|
-
const sessionPath =
|
|
4111
|
-
|
|
4256
|
+
const sessionPath = path25.join(taskDir, "session.json");
|
|
4257
|
+
fs23.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
|
|
4112
4258
|
return { success: true, message: `Session started for ${agent} agent on task '${taskSlug}' (phase: ${phase})` };
|
|
4113
4259
|
}
|
|
4114
4260
|
function endSession(projectName, taskSlug) {
|
|
@@ -4118,11 +4264,11 @@ function endSession(projectName, taskSlug) {
|
|
|
4118
4264
|
if (!project || !project.tasksPath) {
|
|
4119
4265
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
4120
4266
|
}
|
|
4121
|
-
const sessionPath =
|
|
4122
|
-
if (!
|
|
4267
|
+
const sessionPath = path25.join(project.tasksPath, taskSlug, "session.json");
|
|
4268
|
+
if (!fs23.existsSync(sessionPath)) {
|
|
4123
4269
|
return { success: true, message: `No active session for task '${taskSlug}'.` };
|
|
4124
4270
|
}
|
|
4125
|
-
|
|
4271
|
+
fs23.unlinkSync(sessionPath);
|
|
4126
4272
|
return { success: true, message: `Session ended for task '${taskSlug}'.` };
|
|
4127
4273
|
}
|
|
4128
4274
|
function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
@@ -4132,9 +4278,9 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
|
4132
4278
|
if (!project || !project.tasksPath) {
|
|
4133
4279
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
4134
4280
|
}
|
|
4135
|
-
const taskDir =
|
|
4136
|
-
if (!
|
|
4137
|
-
|
|
4281
|
+
const taskDir = path25.join(project.tasksPath, taskSlug);
|
|
4282
|
+
if (!fs23.existsSync(taskDir)) {
|
|
4283
|
+
fs23.mkdirSync(taskDir, { recursive: true });
|
|
4138
4284
|
}
|
|
4139
4285
|
const todos = {
|
|
4140
4286
|
phase,
|
|
@@ -4142,8 +4288,8 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
|
4142
4288
|
items,
|
|
4143
4289
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4144
4290
|
};
|
|
4145
|
-
const todosPath =
|
|
4146
|
-
|
|
4291
|
+
const todosPath = path25.join(taskDir, "agent-todos.json");
|
|
4292
|
+
fs23.writeFileSync(todosPath, JSON.stringify(todos, null, 2));
|
|
4147
4293
|
return { success: true, message: `Updated ${items.length} todo items for task '${taskSlug}'.`, count: items.length };
|
|
4148
4294
|
}
|
|
4149
4295
|
var init_sessions = __esm({
|
|
@@ -4263,143 +4409,69 @@ var init_resources3 = __esm({
|
|
|
4263
4409
|
}
|
|
4264
4410
|
});
|
|
4265
4411
|
|
|
4266
|
-
// src/mcp/
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
}
|
|
4273
|
-
const basePath = path24.join(getAgentCorePromptsDir(), "_base.md");
|
|
4274
|
-
if (fs22.existsSync(basePath)) {
|
|
4275
|
-
const content = fs22.readFileSync(basePath, "utf-8");
|
|
4276
|
-
baseProtocolCache = content.replace(/^---[\s\S]*?---\n*/, "");
|
|
4277
|
-
return baseProtocolCache;
|
|
4278
|
-
}
|
|
4279
|
-
baseProtocolCache = "";
|
|
4280
|
-
return "";
|
|
4281
|
-
}
|
|
4282
|
-
function getAllPrompts() {
|
|
4283
|
-
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
4284
|
-
return prompts.map((p) => {
|
|
4285
|
-
const args = [];
|
|
4286
|
-
if (p.frontmatter["required-args"]) {
|
|
4287
|
-
args.push(...p.frontmatter["required-args"].map((a) => ({
|
|
4288
|
-
name: a.name,
|
|
4289
|
-
description: a.prompt || a.name,
|
|
4290
|
-
required: true
|
|
4291
|
-
})));
|
|
4292
|
-
}
|
|
4293
|
-
if (p.frontmatter["optional-args"]) {
|
|
4294
|
-
args.push(...p.frontmatter["optional-args"].map((a) => ({
|
|
4295
|
-
name: a.name,
|
|
4296
|
-
description: a.prompt || a.name,
|
|
4297
|
-
required: false
|
|
4298
|
-
})));
|
|
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;
|
|
4299
4418
|
}
|
|
4300
|
-
const filename = p.filePath.split("/").pop() || "";
|
|
4301
|
-
const id = filename.replace(/\.md$/, "");
|
|
4302
|
-
return {
|
|
4303
|
-
id,
|
|
4304
|
-
name: p.frontmatter.name,
|
|
4305
|
-
description: p.frontmatter.description,
|
|
4306
|
-
arguments: args,
|
|
4307
|
-
content: p.content
|
|
4308
|
-
};
|
|
4309
|
-
});
|
|
4310
|
-
}
|
|
4311
|
-
function getPromptDef(name) {
|
|
4312
|
-
const all = getAllPrompts();
|
|
4313
|
-
const search = name.toLowerCase();
|
|
4314
|
-
return all.find(
|
|
4315
|
-
(p) => p.name === name || p.id === name || p.name.toLowerCase() === search || p.id.toLowerCase() === search
|
|
4316
|
-
);
|
|
4317
|
-
}
|
|
4318
|
-
function renderPromptWithContext(content, args) {
|
|
4319
|
-
const renderArgs = { ...args };
|
|
4320
|
-
let activeProject = detectActiveProject();
|
|
4321
|
-
if (!activeProject) {
|
|
4322
|
-
projectService.refresh();
|
|
4323
|
-
activeProject = detectActiveProject();
|
|
4324
4419
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
if (activeProject) {
|
|
4331
|
-
resolvedRrceData = activeProject.dataPath;
|
|
4332
|
-
if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
|
|
4333
|
-
resolvedRrceData += "/";
|
|
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) }] };
|
|
4334
4425
|
}
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
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
|
+
};
|
|
4340
4435
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
resolvedWorkspaceName = workspaceName;
|
|
4350
|
-
if (!resolvedRrceData.endsWith("/") && !resolvedRrceData.endsWith("\\")) {
|
|
4351
|
-
resolvedRrceData += "/";
|
|
4352
|
-
}
|
|
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 };
|
|
4353
4444
|
}
|
|
4354
|
-
|
|
4445
|
+
return { content: [{ type: "text", text: context }] };
|
|
4355
4446
|
}
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
if (!renderArgs["WORKSPACE_NAME"]) renderArgs["WORKSPACE_NAME"] = resolvedWorkspaceName;
|
|
4361
|
-
const agentContent = renderPrompt(content, renderArgs);
|
|
4362
|
-
const baseProtocol = loadBaseProtocol2();
|
|
4363
|
-
const rendered = baseProtocol ? `${baseProtocol}
|
|
4364
|
-
${agentContent}` : agentContent;
|
|
4365
|
-
return {
|
|
4366
|
-
rendered,
|
|
4367
|
-
context: {
|
|
4368
|
-
RRCE_DATA: resolvedRrceData,
|
|
4369
|
-
RRCE_HOME: resolvedRrceHome,
|
|
4370
|
-
WORKSPACE_ROOT: resolvedWorkspaceRoot,
|
|
4371
|
-
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) }] };
|
|
4372
4451
|
}
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
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;
|
|
4379
4466
|
}
|
|
4380
|
-
return rendered;
|
|
4381
4467
|
}
|
|
4382
|
-
var
|
|
4383
|
-
var
|
|
4384
|
-
"src/mcp/
|
|
4468
|
+
var projectTools;
|
|
4469
|
+
var init_project = __esm({
|
|
4470
|
+
"src/mcp/handlers/tools/project.ts"() {
|
|
4385
4471
|
"use strict";
|
|
4386
|
-
init_prompts();
|
|
4387
4472
|
init_resources2();
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
baseProtocolCache = null;
|
|
4391
|
-
}
|
|
4392
|
-
});
|
|
4393
|
-
|
|
4394
|
-
// src/mcp/handlers/tools.ts
|
|
4395
|
-
import "@modelcontextprotocol/sdk/server/index.js";
|
|
4396
|
-
import {
|
|
4397
|
-
ListToolsRequestSchema,
|
|
4398
|
-
CallToolRequestSchema
|
|
4399
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
4400
|
-
function registerToolHandlers(server) {
|
|
4401
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
4402
|
-
const tools = [
|
|
4473
|
+
init_logger();
|
|
4474
|
+
projectTools = [
|
|
4403
4475
|
{
|
|
4404
4476
|
name: "resolve_path",
|
|
4405
4477
|
description: "Resolve configuration paths (RRCE_DATA, etc.) for a project. Helps determine if a project is using global or local storage.",
|
|
@@ -4411,6 +4483,124 @@ function registerToolHandlers(server) {
|
|
|
4411
4483
|
}
|
|
4412
4484
|
}
|
|
4413
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 = [
|
|
4414
4604
|
{
|
|
4415
4605
|
name: "search_knowledge",
|
|
4416
4606
|
description: "Search across all exposed project knowledge bases. Returns results with token count and optional truncation.",
|
|
@@ -4518,194 +4708,465 @@ function registerToolHandlers(server) {
|
|
|
4518
4708
|
},
|
|
4519
4709
|
required: ["project", "task_slug"]
|
|
4520
4710
|
}
|
|
4521
|
-
}
|
|
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 = [
|
|
4522
4780
|
{
|
|
4523
|
-
name: "
|
|
4524
|
-
description: "
|
|
4781
|
+
name: "list_tasks",
|
|
4782
|
+
description: "List all tasks for a project",
|
|
4525
4783
|
inputSchema: {
|
|
4526
4784
|
type: "object",
|
|
4527
|
-
properties: {
|
|
4528
|
-
project: { type: "string", description: "Name of the project" },
|
|
4529
|
-
keyword: { type: "string", description: "Search in title/summary" },
|
|
4530
|
-
status: { type: "string", description: "Filter by status (draft, in_progress, complete, etc.)" },
|
|
4531
|
-
agent: { type: "string", description: "Filter by agent phase (research, planning, executor, documentation)" },
|
|
4532
|
-
since: { type: "string", description: "ISO date - tasks updated after this date" },
|
|
4533
|
-
limit: { type: "number", description: "Max results (default: 20)" }
|
|
4534
|
-
},
|
|
4785
|
+
properties: { project: { type: "string", description: "Name of the project" } },
|
|
4535
4786
|
required: ["project"]
|
|
4536
4787
|
}
|
|
4537
4788
|
},
|
|
4538
4789
|
{
|
|
4539
|
-
name: "
|
|
4540
|
-
description: "
|
|
4790
|
+
name: "get_task",
|
|
4791
|
+
description: "Get details of a specific task",
|
|
4541
4792
|
inputSchema: {
|
|
4542
4793
|
type: "object",
|
|
4543
4794
|
properties: {
|
|
4544
4795
|
project: { type: "string", description: "Name of the project" },
|
|
4545
|
-
task_slug: { type: "string", description: "The task
|
|
4546
|
-
phase: { type: "string", enum: ["research", "planning", "execution", "documentation"], description: "Phase to validate" }
|
|
4796
|
+
task_slug: { type: "string", description: "The slug of the task" }
|
|
4547
4797
|
},
|
|
4548
|
-
required: ["project", "task_slug"
|
|
4798
|
+
required: ["project", "task_slug"]
|
|
4549
4799
|
}
|
|
4550
4800
|
},
|
|
4551
4801
|
{
|
|
4552
|
-
name: "
|
|
4553
|
-
description: "
|
|
4802
|
+
name: "create_task",
|
|
4803
|
+
description: "Create a new task in the project",
|
|
4554
4804
|
inputSchema: {
|
|
4555
4805
|
type: "object",
|
|
4556
4806
|
properties: {
|
|
4557
|
-
project: { type: "string", description: "Name of the project
|
|
4558
|
-
|
|
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" }
|
|
4559
4811
|
},
|
|
4560
|
-
required: ["project"]
|
|
4561
|
-
}
|
|
4562
|
-
},
|
|
4563
|
-
{
|
|
4564
|
-
name: "list_projects",
|
|
4565
|
-
description: "List all projects exposed via MCP. Use these names for project-specific tools.",
|
|
4566
|
-
inputSchema: { type: "object", properties: {} }
|
|
4567
|
-
},
|
|
4568
|
-
{
|
|
4569
|
-
name: "get_project_context",
|
|
4570
|
-
description: "Get the project context/architecture for a specific project",
|
|
4571
|
-
inputSchema: {
|
|
4572
|
-
type: "object",
|
|
4573
|
-
properties: { project: { type: "string", description: "Name of the project to get context for" } },
|
|
4574
|
-
required: ["project"]
|
|
4812
|
+
required: ["project", "task_slug"]
|
|
4575
4813
|
}
|
|
4576
4814
|
},
|
|
4577
4815
|
{
|
|
4578
|
-
name: "
|
|
4579
|
-
description: "
|
|
4580
|
-
inputSchema: { type: "object", properties: {} }
|
|
4581
|
-
},
|
|
4582
|
-
{
|
|
4583
|
-
name: "get_agent_prompt",
|
|
4584
|
-
description: 'Get the system prompt for a specific agent. Accepts agent Name (e.g. "RRCE Init") or ID (e.g. "init").',
|
|
4816
|
+
name: "update_task",
|
|
4817
|
+
description: "Update an existing task",
|
|
4585
4818
|
inputSchema: {
|
|
4586
4819
|
type: "object",
|
|
4587
4820
|
properties: {
|
|
4588
|
-
|
|
4589
|
-
|
|
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 }
|
|
4590
4824
|
},
|
|
4591
|
-
required: ["
|
|
4825
|
+
required: ["project", "task_slug", "updates"]
|
|
4592
4826
|
}
|
|
4593
4827
|
},
|
|
4594
4828
|
{
|
|
4595
|
-
name: "
|
|
4596
|
-
description: "
|
|
4829
|
+
name: "delete_task",
|
|
4830
|
+
description: "Delete a task from the project",
|
|
4597
4831
|
inputSchema: {
|
|
4598
4832
|
type: "object",
|
|
4599
|
-
properties: {
|
|
4600
|
-
|
|
4833
|
+
properties: {
|
|
4834
|
+
project: { type: "string", description: "Name of the project" },
|
|
4835
|
+
task_slug: { type: "string", description: "The slug of the task to delete" }
|
|
4836
|
+
},
|
|
4837
|
+
required: ["project", "task_slug"]
|
|
4601
4838
|
}
|
|
4602
4839
|
},
|
|
4603
4840
|
{
|
|
4604
|
-
name: "
|
|
4605
|
-
description: "
|
|
4841
|
+
name: "search_tasks",
|
|
4842
|
+
description: "Search across all tasks by keyword, status, agent phase, or date. Returns matching tasks sorted by relevance.",
|
|
4606
4843
|
inputSchema: {
|
|
4607
4844
|
type: "object",
|
|
4608
4845
|
properties: {
|
|
4609
4846
|
project: { type: "string", description: "Name of the project" },
|
|
4610
|
-
|
|
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)" }
|
|
4611
4852
|
},
|
|
4612
|
-
required: ["project"
|
|
4853
|
+
required: ["project"]
|
|
4613
4854
|
}
|
|
4614
4855
|
},
|
|
4615
4856
|
{
|
|
4616
|
-
name: "
|
|
4617
|
-
description: "
|
|
4857
|
+
name: "validate_phase",
|
|
4858
|
+
description: "Check if a task phase has all prerequisites complete. Returns validation result with missing items and suggestions.",
|
|
4618
4859
|
inputSchema: {
|
|
4619
4860
|
type: "object",
|
|
4620
4861
|
properties: {
|
|
4621
4862
|
project: { type: "string", description: "Name of the project" },
|
|
4622
|
-
task_slug: { type: "string", description: "The
|
|
4623
|
-
|
|
4624
|
-
summary: { type: "string", description: "Brief summary of the task" }
|
|
4863
|
+
task_slug: { type: "string", description: "The task slug" },
|
|
4864
|
+
phase: { type: "string", enum: ["research", "planning", "execution", "documentation"], description: "Phase to validate" }
|
|
4625
4865
|
},
|
|
4626
|
-
required: ["project", "task_slug"]
|
|
4866
|
+
required: ["project", "task_slug", "phase"]
|
|
4627
4867
|
}
|
|
4628
|
-
}
|
|
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 = [
|
|
4629
4902
|
{
|
|
4630
|
-
name: "
|
|
4631
|
-
description: "
|
|
4903
|
+
name: "start_session",
|
|
4904
|
+
description: "Start an agent session for active task tracking. Call this when beginning work on a task phase.",
|
|
4632
4905
|
inputSchema: {
|
|
4633
4906
|
type: "object",
|
|
4634
4907
|
properties: {
|
|
4635
4908
|
project: { type: "string", description: "Name of the project" },
|
|
4636
4909
|
task_slug: { type: "string", description: "The slug of the task" },
|
|
4637
|
-
|
|
4910
|
+
agent: { type: "string", description: "Agent type: research, planning, executor, or documentation" },
|
|
4911
|
+
phase: { type: "string", description: 'Current phase description (e.g., "clarification", "task breakdown")' }
|
|
4638
4912
|
},
|
|
4639
|
-
required: ["project", "task_slug", "
|
|
4913
|
+
required: ["project", "task_slug", "agent", "phase"]
|
|
4640
4914
|
}
|
|
4641
4915
|
},
|
|
4642
4916
|
{
|
|
4643
|
-
name: "
|
|
4644
|
-
description: "
|
|
4917
|
+
name: "end_session",
|
|
4918
|
+
description: "End an agent session. Call this before emitting completion signal.",
|
|
4645
4919
|
inputSchema: {
|
|
4646
4920
|
type: "object",
|
|
4647
4921
|
properties: {
|
|
4648
4922
|
project: { type: "string", description: "Name of the project" },
|
|
4649
|
-
task_slug: { type: "string", description: "The slug of the task
|
|
4923
|
+
task_slug: { type: "string", description: "The slug of the task" }
|
|
4650
4924
|
},
|
|
4651
4925
|
required: ["project", "task_slug"]
|
|
4652
4926
|
}
|
|
4653
4927
|
},
|
|
4654
4928
|
{
|
|
4655
|
-
name: "
|
|
4656
|
-
description: "
|
|
4929
|
+
name: "update_agent_todos",
|
|
4930
|
+
description: "Update the agent todo list for a task. Use this to track granular work items during a phase.",
|
|
4657
4931
|
inputSchema: {
|
|
4658
4932
|
type: "object",
|
|
4659
4933
|
properties: {
|
|
4660
4934
|
project: { type: "string", description: "Name of the project" },
|
|
4661
4935
|
task_slug: { type: "string", description: "The slug of the task" },
|
|
4662
|
-
|
|
4663
|
-
|
|
4936
|
+
phase: { type: "string", description: "Current phase" },
|
|
4937
|
+
agent: { type: "string", description: "Agent type" },
|
|
4938
|
+
items: {
|
|
4939
|
+
type: "array",
|
|
4940
|
+
description: "Todo items array",
|
|
4941
|
+
items: {
|
|
4942
|
+
type: "object",
|
|
4943
|
+
properties: {
|
|
4944
|
+
id: { type: "string" },
|
|
4945
|
+
content: { type: "string" },
|
|
4946
|
+
status: { type: "string", enum: ["pending", "in_progress", "completed"] },
|
|
4947
|
+
priority: { type: "string", enum: ["high", "medium", "low"] }
|
|
4948
|
+
},
|
|
4949
|
+
required: ["id", "content", "status", "priority"]
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4664
4952
|
},
|
|
4665
|
-
required: ["project", "task_slug", "agent", "
|
|
4953
|
+
required: ["project", "task_slug", "phase", "agent", "items"]
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
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 += "/";
|
|
4666
5046
|
}
|
|
4667
|
-
}
|
|
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 = [
|
|
4668
5134
|
{
|
|
4669
|
-
name: "
|
|
4670
|
-
description: "
|
|
4671
|
-
inputSchema: {
|
|
4672
|
-
type: "object",
|
|
4673
|
-
properties: {
|
|
4674
|
-
project: { type: "string", description: "Name of the project" },
|
|
4675
|
-
task_slug: { type: "string", description: "The slug of the task" }
|
|
4676
|
-
},
|
|
4677
|
-
required: ["project", "task_slug"]
|
|
4678
|
-
}
|
|
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: {} }
|
|
4679
5138
|
},
|
|
4680
5139
|
{
|
|
4681
|
-
name: "
|
|
4682
|
-
description:
|
|
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").',
|
|
4683
5142
|
inputSchema: {
|
|
4684
5143
|
type: "object",
|
|
4685
5144
|
properties: {
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
phase: { type: "string", description: "Current phase" },
|
|
4689
|
-
agent: { type: "string", description: "Agent type" },
|
|
4690
|
-
items: {
|
|
4691
|
-
type: "array",
|
|
4692
|
-
description: "Todo items array",
|
|
4693
|
-
items: {
|
|
4694
|
-
type: "object",
|
|
4695
|
-
properties: {
|
|
4696
|
-
id: { type: "string" },
|
|
4697
|
-
content: { type: "string" },
|
|
4698
|
-
status: { type: "string", enum: ["pending", "in_progress", "completed"] },
|
|
4699
|
-
priority: { type: "string", enum: ["high", "medium", "low"] }
|
|
4700
|
-
},
|
|
4701
|
-
required: ["id", "content", "status", "priority"]
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
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 }
|
|
4704
5147
|
},
|
|
4705
|
-
required: ["
|
|
5148
|
+
required: ["agent"]
|
|
4706
5149
|
}
|
|
4707
5150
|
}
|
|
4708
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
|
+
];
|
|
4709
5170
|
const projects = getExposedProjects();
|
|
4710
5171
|
if (projects.length === 0) {
|
|
4711
5172
|
tools.push({
|
|
@@ -4720,221 +5181,17 @@ function registerToolHandlers(server) {
|
|
|
4720
5181
|
const { name, arguments: args } = request.params;
|
|
4721
5182
|
logger.info(`Calling tool: ${name}`, args);
|
|
4722
5183
|
try {
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
});
|
|
4735
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4736
|
-
}
|
|
4737
|
-
case "search_code": {
|
|
4738
|
-
const params = args;
|
|
4739
|
-
const result = await searchCode(params.query, params.project, params.limit, {
|
|
4740
|
-
max_tokens: params.max_tokens,
|
|
4741
|
-
min_score: params.min_score
|
|
4742
|
-
});
|
|
4743
|
-
if (result.results.length === 0) {
|
|
4744
|
-
return {
|
|
4745
|
-
content: [{
|
|
4746
|
-
type: "text",
|
|
4747
|
-
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."
|
|
4748
|
-
}]
|
|
4749
|
-
};
|
|
4750
|
-
}
|
|
4751
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4752
|
-
}
|
|
4753
|
-
case "find_related_files": {
|
|
4754
|
-
const params = args;
|
|
4755
|
-
const result = await findRelatedFiles2(params.file, params.project, {
|
|
4756
|
-
includeImports: params.include_imports,
|
|
4757
|
-
includeImportedBy: params.include_imported_by,
|
|
4758
|
-
depth: params.depth
|
|
4759
|
-
});
|
|
4760
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4761
|
-
}
|
|
4762
|
-
case "search_symbols": {
|
|
4763
|
-
const params = args;
|
|
4764
|
-
const result = await searchSymbols2(params.name, params.project, {
|
|
4765
|
-
type: params.type,
|
|
4766
|
-
fuzzy: params.fuzzy,
|
|
4767
|
-
limit: params.limit
|
|
4768
|
-
});
|
|
4769
|
-
if (!result.success) {
|
|
4770
|
-
return { content: [{ type: "text", text: result.message || "Search failed" }], isError: true };
|
|
4771
|
-
}
|
|
4772
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4773
|
-
}
|
|
4774
|
-
case "get_file_summary": {
|
|
4775
|
-
const params = args;
|
|
4776
|
-
const result = await getFileSummary(params.file, params.project);
|
|
4777
|
-
if (!result.success) {
|
|
4778
|
-
return { content: [{ type: "text", text: result.message || "Failed to get file summary" }], isError: true };
|
|
4779
|
-
}
|
|
4780
|
-
return { content: [{ type: "text", text: JSON.stringify(result.summary, null, 2) }] };
|
|
4781
|
-
}
|
|
4782
|
-
case "get_context_bundle": {
|
|
4783
|
-
const params = args;
|
|
4784
|
-
const result = await getContextBundle(params.query, params.project, {
|
|
4785
|
-
task_slug: params.task_slug,
|
|
4786
|
-
max_tokens: params.max_tokens,
|
|
4787
|
-
include: params.include
|
|
4788
|
-
});
|
|
4789
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4790
|
-
}
|
|
4791
|
-
case "index_knowledge": {
|
|
4792
|
-
const params = args;
|
|
4793
|
-
const result = await indexKnowledge(params.project, params.force);
|
|
4794
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4795
|
-
}
|
|
4796
|
-
case "list_projects": {
|
|
4797
|
-
const projects = getExposedProjects();
|
|
4798
|
-
const list = projects.map((p) => ({ name: p.name, source: p.source, path: p.path }));
|
|
4799
|
-
return {
|
|
4800
|
-
content: [{
|
|
4801
|
-
type: "text",
|
|
4802
|
-
text: JSON.stringify(list, null, 2) + "\n\nTip: Use these project names for tools like `get_project_context` or `index_knowledge`."
|
|
4803
|
-
}]
|
|
4804
|
-
};
|
|
4805
|
-
}
|
|
4806
|
-
case "get_project_context": {
|
|
4807
|
-
const context = getProjectContext(args.project);
|
|
4808
|
-
if (!context) {
|
|
4809
|
-
const projects = getExposedProjects().map((p) => p.name).join(", ");
|
|
4810
|
-
const msg = `No project context found for "${args.project}".
|
|
4811
|
-
Available projects: ${projects}`;
|
|
4812
|
-
logger.warn(msg);
|
|
4813
|
-
return { content: [{ type: "text", text: msg }], isError: true };
|
|
4814
|
-
}
|
|
4815
|
-
return { content: [{ type: "text", text: context }] };
|
|
4816
|
-
}
|
|
4817
|
-
case "list_agents": {
|
|
4818
|
-
const prompts = getAllPrompts();
|
|
4819
|
-
return {
|
|
4820
|
-
content: [{
|
|
4821
|
-
type: "text",
|
|
4822
|
-
text: JSON.stringify(prompts.map((p) => ({
|
|
4823
|
-
name: p.name,
|
|
4824
|
-
id: p.id,
|
|
4825
|
-
description: p.description,
|
|
4826
|
-
arguments: p.arguments
|
|
4827
|
-
})), null, 2) + "\n\nTip: Retrieve the prompt for an agent using `get_agent_prompt` with its name or ID."
|
|
4828
|
-
}]
|
|
4829
|
-
};
|
|
4830
|
-
}
|
|
4831
|
-
case "get_agent_prompt": {
|
|
4832
|
-
const params = args;
|
|
4833
|
-
const agentName = params.agent;
|
|
4834
|
-
const promptDef = getPromptDef(agentName);
|
|
4835
|
-
if (!promptDef) {
|
|
4836
|
-
const available = getAllPrompts().map((p) => `${p.name} (id: ${p.id})`).join(", ");
|
|
4837
|
-
throw new Error(`Agent not found: ${agentName}. Available agents: ${available}`);
|
|
4838
|
-
}
|
|
4839
|
-
const renderArgs = params.args || {};
|
|
4840
|
-
const stringArgs = {};
|
|
4841
|
-
for (const [key, val] of Object.entries(renderArgs)) {
|
|
4842
|
-
stringArgs[key] = String(val);
|
|
4843
|
-
}
|
|
4844
|
-
const { rendered } = renderPromptWithContext(promptDef.content, stringArgs);
|
|
4845
|
-
const contextPreamble = getContextPreamble();
|
|
4846
|
-
return { content: [{ type: "text", text: contextPreamble + rendered }] };
|
|
4847
|
-
}
|
|
4848
|
-
case "list_tasks": {
|
|
4849
|
-
const params = args;
|
|
4850
|
-
const tasks = getProjectTasks(params.project);
|
|
4851
|
-
return { content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }] };
|
|
4852
|
-
}
|
|
4853
|
-
case "get_task": {
|
|
4854
|
-
const params = args;
|
|
4855
|
-
const task = getTask(params.project, params.task_slug);
|
|
4856
|
-
if (!task) {
|
|
4857
|
-
return { content: [{ type: "text", text: `Task '${params.task_slug}' not found in project '${params.project}'.` }], isError: true };
|
|
4858
|
-
}
|
|
4859
|
-
return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }] };
|
|
4860
|
-
}
|
|
4861
|
-
case "create_task": {
|
|
4862
|
-
const params = args;
|
|
4863
|
-
const taskData = {
|
|
4864
|
-
title: params.title || params.task_slug,
|
|
4865
|
-
summary: params.summary || ""
|
|
4866
|
-
};
|
|
4867
|
-
const task = await createTask(params.project, params.task_slug, taskData);
|
|
4868
|
-
return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' created. meta.json saved.
|
|
4869
|
-
${JSON.stringify(task, null, 2)}` }] };
|
|
4870
|
-
}
|
|
4871
|
-
case "update_task": {
|
|
4872
|
-
const params = args;
|
|
4873
|
-
const task = await updateTask(params.project, params.task_slug, params.updates);
|
|
4874
|
-
return { content: [{ type: "text", text: `\u2713 Task '${params.task_slug}' updated. meta.json saved.
|
|
4875
|
-
${JSON.stringify(task, null, 2)}` }] };
|
|
4876
|
-
}
|
|
4877
|
-
case "delete_task": {
|
|
4878
|
-
const params = args;
|
|
4879
|
-
const success = deleteTask(params.project, params.task_slug);
|
|
4880
|
-
return { content: [{ type: "text", text: success ? `\u2713 Task '${params.task_slug}' deleted.` : `\u2717 Failed to delete '${params.task_slug}'.` }] };
|
|
4881
|
-
}
|
|
4882
|
-
case "start_session": {
|
|
4883
|
-
const params = args;
|
|
4884
|
-
const result = startSession(params.project, params.task_slug, params.agent, params.phase);
|
|
4885
|
-
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4886
|
-
}
|
|
4887
|
-
case "end_session": {
|
|
4888
|
-
const params = args;
|
|
4889
|
-
const result = endSession(params.project, params.task_slug);
|
|
4890
|
-
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4891
|
-
}
|
|
4892
|
-
case "update_agent_todos": {
|
|
4893
|
-
const params = args;
|
|
4894
|
-
const result = updateAgentTodos(params.project, params.task_slug, params.phase, params.agent, params.items);
|
|
4895
|
-
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4896
|
-
}
|
|
4897
|
-
case "prefetch_task_context": {
|
|
4898
|
-
const params = args;
|
|
4899
|
-
const result = await prefetchTaskContext(params.project, params.task_slug, {
|
|
4900
|
-
max_tokens: params.max_tokens
|
|
4901
|
-
});
|
|
4902
|
-
if (!result.success) {
|
|
4903
|
-
return { content: [{ type: "text", text: result.message || "Failed to prefetch task context" }], isError: true };
|
|
4904
|
-
}
|
|
4905
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4906
|
-
}
|
|
4907
|
-
case "search_tasks": {
|
|
4908
|
-
const params = args;
|
|
4909
|
-
const results = searchTasks(params.project, {
|
|
4910
|
-
keyword: params.keyword,
|
|
4911
|
-
status: params.status,
|
|
4912
|
-
agent: params.agent,
|
|
4913
|
-
since: params.since,
|
|
4914
|
-
limit: params.limit
|
|
4915
|
-
});
|
|
4916
|
-
return { content: [{ type: "text", text: JSON.stringify({ count: results.length, tasks: results }, null, 2) }] };
|
|
4917
|
-
}
|
|
4918
|
-
case "validate_phase": {
|
|
4919
|
-
const params = args;
|
|
4920
|
-
const result = validatePhase(params.project, params.task_slug, params.phase);
|
|
4921
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4922
|
-
}
|
|
4923
|
-
case "help_setup": {
|
|
4924
|
-
const msg = `
|
|
4925
|
-
RRCE MCP Server is running, but no projects are configured/exposed.
|
|
4926
|
-
|
|
4927
|
-
To fix this:
|
|
4928
|
-
1. Open a terminal.
|
|
4929
|
-
2. Run: npx rrce-workflow mcp configure
|
|
4930
|
-
3. Select the projects you want to expose to the AI.
|
|
4931
|
-
4. Restart the MCP server (or it may pick up changes automatically).
|
|
4932
|
-
`;
|
|
4933
|
-
return { content: [{ type: "text", text: msg }] };
|
|
4934
|
-
}
|
|
4935
|
-
default:
|
|
4936
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
4937
|
-
}
|
|
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}`);
|
|
4938
5195
|
} catch (error) {
|
|
4939
5196
|
logger.error(`Tool execution failed: ${name}`, error);
|
|
4940
5197
|
throw error;
|
|
@@ -4946,7 +5203,11 @@ var init_tools = __esm({
|
|
|
4946
5203
|
"use strict";
|
|
4947
5204
|
init_logger();
|
|
4948
5205
|
init_resources2();
|
|
4949
|
-
|
|
5206
|
+
init_project();
|
|
5207
|
+
init_search2();
|
|
5208
|
+
init_task();
|
|
5209
|
+
init_session();
|
|
5210
|
+
init_agent();
|
|
4950
5211
|
}
|
|
4951
5212
|
});
|
|
4952
5213
|
|
|
@@ -5172,8 +5433,8 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
5172
5433
|
}
|
|
5173
5434
|
async function handleConfigureGlobalPath() {
|
|
5174
5435
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
5175
|
-
const
|
|
5176
|
-
const
|
|
5436
|
+
const fs34 = await import("fs");
|
|
5437
|
+
const path33 = await import("path");
|
|
5177
5438
|
note3(
|
|
5178
5439
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
5179
5440
|
and coordinate across projects.
|
|
@@ -5187,8 +5448,8 @@ locally in each project. MCP needs a central location.`,
|
|
|
5187
5448
|
return false;
|
|
5188
5449
|
}
|
|
5189
5450
|
try {
|
|
5190
|
-
if (!
|
|
5191
|
-
|
|
5451
|
+
if (!fs34.existsSync(resolvedPath)) {
|
|
5452
|
+
fs34.mkdirSync(resolvedPath, { recursive: true });
|
|
5192
5453
|
}
|
|
5193
5454
|
const config = loadMCPConfig();
|
|
5194
5455
|
saveMCPConfig(config);
|
|
@@ -5196,7 +5457,7 @@ locally in each project. MCP needs a central location.`,
|
|
|
5196
5457
|
`${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
|
|
5197
5458
|
|
|
5198
5459
|
MCP config will be stored at:
|
|
5199
|
-
${
|
|
5460
|
+
${path33.join(resolvedPath, "mcp.yaml")}`,
|
|
5200
5461
|
"Configuration Saved"
|
|
5201
5462
|
);
|
|
5202
5463
|
return true;
|
|
@@ -5292,105 +5553,6 @@ var init_Header = __esm({
|
|
|
5292
5553
|
}
|
|
5293
5554
|
});
|
|
5294
5555
|
|
|
5295
|
-
// src/lib/drift-service.ts
|
|
5296
|
-
import * as fs23 from "fs";
|
|
5297
|
-
import * as path25 from "path";
|
|
5298
|
-
import * as crypto2 from "crypto";
|
|
5299
|
-
var DriftService;
|
|
5300
|
-
var init_drift_service = __esm({
|
|
5301
|
-
"src/lib/drift-service.ts"() {
|
|
5302
|
-
"use strict";
|
|
5303
|
-
DriftService = class {
|
|
5304
|
-
static CHECKSUM_FILENAME = ".rrce-checksums.json";
|
|
5305
|
-
static calculateHash(filePath) {
|
|
5306
|
-
const content = fs23.readFileSync(filePath);
|
|
5307
|
-
return crypto2.createHash("md5").update(content).digest("hex");
|
|
5308
|
-
}
|
|
5309
|
-
static getManifestPath(projectPath) {
|
|
5310
|
-
return path25.join(projectPath, this.CHECKSUM_FILENAME);
|
|
5311
|
-
}
|
|
5312
|
-
static loadManifest(projectPath) {
|
|
5313
|
-
const manifestPath = this.getManifestPath(projectPath);
|
|
5314
|
-
if (!fs23.existsSync(manifestPath)) {
|
|
5315
|
-
return {};
|
|
5316
|
-
}
|
|
5317
|
-
try {
|
|
5318
|
-
return JSON.parse(fs23.readFileSync(manifestPath, "utf8"));
|
|
5319
|
-
} catch (e) {
|
|
5320
|
-
return {};
|
|
5321
|
-
}
|
|
5322
|
-
}
|
|
5323
|
-
static saveManifest(projectPath, manifest) {
|
|
5324
|
-
const manifestPath = this.getManifestPath(projectPath);
|
|
5325
|
-
fs23.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
5326
|
-
}
|
|
5327
|
-
/**
|
|
5328
|
-
* Generates a manifest for the current state of files in the project
|
|
5329
|
-
*/
|
|
5330
|
-
static generateManifest(projectPath, files) {
|
|
5331
|
-
const manifest = {};
|
|
5332
|
-
for (const file of files) {
|
|
5333
|
-
const fullPath = path25.join(projectPath, file);
|
|
5334
|
-
if (fs23.existsSync(fullPath)) {
|
|
5335
|
-
const stats = fs23.statSync(fullPath);
|
|
5336
|
-
manifest[file] = {
|
|
5337
|
-
hash: this.calculateHash(fullPath),
|
|
5338
|
-
mtime: stats.mtimeMs
|
|
5339
|
-
};
|
|
5340
|
-
}
|
|
5341
|
-
}
|
|
5342
|
-
return manifest;
|
|
5343
|
-
}
|
|
5344
|
-
/**
|
|
5345
|
-
* Compares current files against the manifest to detect modifications
|
|
5346
|
-
*/
|
|
5347
|
-
static detectModifiedFiles(projectPath) {
|
|
5348
|
-
const manifest = this.loadManifest(projectPath);
|
|
5349
|
-
const modifiedFiles = [];
|
|
5350
|
-
for (const [relPath, entry] of Object.entries(manifest)) {
|
|
5351
|
-
const fullPath = path25.join(projectPath, relPath);
|
|
5352
|
-
if (!fs23.existsSync(fullPath)) {
|
|
5353
|
-
continue;
|
|
5354
|
-
}
|
|
5355
|
-
const stats = fs23.statSync(fullPath);
|
|
5356
|
-
if (stats.mtimeMs === entry.mtime) {
|
|
5357
|
-
continue;
|
|
5358
|
-
}
|
|
5359
|
-
const currentHash = this.calculateHash(fullPath);
|
|
5360
|
-
if (currentHash !== entry.hash) {
|
|
5361
|
-
modifiedFiles.push(relPath);
|
|
5362
|
-
}
|
|
5363
|
-
}
|
|
5364
|
-
return modifiedFiles;
|
|
5365
|
-
}
|
|
5366
|
-
/**
|
|
5367
|
-
* Full drift check: version + modifications
|
|
5368
|
-
*/
|
|
5369
|
-
static checkDrift(projectPath, currentVersion, runningVersion) {
|
|
5370
|
-
const modifiedFiles = this.detectModifiedFiles(projectPath);
|
|
5371
|
-
let type = "none";
|
|
5372
|
-
let hasDrift = false;
|
|
5373
|
-
if (currentVersion !== runningVersion) {
|
|
5374
|
-
hasDrift = true;
|
|
5375
|
-
type = "version";
|
|
5376
|
-
} else if (modifiedFiles.length > 0) {
|
|
5377
|
-
hasDrift = true;
|
|
5378
|
-
type = "modified";
|
|
5379
|
-
}
|
|
5380
|
-
return {
|
|
5381
|
-
hasDrift,
|
|
5382
|
-
type,
|
|
5383
|
-
modifiedFiles,
|
|
5384
|
-
version: {
|
|
5385
|
-
current: currentVersion || "0.0.0",
|
|
5386
|
-
running: runningVersion
|
|
5387
|
-
}
|
|
5388
|
-
};
|
|
5389
|
-
}
|
|
5390
|
-
};
|
|
5391
|
-
}
|
|
5392
|
-
});
|
|
5393
|
-
|
|
5394
5556
|
// src/mcp/ui/ConfigContext.tsx
|
|
5395
5557
|
var ConfigContext_exports = {};
|
|
5396
5558
|
__export(ConfigContext_exports, {
|
|
@@ -5399,15 +5561,15 @@ __export(ConfigContext_exports, {
|
|
|
5399
5561
|
useConfig: () => useConfig
|
|
5400
5562
|
});
|
|
5401
5563
|
import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
|
|
5402
|
-
import * as
|
|
5403
|
-
import * as
|
|
5564
|
+
import * as fs25 from "fs";
|
|
5565
|
+
import * as path27 from "path";
|
|
5404
5566
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
5405
5567
|
function getPackageVersion() {
|
|
5406
5568
|
try {
|
|
5407
5569
|
const agentCoreDir = getAgentCoreDir();
|
|
5408
|
-
const packageJsonPath =
|
|
5409
|
-
if (
|
|
5410
|
-
return JSON.parse(
|
|
5570
|
+
const packageJsonPath = path27.join(path27.dirname(agentCoreDir), "package.json");
|
|
5571
|
+
if (fs25.existsSync(packageJsonPath)) {
|
|
5572
|
+
return JSON.parse(fs25.readFileSync(packageJsonPath, "utf8")).version;
|
|
5411
5573
|
}
|
|
5412
5574
|
} catch (e) {
|
|
5413
5575
|
}
|
|
@@ -5474,16 +5636,16 @@ var init_ConfigContext = __esm({
|
|
|
5474
5636
|
});
|
|
5475
5637
|
|
|
5476
5638
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
5477
|
-
import * as
|
|
5478
|
-
import * as
|
|
5639
|
+
import * as fs26 from "fs";
|
|
5640
|
+
import * as path28 from "path";
|
|
5479
5641
|
function readSession(project, taskSlug) {
|
|
5480
5642
|
const rrceData = getProjectRRCEData(project);
|
|
5481
|
-
const sessionPath =
|
|
5482
|
-
if (!
|
|
5643
|
+
const sessionPath = path28.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5644
|
+
if (!fs26.existsSync(sessionPath)) {
|
|
5483
5645
|
return null;
|
|
5484
5646
|
}
|
|
5485
5647
|
try {
|
|
5486
|
-
const raw =
|
|
5648
|
+
const raw = fs26.readFileSync(sessionPath, "utf-8");
|
|
5487
5649
|
return JSON.parse(raw);
|
|
5488
5650
|
} catch {
|
|
5489
5651
|
return null;
|
|
@@ -5496,12 +5658,12 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
|
5496
5658
|
}
|
|
5497
5659
|
function readAgentTodos(project, taskSlug) {
|
|
5498
5660
|
const rrceData = getProjectRRCEData(project);
|
|
5499
|
-
const todosPath =
|
|
5500
|
-
if (!
|
|
5661
|
+
const todosPath = path28.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5662
|
+
if (!fs26.existsSync(todosPath)) {
|
|
5501
5663
|
return null;
|
|
5502
5664
|
}
|
|
5503
5665
|
try {
|
|
5504
|
-
const raw =
|
|
5666
|
+
const raw = fs26.readFileSync(todosPath, "utf-8");
|
|
5505
5667
|
return JSON.parse(raw);
|
|
5506
5668
|
} catch {
|
|
5507
5669
|
return null;
|
|
@@ -5514,8 +5676,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5514
5676
|
if (configPath.startsWith(rrceHome)) {
|
|
5515
5677
|
return "global";
|
|
5516
5678
|
}
|
|
5517
|
-
if (
|
|
5518
|
-
const content =
|
|
5679
|
+
if (fs26.existsSync(configPath)) {
|
|
5680
|
+
const content = fs26.readFileSync(configPath, "utf-8");
|
|
5519
5681
|
if (content.includes("mode: workspace")) return "workspace";
|
|
5520
5682
|
if (content.includes("mode: global")) return "global";
|
|
5521
5683
|
}
|
|
@@ -5525,7 +5687,7 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5525
5687
|
}
|
|
5526
5688
|
function getEffectiveGlobalBase() {
|
|
5527
5689
|
const dummy = resolveDataPath("global", "__rrce_dummy__", "");
|
|
5528
|
-
return
|
|
5690
|
+
return path28.dirname(path28.dirname(dummy));
|
|
5529
5691
|
}
|
|
5530
5692
|
function getProjectRRCEData(project) {
|
|
5531
5693
|
const workspaceRoot = project.sourcePath || project.path;
|
|
@@ -5534,19 +5696,19 @@ function getProjectRRCEData(project) {
|
|
|
5534
5696
|
}
|
|
5535
5697
|
function listProjectTasks(project) {
|
|
5536
5698
|
const rrceData = getProjectRRCEData(project);
|
|
5537
|
-
const tasksPath =
|
|
5538
|
-
if (!
|
|
5699
|
+
const tasksPath = path28.join(rrceData, "tasks");
|
|
5700
|
+
if (!fs26.existsSync(tasksPath)) {
|
|
5539
5701
|
return { projectName: project.name, tasksPath, tasks: [] };
|
|
5540
5702
|
}
|
|
5541
5703
|
const tasks = [];
|
|
5542
5704
|
try {
|
|
5543
|
-
const entries =
|
|
5705
|
+
const entries = fs26.readdirSync(tasksPath, { withFileTypes: true });
|
|
5544
5706
|
for (const entry of entries) {
|
|
5545
5707
|
if (!entry.isDirectory()) continue;
|
|
5546
|
-
const metaPath =
|
|
5547
|
-
if (!
|
|
5708
|
+
const metaPath = path28.join(tasksPath, entry.name, "meta.json");
|
|
5709
|
+
if (!fs26.existsSync(metaPath)) continue;
|
|
5548
5710
|
try {
|
|
5549
|
-
const raw =
|
|
5711
|
+
const raw = fs26.readFileSync(metaPath, "utf-8");
|
|
5550
5712
|
const meta = JSON.parse(raw);
|
|
5551
5713
|
if (!meta.task_slug) meta.task_slug = entry.name;
|
|
5552
5714
|
tasks.push(meta);
|
|
@@ -5565,18 +5727,18 @@ function listProjectTasks(project) {
|
|
|
5565
5727
|
}
|
|
5566
5728
|
function updateTaskStatus(project, taskSlug, status) {
|
|
5567
5729
|
const rrceData = getProjectRRCEData(project);
|
|
5568
|
-
const metaPath =
|
|
5569
|
-
if (!
|
|
5730
|
+
const metaPath = path28.join(rrceData, "tasks", taskSlug, "meta.json");
|
|
5731
|
+
if (!fs26.existsSync(metaPath)) {
|
|
5570
5732
|
return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
|
|
5571
5733
|
}
|
|
5572
5734
|
try {
|
|
5573
|
-
const meta = JSON.parse(
|
|
5735
|
+
const meta = JSON.parse(fs26.readFileSync(metaPath, "utf-8"));
|
|
5574
5736
|
const next = {
|
|
5575
5737
|
...meta,
|
|
5576
5738
|
status,
|
|
5577
5739
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5578
5740
|
};
|
|
5579
|
-
|
|
5741
|
+
fs26.writeFileSync(metaPath, JSON.stringify(next, null, 2));
|
|
5580
5742
|
return { ok: true, meta: next };
|
|
5581
5743
|
} catch (e) {
|
|
5582
5744
|
return { ok: false, error: String(e) };
|
|
@@ -5934,31 +6096,54 @@ var init_project_utils = __esm({
|
|
|
5934
6096
|
}
|
|
5935
6097
|
});
|
|
5936
6098
|
|
|
5937
|
-
// src/mcp/ui/
|
|
5938
|
-
import
|
|
5939
|
-
import { Box as Box4, Text as Text4
|
|
5940
|
-
import * as fs26 from "fs";
|
|
5941
|
-
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";
|
|
5942
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";
|
|
5943
6128
|
function getIndexStats(project) {
|
|
5944
6129
|
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
5945
6130
|
try {
|
|
5946
6131
|
const knowledgePath = project.knowledgePath;
|
|
5947
6132
|
if (knowledgePath) {
|
|
5948
|
-
const embPath =
|
|
5949
|
-
const codeEmbPath =
|
|
5950
|
-
if (
|
|
5951
|
-
const stat =
|
|
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);
|
|
5952
6137
|
stats.lastIndexed = stat.mtime.toISOString();
|
|
5953
6138
|
try {
|
|
5954
|
-
const data = JSON.parse(
|
|
6139
|
+
const data = JSON.parse(fs27.readFileSync(embPath, "utf-8"));
|
|
5955
6140
|
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5956
6141
|
} catch {
|
|
5957
6142
|
}
|
|
5958
6143
|
}
|
|
5959
|
-
if (
|
|
6144
|
+
if (fs27.existsSync(codeEmbPath)) {
|
|
5960
6145
|
try {
|
|
5961
|
-
const data = JSON.parse(
|
|
6146
|
+
const data = JSON.parse(fs27.readFileSync(codeEmbPath, "utf-8"));
|
|
5962
6147
|
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5963
6148
|
} catch {
|
|
5964
6149
|
}
|
|
@@ -5968,6 +6153,16 @@ function getIndexStats(project) {
|
|
|
5968
6153
|
}
|
|
5969
6154
|
return stats;
|
|
5970
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";
|
|
5971
6166
|
var ProjectsView;
|
|
5972
6167
|
var init_ProjectsView = __esm({
|
|
5973
6168
|
"src/mcp/ui/ProjectsView.tsx"() {
|
|
@@ -5979,6 +6174,8 @@ var init_ProjectsView = __esm({
|
|
|
5979
6174
|
init_config_utils();
|
|
5980
6175
|
init_project_utils();
|
|
5981
6176
|
init_ui_helpers();
|
|
6177
|
+
init_ProjectViews();
|
|
6178
|
+
init_projects2();
|
|
5982
6179
|
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
|
|
5983
6180
|
const { driftReports, checkAllDrift } = useConfig();
|
|
5984
6181
|
const [config, setConfig] = useState3(initialConfig);
|
|
@@ -6084,20 +6281,10 @@ ${statsRow}` : label;
|
|
|
6084
6281
|
setConfig(newConfig);
|
|
6085
6282
|
onConfigChange?.();
|
|
6086
6283
|
};
|
|
6087
|
-
return /* @__PURE__ */
|
|
6088
|
-
/* @__PURE__ */
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
/* @__PURE__ */ jsx5(Text4, { color: "dim", children: " \u2502 " }),
|
|
6092
|
-
/* @__PURE__ */ jsxs3(Text4, { color: config.defaults.includeNew ? "green" : "red", children: [
|
|
6093
|
-
"Auto-expose: ",
|
|
6094
|
-
config.defaults.includeNew ? "ON" : "OFF"
|
|
6095
|
-
] })
|
|
6096
|
-
] }),
|
|
6097
|
-
/* @__PURE__ */ jsx5(Text4, { color: "dim", children: "v0.3.14" })
|
|
6098
|
-
] }),
|
|
6099
|
-
/* @__PURE__ */ jsx5(Box4, { marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Manage which projects are exposed to the MCP server." }) }),
|
|
6100
|
-
/* @__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(
|
|
6101
6288
|
SimpleSelect,
|
|
6102
6289
|
{
|
|
6103
6290
|
message: "",
|
|
@@ -6112,111 +6299,7 @@ ${statsRow}` : label;
|
|
|
6112
6299
|
},
|
|
6113
6300
|
JSON.stringify(initialSelected) + config.defaults.includeNew + JSON.stringify(indexingStats)
|
|
6114
6301
|
) }),
|
|
6115
|
-
/* @__PURE__ */
|
|
6116
|
-
/* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Space:Select Enter:Save a:Toggle Auto u:Refresh Drift" }),
|
|
6117
|
-
/* @__PURE__ */ jsx5(Text4, { color: "dim", children: "Use 'rrce-workflow wizard' for advanced config" })
|
|
6118
|
-
] })
|
|
6119
|
-
] });
|
|
6120
|
-
};
|
|
6121
|
-
}
|
|
6122
|
-
});
|
|
6123
|
-
|
|
6124
|
-
// src/mcp/ui/components/TaskRow.tsx
|
|
6125
|
-
import "react";
|
|
6126
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
6127
|
-
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
6128
|
-
function getActiveAgent(task) {
|
|
6129
|
-
if (!task.agents) return null;
|
|
6130
|
-
for (const [agent, info] of Object.entries(task.agents)) {
|
|
6131
|
-
if (info?.status === "in_progress") {
|
|
6132
|
-
return { agent, status: "in_progress" };
|
|
6133
|
-
}
|
|
6134
|
-
}
|
|
6135
|
-
const agentOrder = ["documentation", "executor", "planning", "research"];
|
|
6136
|
-
for (const agent of agentOrder) {
|
|
6137
|
-
if (task.agents[agent]?.status === "complete") {
|
|
6138
|
-
return { agent, status: "complete" };
|
|
6139
|
-
}
|
|
6140
|
-
}
|
|
6141
|
-
return null;
|
|
6142
|
-
}
|
|
6143
|
-
var TaskRow;
|
|
6144
|
-
var init_TaskRow = __esm({
|
|
6145
|
-
"src/mcp/ui/components/TaskRow.tsx"() {
|
|
6146
|
-
"use strict";
|
|
6147
|
-
init_ui_helpers();
|
|
6148
|
-
init_project_utils();
|
|
6149
|
-
TaskRow = ({
|
|
6150
|
-
row,
|
|
6151
|
-
isSelected,
|
|
6152
|
-
isExpanded,
|
|
6153
|
-
taskCount,
|
|
6154
|
-
hasDrift,
|
|
6155
|
-
isCurrentProject = false,
|
|
6156
|
-
isLastTask = false
|
|
6157
|
-
}) => {
|
|
6158
|
-
if (row.kind === "project") {
|
|
6159
|
-
const projectColor = isSelected ? "cyan" : isCurrentProject ? "yellow" : "white";
|
|
6160
|
-
const isBold = isSelected || isCurrentProject;
|
|
6161
|
-
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
6162
|
-
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
|
|
6163
|
-
/* @__PURE__ */ jsxs4(Text5, { bold: isBold, color: projectColor, children: [
|
|
6164
|
-
getFolderIcon(isExpanded),
|
|
6165
|
-
" ",
|
|
6166
|
-
formatProjectLabel(row.project)
|
|
6167
|
-
] }),
|
|
6168
|
-
isCurrentProject && /* @__PURE__ */ jsx6(Text5, { color: "yellow", dimColor: true, children: " (current)" }),
|
|
6169
|
-
hasDrift && /* @__PURE__ */ jsx6(Text5, { color: "magenta", children: " \u26A0" }),
|
|
6170
|
-
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6171
|
-
" ",
|
|
6172
|
-
taskCount > 0 ? `[${taskCount}]` : ""
|
|
6173
|
-
] })
|
|
6174
|
-
] });
|
|
6175
|
-
}
|
|
6176
|
-
const task = row.task;
|
|
6177
|
-
const taskLabel = task.title || task.task_slug;
|
|
6178
|
-
const status = task.status || "";
|
|
6179
|
-
const isPlaceholder = task.task_slug === "__none__";
|
|
6180
|
-
const branch = getTreeBranch(isLastTask);
|
|
6181
|
-
const activeAgent = getActiveAgent(task);
|
|
6182
|
-
const progress = getChecklistProgress(task.checklist || []);
|
|
6183
|
-
const relativeTime = task.updated_at ? formatRelativeTime(task.updated_at) : "";
|
|
6184
|
-
if (isPlaceholder) {
|
|
6185
|
-
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
6186
|
-
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6187
|
-
" ",
|
|
6188
|
-
branch,
|
|
6189
|
-
" "
|
|
6190
|
-
] }),
|
|
6191
|
-
/* @__PURE__ */ jsx6(Text5, { color: "dim", italic: true, children: taskLabel })
|
|
6192
|
-
] });
|
|
6193
|
-
}
|
|
6194
|
-
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
6195
|
-
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
|
|
6196
|
-
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6197
|
-
branch,
|
|
6198
|
-
" "
|
|
6199
|
-
] }),
|
|
6200
|
-
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: "\u{1F4CB} " }),
|
|
6201
|
-
/* @__PURE__ */ jsx6(Box5, { flexGrow: 1, children: /* @__PURE__ */ jsx6(Text5, { bold: isSelected, color: isSelected ? "cyan" : "white", children: taskLabel.length > 25 ? taskLabel.substring(0, 22) + "..." : taskLabel }) }),
|
|
6202
|
-
activeAgent && /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
6203
|
-
/* @__PURE__ */ jsx6(Text5, { color: "dim", children: " " }),
|
|
6204
|
-
/* @__PURE__ */ jsx6(Text5, { children: getPhaseIcon(activeAgent.agent) }),
|
|
6205
|
-
/* @__PURE__ */ jsx6(Text5, { color: activeAgent.status === "in_progress" ? "yellow" : "green", children: getAgentStatusIcon(activeAgent.status) })
|
|
6206
|
-
] }),
|
|
6207
|
-
progress.total > 0 && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6208
|
-
" ",
|
|
6209
|
-
getProgressBar(progress.percentage, 6),
|
|
6210
|
-
" ",
|
|
6211
|
-
progress.completed,
|
|
6212
|
-
"/",
|
|
6213
|
-
progress.total
|
|
6214
|
-
] }),
|
|
6215
|
-
relativeTime && relativeTime !== "\u2014" && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6216
|
-
" ",
|
|
6217
|
-
relativeTime
|
|
6218
|
-
] }),
|
|
6219
|
-
!activeAgent && status && /* @__PURE__ */ jsx6(Text5, { color: getStatusColor(status), children: ` ${getStatusIcon(status)} ${status}` })
|
|
6302
|
+
/* @__PURE__ */ jsx6(ProjectsFooter, {})
|
|
6220
6303
|
] });
|
|
6221
6304
|
};
|
|
6222
6305
|
}
|
|
@@ -6346,10 +6429,184 @@ var init_TaskDetails = __esm({
|
|
|
6346
6429
|
}
|
|
6347
6430
|
});
|
|
6348
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
|
+
|
|
6349
6606
|
// src/mcp/ui/TasksView.tsx
|
|
6350
6607
|
import { useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
|
|
6351
|
-
import { Box as
|
|
6352
|
-
import { jsx as
|
|
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";
|
|
6353
6610
|
function nextStatus(current) {
|
|
6354
6611
|
const idx = STATUS_CYCLE.indexOf(current || "");
|
|
6355
6612
|
if (idx === -1) return STATUS_CYCLE[0];
|
|
@@ -6362,8 +6619,9 @@ var init_TasksView = __esm({
|
|
|
6362
6619
|
init_tasks_fs();
|
|
6363
6620
|
init_ConfigContext();
|
|
6364
6621
|
init_project_utils();
|
|
6365
|
-
init_TaskRow();
|
|
6366
6622
|
init_TaskDetails();
|
|
6623
|
+
init_TaskTree();
|
|
6624
|
+
init_TaskViews();
|
|
6367
6625
|
STATUS_CYCLE = ["pending", "in_progress", "blocked", "complete"];
|
|
6368
6626
|
TasksView = ({ projects: allProjects, workspacePath }) => {
|
|
6369
6627
|
const { driftReports } = useConfig();
|
|
@@ -6484,55 +6742,26 @@ var init_TasksView = __esm({
|
|
|
6484
6742
|
const selectedRow = flattenedRows[selectedIndex];
|
|
6485
6743
|
const selectedTask = selectedRow?.kind === "task" && selectedRow.task.task_slug !== "__none__" ? selectedRow.task : null;
|
|
6486
6744
|
const totalTasks = Object.values(taskCache).flat().length;
|
|
6487
|
-
return /* @__PURE__ */
|
|
6488
|
-
/* @__PURE__ */
|
|
6489
|
-
|
|
6490
|
-
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
|
|
6491
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2502 " }),
|
|
6492
|
-
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
6493
|
-
sortedProjects.length,
|
|
6494
|
-
" projects"
|
|
6495
|
-
] }),
|
|
6496
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2022 " }),
|
|
6497
|
-
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
6498
|
-
totalTasks,
|
|
6499
|
-
" tasks"
|
|
6500
|
-
] })
|
|
6501
|
-
] }),
|
|
6502
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "v0.3.14" })
|
|
6503
|
-
] }),
|
|
6504
|
-
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: [
|
|
6505
6748
|
"\u26A0 ",
|
|
6506
6749
|
errorLine
|
|
6507
6750
|
] }) }),
|
|
6508
|
-
/* @__PURE__ */
|
|
6509
|
-
/* @__PURE__ */
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
isSelected: idx === selectedIndex,
|
|
6521
|
-
isExpanded: expanded.has(k),
|
|
6522
|
-
taskCount: (taskCache[k] || []).length,
|
|
6523
|
-
hasDrift: !!driftReports[row.project.path]?.hasDrift,
|
|
6524
|
-
isCurrentProject,
|
|
6525
|
-
isLastTask
|
|
6526
|
-
},
|
|
6527
|
-
row.kind === "project" ? `p:${k}` : `t:${k}:${row.task.task_slug}`
|
|
6528
|
-
);
|
|
6529
|
-
}) }) }),
|
|
6530
|
-
/* @__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 }) })
|
|
6531
6763
|
] }),
|
|
6532
|
-
/* @__PURE__ */
|
|
6533
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "\u2191\u2193:Nav Enter:Expand s:Cycle Status R:Refresh" }),
|
|
6534
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "Press 'q' to exit" })
|
|
6535
|
-
] })
|
|
6764
|
+
/* @__PURE__ */ jsx11(TasksFooter, {})
|
|
6536
6765
|
] });
|
|
6537
6766
|
};
|
|
6538
6767
|
}
|
|
@@ -6540,8 +6769,8 @@ var init_TasksView = __esm({
|
|
|
6540
6769
|
|
|
6541
6770
|
// src/mcp/ui/LogViewer.tsx
|
|
6542
6771
|
import "react";
|
|
6543
|
-
import { Box as
|
|
6544
|
-
import { jsx as
|
|
6772
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
6773
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
6545
6774
|
var LogViewer;
|
|
6546
6775
|
var init_LogViewer = __esm({
|
|
6547
6776
|
"src/mcp/ui/LogViewer.tsx"() {
|
|
@@ -6551,16 +6780,16 @@ var init_LogViewer = __esm({
|
|
|
6551
6780
|
const emptyLines = Math.max(0, height - visibleLogs.length);
|
|
6552
6781
|
const padding = Array(emptyLines).fill("");
|
|
6553
6782
|
const formatLog = (log) => {
|
|
6554
|
-
if (log.includes("[RAG]")) return /* @__PURE__ */
|
|
6555
|
-
if (log.includes("[ERROR]")) return /* @__PURE__ */
|
|
6556
|
-
if (log.includes("[WARN]")) return /* @__PURE__ */
|
|
6557
|
-
if (log.includes("[INFO]")) return /* @__PURE__ */
|
|
6558
|
-
if (log.includes("Success")) return /* @__PURE__ */
|
|
6559
|
-
return /* @__PURE__ */
|
|
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 });
|
|
6560
6789
|
};
|
|
6561
|
-
return /* @__PURE__ */
|
|
6562
|
-
padding.map((_, i) => /* @__PURE__ */
|
|
6563
|
-
visibleLogs.map((log, i) => /* @__PURE__ */
|
|
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}`))
|
|
6564
6793
|
] });
|
|
6565
6794
|
};
|
|
6566
6795
|
}
|
|
@@ -6568,28 +6797,28 @@ var init_LogViewer = __esm({
|
|
|
6568
6797
|
|
|
6569
6798
|
// src/mcp/ui/StatusBoard.tsx
|
|
6570
6799
|
import "react";
|
|
6571
|
-
import { Box as
|
|
6572
|
-
import { jsx as
|
|
6800
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
6801
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
6573
6802
|
var StatusBoard;
|
|
6574
6803
|
var init_StatusBoard = __esm({
|
|
6575
6804
|
"src/mcp/ui/StatusBoard.tsx"() {
|
|
6576
6805
|
"use strict";
|
|
6577
6806
|
StatusBoard = ({ exposedLabel, port, pid, running, hasDrift }) => {
|
|
6578
|
-
return /* @__PURE__ */
|
|
6579
|
-
running ? /* @__PURE__ */
|
|
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" }),
|
|
6580
6809
|
" ",
|
|
6581
6810
|
"\u2502",
|
|
6582
6811
|
" \u{1F4CB} ",
|
|
6583
|
-
/* @__PURE__ */
|
|
6812
|
+
/* @__PURE__ */ jsx13(Text12, { color: "yellow", children: exposedLabel }),
|
|
6584
6813
|
" ",
|
|
6585
6814
|
"\u2502",
|
|
6586
6815
|
" Port: ",
|
|
6587
|
-
/* @__PURE__ */
|
|
6816
|
+
/* @__PURE__ */ jsx13(Text12, { color: "green", children: port }),
|
|
6588
6817
|
" ",
|
|
6589
6818
|
"\u2502",
|
|
6590
6819
|
" PID: ",
|
|
6591
|
-
/* @__PURE__ */
|
|
6592
|
-
hasDrift && /* @__PURE__ */
|
|
6820
|
+
/* @__PURE__ */ jsx13(Text12, { color: "green", children: pid }),
|
|
6821
|
+
hasDrift && /* @__PURE__ */ jsxs11(Text12, { color: "magenta", bold: true, children: [
|
|
6593
6822
|
" ",
|
|
6594
6823
|
"\u2502",
|
|
6595
6824
|
" \u2B06 UPDATE AVAILABLE"
|
|
@@ -6601,8 +6830,8 @@ var init_StatusBoard = __esm({
|
|
|
6601
6830
|
|
|
6602
6831
|
// src/mcp/ui/components/TabBar.tsx
|
|
6603
6832
|
import "react";
|
|
6604
|
-
import { Box as
|
|
6605
|
-
import { jsx as
|
|
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";
|
|
6606
6835
|
var TabBar;
|
|
6607
6836
|
var init_TabBar = __esm({
|
|
6608
6837
|
"src/mcp/ui/components/TabBar.tsx"() {
|
|
@@ -6632,11 +6861,11 @@ var init_TabBar = __esm({
|
|
|
6632
6861
|
if (tab) onChange(tab.id);
|
|
6633
6862
|
}
|
|
6634
6863
|
});
|
|
6635
|
-
return /* @__PURE__ */
|
|
6864
|
+
return /* @__PURE__ */ jsxs12(Box13, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [
|
|
6636
6865
|
tabs.map((tab, index) => {
|
|
6637
6866
|
const isActive = tab.id === activeTab;
|
|
6638
|
-
return /* @__PURE__ */
|
|
6639
|
-
|
|
6867
|
+
return /* @__PURE__ */ jsx14(Box13, { marginRight: 2, children: /* @__PURE__ */ jsx14(
|
|
6868
|
+
Text13,
|
|
6640
6869
|
{
|
|
6641
6870
|
color: isActive ? "cyan" : "white",
|
|
6642
6871
|
bold: isActive,
|
|
@@ -6645,8 +6874,8 @@ var init_TabBar = __esm({
|
|
|
6645
6874
|
}
|
|
6646
6875
|
) }, tab.id);
|
|
6647
6876
|
}),
|
|
6648
|
-
/* @__PURE__ */
|
|
6649
|
-
/* @__PURE__ */
|
|
6877
|
+
/* @__PURE__ */ jsx14(Box13, { flexGrow: 1 }),
|
|
6878
|
+
/* @__PURE__ */ jsx14(Text13, { color: "dim", children: "Use \u25C4/\u25BA arrows to navigate" })
|
|
6650
6879
|
] });
|
|
6651
6880
|
};
|
|
6652
6881
|
}
|
|
@@ -6658,9 +6887,9 @@ __export(App_exports, {
|
|
|
6658
6887
|
App: () => App
|
|
6659
6888
|
});
|
|
6660
6889
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
6661
|
-
import { Box as
|
|
6662
|
-
import
|
|
6663
|
-
import { jsx as
|
|
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";
|
|
6664
6893
|
var App;
|
|
6665
6894
|
var init_App = __esm({
|
|
6666
6895
|
"src/mcp/ui/App.tsx"() {
|
|
@@ -6732,18 +6961,18 @@ var init_App = __esm({
|
|
|
6732
6961
|
useEffect6(() => {
|
|
6733
6962
|
const logPath = getLogFilePath();
|
|
6734
6963
|
let lastSize = 0;
|
|
6735
|
-
if (
|
|
6736
|
-
const stats =
|
|
6964
|
+
if (fs28.existsSync(logPath)) {
|
|
6965
|
+
const stats = fs28.statSync(logPath);
|
|
6737
6966
|
lastSize = stats.size;
|
|
6738
6967
|
}
|
|
6739
6968
|
const interval = setInterval(() => {
|
|
6740
|
-
if (
|
|
6741
|
-
const stats =
|
|
6969
|
+
if (fs28.existsSync(logPath)) {
|
|
6970
|
+
const stats = fs28.statSync(logPath);
|
|
6742
6971
|
if (stats.size > lastSize) {
|
|
6743
6972
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
6744
|
-
const fd =
|
|
6745
|
-
|
|
6746
|
-
|
|
6973
|
+
const fd = fs28.openSync(logPath, "r");
|
|
6974
|
+
fs28.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
6975
|
+
fs28.closeSync(fd);
|
|
6747
6976
|
const newContent = buffer.toString("utf-8");
|
|
6748
6977
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
6749
6978
|
setLogs((prev) => {
|
|
@@ -6780,10 +7009,10 @@ var init_App = __esm({
|
|
|
6780
7009
|
const handleConfigChange = useCallback3(() => {
|
|
6781
7010
|
refreshData();
|
|
6782
7011
|
}, [refreshData]);
|
|
6783
|
-
return /* @__PURE__ */
|
|
6784
|
-
/* @__PURE__ */
|
|
6785
|
-
/* @__PURE__ */
|
|
6786
|
-
activeTab === "overview" && /* @__PURE__ */
|
|
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(
|
|
6787
7016
|
Overview,
|
|
6788
7017
|
{
|
|
6789
7018
|
serverStatus: serverInfo,
|
|
@@ -6795,11 +7024,11 @@ var init_App = __esm({
|
|
|
6795
7024
|
logs
|
|
6796
7025
|
}
|
|
6797
7026
|
),
|
|
6798
|
-
activeTab === "logs" && /* @__PURE__ */
|
|
6799
|
-
activeTab === "tasks" && /* @__PURE__ */
|
|
6800
|
-
activeTab === "projects" && /* @__PURE__ */
|
|
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 })
|
|
6801
7030
|
] }),
|
|
6802
|
-
/* @__PURE__ */
|
|
7031
|
+
/* @__PURE__ */ jsx15(Box14, { marginTop: 0, children: /* @__PURE__ */ jsx15(
|
|
6803
7032
|
StatusBoard,
|
|
6804
7033
|
{
|
|
6805
7034
|
exposedLabel: `${exposedProjects.length} / ${projects.length} projects`,
|
|
@@ -6817,7 +7046,7 @@ var init_App = __esm({
|
|
|
6817
7046
|
// src/mcp/commands/start.ts
|
|
6818
7047
|
import { confirm as confirm3, isCancel as isCancel5, text } from "@clack/prompts";
|
|
6819
7048
|
async function handleStartServer() {
|
|
6820
|
-
const
|
|
7049
|
+
const React16 = await import("react");
|
|
6821
7050
|
const { render } = await import("ink");
|
|
6822
7051
|
const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
|
|
6823
7052
|
const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
|
|
@@ -6860,10 +7089,10 @@ async function handleStartServer() {
|
|
|
6860
7089
|
}
|
|
6861
7090
|
process.stdin.resume();
|
|
6862
7091
|
const app = render(
|
|
6863
|
-
|
|
7092
|
+
React16.createElement(
|
|
6864
7093
|
ConfigProvider2,
|
|
6865
7094
|
null,
|
|
6866
|
-
|
|
7095
|
+
React16.createElement(App2, {
|
|
6867
7096
|
initialPort,
|
|
6868
7097
|
onExit: () => {
|
|
6869
7098
|
}
|
|
@@ -6968,15 +7197,15 @@ __export(update_flow_exports, {
|
|
|
6968
7197
|
});
|
|
6969
7198
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
6970
7199
|
import pc8 from "picocolors";
|
|
6971
|
-
import * as
|
|
6972
|
-
import * as
|
|
7200
|
+
import * as fs29 from "fs";
|
|
7201
|
+
import * as path30 from "path";
|
|
6973
7202
|
import { stringify as stringify2, parse } from "yaml";
|
|
6974
7203
|
function backupFile(filePath) {
|
|
6975
|
-
if (!
|
|
7204
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
6976
7205
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
6977
7206
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
6978
7207
|
try {
|
|
6979
|
-
|
|
7208
|
+
fs29.copyFileSync(filePath, backupPath);
|
|
6980
7209
|
return backupPath;
|
|
6981
7210
|
} catch (e) {
|
|
6982
7211
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -6986,9 +7215,9 @@ function backupFile(filePath) {
|
|
|
6986
7215
|
function getPackageVersion2() {
|
|
6987
7216
|
try {
|
|
6988
7217
|
const agentCoreDir = getAgentCoreDir();
|
|
6989
|
-
const packageJsonPath =
|
|
6990
|
-
if (
|
|
6991
|
-
return JSON.parse(
|
|
7218
|
+
const packageJsonPath = path30.join(path30.dirname(agentCoreDir), "package.json");
|
|
7219
|
+
if (fs29.existsSync(packageJsonPath)) {
|
|
7220
|
+
return JSON.parse(fs29.readFileSync(packageJsonPath, "utf8")).version;
|
|
6992
7221
|
}
|
|
6993
7222
|
} catch (e) {
|
|
6994
7223
|
}
|
|
@@ -7003,9 +7232,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7003
7232
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7004
7233
|
const configFilePath = getConfigPath(workspacePath);
|
|
7005
7234
|
let currentSyncedVersion;
|
|
7006
|
-
if (
|
|
7235
|
+
if (fs29.existsSync(configFilePath)) {
|
|
7007
7236
|
try {
|
|
7008
|
-
const content =
|
|
7237
|
+
const content = fs29.readFileSync(configFilePath, "utf-8");
|
|
7009
7238
|
const config = parse(content);
|
|
7010
7239
|
currentSyncedVersion = config.last_synced_version;
|
|
7011
7240
|
} catch (e) {
|
|
@@ -7013,8 +7242,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7013
7242
|
}
|
|
7014
7243
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
7015
7244
|
const ideTargets = [];
|
|
7016
|
-
if (
|
|
7017
|
-
const configContent =
|
|
7245
|
+
if (fs29.existsSync(configFilePath)) {
|
|
7246
|
+
const configContent = fs29.readFileSync(configFilePath, "utf-8");
|
|
7018
7247
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7019
7248
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7020
7249
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7023,14 +7252,14 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7023
7252
|
const dirs = ["templates", "prompts", "docs"];
|
|
7024
7253
|
const updatedFiles = [];
|
|
7025
7254
|
for (const dir of dirs) {
|
|
7026
|
-
const srcDir =
|
|
7027
|
-
if (!
|
|
7255
|
+
const srcDir = path30.join(agentCoreDir, dir);
|
|
7256
|
+
if (!fs29.existsSync(srcDir)) continue;
|
|
7028
7257
|
const syncFiles = (src, rel) => {
|
|
7029
|
-
const entries =
|
|
7258
|
+
const entries = fs29.readdirSync(src, { withFileTypes: true });
|
|
7030
7259
|
for (const entry of entries) {
|
|
7031
|
-
const entrySrc =
|
|
7032
|
-
const entryRel =
|
|
7033
|
-
const entryDest =
|
|
7260
|
+
const entrySrc = path30.join(src, entry.name);
|
|
7261
|
+
const entryRel = path30.join(rel, entry.name);
|
|
7262
|
+
const entryDest = path30.join(dataPath, entryRel);
|
|
7034
7263
|
if (entry.isDirectory()) {
|
|
7035
7264
|
ensureDir(entryDest);
|
|
7036
7265
|
syncFiles(entrySrc, entryRel);
|
|
@@ -7038,8 +7267,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7038
7267
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
7039
7268
|
backupFile(entryDest);
|
|
7040
7269
|
}
|
|
7041
|
-
ensureDir(
|
|
7042
|
-
|
|
7270
|
+
ensureDir(path30.dirname(entryDest));
|
|
7271
|
+
fs29.copyFileSync(entrySrc, entryDest);
|
|
7043
7272
|
updatedFiles.push(entryRel);
|
|
7044
7273
|
}
|
|
7045
7274
|
}
|
|
@@ -7050,12 +7279,12 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7050
7279
|
DriftService.saveManifest(dataPath, manifest);
|
|
7051
7280
|
}
|
|
7052
7281
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
7053
|
-
ensureDir(
|
|
7054
|
-
ensureDir(
|
|
7055
|
-
copyDirRecursive(
|
|
7056
|
-
copyDirRecursive(
|
|
7057
|
-
if (
|
|
7058
|
-
const configContent =
|
|
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");
|
|
7059
7288
|
if (configContent.includes("copilot: true")) {
|
|
7060
7289
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7061
7290
|
ensureDir(copilotPath);
|
|
@@ -7077,21 +7306,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7077
7306
|
try {
|
|
7078
7307
|
const yaml = parse(configContent);
|
|
7079
7308
|
yaml.last_synced_version = runningVersion;
|
|
7080
|
-
|
|
7309
|
+
fs29.writeFileSync(configFilePath, stringify2(yaml));
|
|
7081
7310
|
} catch (e) {
|
|
7082
7311
|
console.error("Failed to update config.yaml version:", e);
|
|
7083
7312
|
}
|
|
7084
7313
|
}
|
|
7085
|
-
const mcpPath =
|
|
7086
|
-
if (
|
|
7314
|
+
const mcpPath = path30.join(rrceHome, "mcp.yaml");
|
|
7315
|
+
if (fs29.existsSync(mcpPath)) {
|
|
7087
7316
|
try {
|
|
7088
|
-
const content =
|
|
7317
|
+
const content = fs29.readFileSync(mcpPath, "utf-8");
|
|
7089
7318
|
const yaml = parse(content);
|
|
7090
7319
|
if (yaml.projects) {
|
|
7091
7320
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7092
7321
|
if (project) {
|
|
7093
7322
|
project.last_synced_version = runningVersion;
|
|
7094
|
-
|
|
7323
|
+
fs29.writeFileSync(mcpPath, stringify2(yaml));
|
|
7095
7324
|
}
|
|
7096
7325
|
}
|
|
7097
7326
|
} catch (e) {
|
|
@@ -7127,9 +7356,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7127
7356
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7128
7357
|
const configFilePath = getConfigPath(workspacePath);
|
|
7129
7358
|
let currentSyncedVersion;
|
|
7130
|
-
if (
|
|
7359
|
+
if (fs29.existsSync(configFilePath)) {
|
|
7131
7360
|
try {
|
|
7132
|
-
const content =
|
|
7361
|
+
const content = fs29.readFileSync(configFilePath, "utf-8");
|
|
7133
7362
|
const config = parse(content);
|
|
7134
7363
|
currentSyncedVersion = config.last_synced_version;
|
|
7135
7364
|
} catch (e) {
|
|
@@ -7153,8 +7382,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7153
7382
|
` \u2022 docs/ (documentation)`
|
|
7154
7383
|
];
|
|
7155
7384
|
const ideTargets = [];
|
|
7156
|
-
if (
|
|
7157
|
-
const configContent =
|
|
7385
|
+
if (fs29.existsSync(configFilePath)) {
|
|
7386
|
+
const configContent = fs29.readFileSync(configFilePath, "utf-8");
|
|
7158
7387
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7159
7388
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7160
7389
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7183,14 +7412,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7183
7412
|
const dirs = ["templates", "prompts", "docs"];
|
|
7184
7413
|
const updatedFiles = [];
|
|
7185
7414
|
for (const dir of dirs) {
|
|
7186
|
-
const srcDir =
|
|
7187
|
-
if (!
|
|
7415
|
+
const srcDir = path30.join(agentCoreDir, dir);
|
|
7416
|
+
if (!fs29.existsSync(srcDir)) continue;
|
|
7188
7417
|
const syncFiles = (src, rel) => {
|
|
7189
|
-
const entries =
|
|
7418
|
+
const entries = fs29.readdirSync(src, { withFileTypes: true });
|
|
7190
7419
|
for (const entry of entries) {
|
|
7191
|
-
const entrySrc =
|
|
7192
|
-
const entryRel =
|
|
7193
|
-
const entryDest =
|
|
7420
|
+
const entrySrc = path30.join(src, entry.name);
|
|
7421
|
+
const entryRel = path30.join(rel, entry.name);
|
|
7422
|
+
const entryDest = path30.join(dataPath, entryRel);
|
|
7194
7423
|
if (entry.isDirectory()) {
|
|
7195
7424
|
ensureDir(entryDest);
|
|
7196
7425
|
syncFiles(entrySrc, entryRel);
|
|
@@ -7198,8 +7427,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7198
7427
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
7199
7428
|
backupFile(entryDest);
|
|
7200
7429
|
}
|
|
7201
|
-
ensureDir(
|
|
7202
|
-
|
|
7430
|
+
ensureDir(path30.dirname(entryDest));
|
|
7431
|
+
fs29.copyFileSync(entrySrc, entryDest);
|
|
7203
7432
|
updatedFiles.push(entryRel);
|
|
7204
7433
|
}
|
|
7205
7434
|
}
|
|
@@ -7210,12 +7439,12 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7210
7439
|
DriftService.saveManifest(dataPath, manifest);
|
|
7211
7440
|
}
|
|
7212
7441
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
7213
|
-
ensureDir(
|
|
7214
|
-
ensureDir(
|
|
7215
|
-
copyDirRecursive(
|
|
7216
|
-
copyDirRecursive(
|
|
7217
|
-
if (
|
|
7218
|
-
const configContent =
|
|
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");
|
|
7219
7448
|
if (configContent.includes("copilot: true")) {
|
|
7220
7449
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7221
7450
|
ensureDir(copilotPath);
|
|
@@ -7237,21 +7466,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7237
7466
|
try {
|
|
7238
7467
|
const yaml = parse(configContent);
|
|
7239
7468
|
yaml.last_synced_version = runningVersion;
|
|
7240
|
-
|
|
7469
|
+
fs29.writeFileSync(configFilePath, stringify2(yaml));
|
|
7241
7470
|
} catch (e) {
|
|
7242
7471
|
console.error("Failed to update config.yaml version:", e);
|
|
7243
7472
|
}
|
|
7244
7473
|
}
|
|
7245
|
-
const mcpPath =
|
|
7246
|
-
if (
|
|
7474
|
+
const mcpPath = path30.join(rrceHome, "mcp.yaml");
|
|
7475
|
+
if (fs29.existsSync(mcpPath)) {
|
|
7247
7476
|
try {
|
|
7248
|
-
const content =
|
|
7477
|
+
const content = fs29.readFileSync(mcpPath, "utf-8");
|
|
7249
7478
|
const yaml = parse(content);
|
|
7250
7479
|
if (yaml.projects) {
|
|
7251
7480
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7252
7481
|
if (project) {
|
|
7253
7482
|
project.last_synced_version = runningVersion;
|
|
7254
|
-
|
|
7483
|
+
fs29.writeFileSync(mcpPath, stringify2(yaml));
|
|
7255
7484
|
}
|
|
7256
7485
|
}
|
|
7257
7486
|
} catch (e) {
|
|
@@ -7286,8 +7515,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7286
7515
|
}
|
|
7287
7516
|
}
|
|
7288
7517
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
7289
|
-
const globalPath =
|
|
7290
|
-
const workspacePath =
|
|
7518
|
+
const globalPath = path30.join(customGlobalPath, "workspaces", workspaceName);
|
|
7519
|
+
const workspacePath = path30.join(workspaceRoot, ".rrce-workflow");
|
|
7291
7520
|
switch (mode) {
|
|
7292
7521
|
case "global":
|
|
7293
7522
|
return [globalPath];
|
|
@@ -7310,8 +7539,8 @@ var init_update_flow = __esm({
|
|
|
7310
7539
|
// src/commands/wizard/index.ts
|
|
7311
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";
|
|
7312
7541
|
import pc13 from "picocolors";
|
|
7313
|
-
import * as
|
|
7314
|
-
import * as
|
|
7542
|
+
import * as fs33 from "fs";
|
|
7543
|
+
import * as path32 from "path";
|
|
7315
7544
|
import { parse as parse2 } from "yaml";
|
|
7316
7545
|
|
|
7317
7546
|
// src/lib/git.ts
|
|
@@ -7453,22 +7682,22 @@ async function promptConfirmation() {
|
|
|
7453
7682
|
init_paths();
|
|
7454
7683
|
init_prompts();
|
|
7455
7684
|
init_utils();
|
|
7456
|
-
import * as
|
|
7457
|
-
import * as
|
|
7685
|
+
import * as fs11 from "fs";
|
|
7686
|
+
import * as path13 from "path";
|
|
7458
7687
|
import pc4 from "picocolors";
|
|
7459
7688
|
import { note as note2 } from "@clack/prompts";
|
|
7460
7689
|
|
|
7461
7690
|
// src/commands/wizard/vscode.ts
|
|
7462
7691
|
init_paths();
|
|
7463
7692
|
init_detection();
|
|
7464
|
-
import * as
|
|
7465
|
-
import * as
|
|
7693
|
+
import * as fs9 from "fs";
|
|
7694
|
+
import * as path10 from "path";
|
|
7466
7695
|
function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, customGlobalPath) {
|
|
7467
|
-
const workspaceFilePath =
|
|
7696
|
+
const workspaceFilePath = path10.join(workspacePath, `${workspaceName}.code-workspace`);
|
|
7468
7697
|
let workspace;
|
|
7469
|
-
if (
|
|
7698
|
+
if (fs9.existsSync(workspaceFilePath)) {
|
|
7470
7699
|
try {
|
|
7471
|
-
const content =
|
|
7700
|
+
const content = fs9.readFileSync(workspaceFilePath, "utf-8");
|
|
7472
7701
|
workspace = JSON.parse(content);
|
|
7473
7702
|
} catch {
|
|
7474
7703
|
workspace = { folders: [], settings: {} };
|
|
@@ -7501,7 +7730,7 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
|
|
|
7501
7730
|
for (const project of projects) {
|
|
7502
7731
|
const sourceLabel = project.source === "global" ? "global" : "local";
|
|
7503
7732
|
const folderPath = project.dataPath;
|
|
7504
|
-
if (
|
|
7733
|
+
if (fs9.existsSync(folderPath)) {
|
|
7505
7734
|
referenceFolderPaths.push(folderPath);
|
|
7506
7735
|
workspace.folders.push({
|
|
7507
7736
|
path: folderPath,
|
|
@@ -7513,8 +7742,8 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
|
|
|
7513
7742
|
const projectNames = linkedProjects;
|
|
7514
7743
|
const rrceHome = customGlobalPath || getRRCEHome();
|
|
7515
7744
|
for (const projectName of projectNames) {
|
|
7516
|
-
const folderPath =
|
|
7517
|
-
if (
|
|
7745
|
+
const folderPath = path10.join(rrceHome, "workspaces", projectName);
|
|
7746
|
+
if (fs9.existsSync(folderPath)) {
|
|
7518
7747
|
referenceFolderPaths.push(folderPath);
|
|
7519
7748
|
workspace.folders.push({
|
|
7520
7749
|
path: folderPath,
|
|
@@ -7540,23 +7769,23 @@ function generateVSCodeWorkspace(workspacePath, workspaceName, linkedProjects, c
|
|
|
7540
7769
|
...readonlyPatterns
|
|
7541
7770
|
};
|
|
7542
7771
|
}
|
|
7543
|
-
|
|
7772
|
+
fs9.writeFileSync(workspaceFilePath, JSON.stringify(workspace, null, 2));
|
|
7544
7773
|
}
|
|
7545
7774
|
|
|
7546
7775
|
// src/commands/wizard/setup-actions.ts
|
|
7547
7776
|
init_install();
|
|
7548
7777
|
function detectExistingProject(workspacePath, workspaceName, globalPath) {
|
|
7549
7778
|
const rrceHome = globalPath || getDefaultRRCEHome2();
|
|
7550
|
-
const globalConfigPath =
|
|
7551
|
-
const workspaceConfigPath =
|
|
7779
|
+
const globalConfigPath = path13.join(rrceHome, "workspaces", workspaceName, "config.yaml");
|
|
7780
|
+
const workspaceConfigPath = path13.join(workspacePath, ".rrce-workflow", "config.yaml");
|
|
7552
7781
|
const hasOpenCodeAgents = checkForRRCEAgents();
|
|
7553
|
-
if (
|
|
7782
|
+
if (fs11.existsSync(globalConfigPath)) {
|
|
7554
7783
|
return {
|
|
7555
7784
|
isExisting: true,
|
|
7556
7785
|
currentMode: "global",
|
|
7557
7786
|
configPath: globalConfigPath
|
|
7558
7787
|
};
|
|
7559
|
-
} else if (
|
|
7788
|
+
} else if (fs11.existsSync(workspaceConfigPath)) {
|
|
7560
7789
|
return {
|
|
7561
7790
|
isExisting: true,
|
|
7562
7791
|
currentMode: "workspace",
|
|
@@ -7576,9 +7805,9 @@ function detectExistingProject(workspacePath, workspaceName, globalPath) {
|
|
|
7576
7805
|
};
|
|
7577
7806
|
}
|
|
7578
7807
|
function checkForRRCEAgents() {
|
|
7579
|
-
if (!
|
|
7808
|
+
if (!fs11.existsSync(OPENCODE_CONFIG)) return false;
|
|
7580
7809
|
try {
|
|
7581
|
-
const config = JSON.parse(
|
|
7810
|
+
const config = JSON.parse(fs11.readFileSync(OPENCODE_CONFIG, "utf8"));
|
|
7582
7811
|
const agentKeys = Object.keys(config.agent || config.agents || {});
|
|
7583
7812
|
return agentKeys.some((key) => key.startsWith("rrce_"));
|
|
7584
7813
|
} catch {
|
|
@@ -7588,23 +7817,23 @@ function checkForRRCEAgents() {
|
|
|
7588
7817
|
function createDirectoryStructure(dataPaths) {
|
|
7589
7818
|
for (const dataPath of dataPaths) {
|
|
7590
7819
|
ensureDir(dataPath);
|
|
7591
|
-
ensureDir(
|
|
7592
|
-
ensureDir(
|
|
7593
|
-
ensureDir(
|
|
7594
|
-
ensureDir(
|
|
7820
|
+
ensureDir(path13.join(dataPath, "knowledge"));
|
|
7821
|
+
ensureDir(path13.join(dataPath, "refs"));
|
|
7822
|
+
ensureDir(path13.join(dataPath, "tasks"));
|
|
7823
|
+
ensureDir(path13.join(dataPath, "templates"));
|
|
7595
7824
|
}
|
|
7596
7825
|
}
|
|
7597
7826
|
async function installAgentPrompts(config, workspacePath, dataPaths) {
|
|
7598
7827
|
const agentCoreDir = getAgentCoreDir();
|
|
7599
7828
|
syncMetadataToAll(agentCoreDir, dataPaths);
|
|
7600
|
-
copyDirToAllStoragePaths(
|
|
7601
|
-
copyDirToAllStoragePaths(
|
|
7602
|
-
copyDirToAllStoragePaths(
|
|
7829
|
+
copyDirToAllStoragePaths(path13.join(agentCoreDir, "templates"), "templates", dataPaths);
|
|
7830
|
+
copyDirToAllStoragePaths(path13.join(agentCoreDir, "prompts"), "prompts", dataPaths);
|
|
7831
|
+
copyDirToAllStoragePaths(path13.join(agentCoreDir, "docs"), "docs", dataPaths);
|
|
7603
7832
|
const rrceHome = config.globalPath || getDefaultRRCEHome2();
|
|
7604
|
-
ensureDir(
|
|
7605
|
-
ensureDir(
|
|
7606
|
-
copyDirRecursive(
|
|
7607
|
-
copyDirRecursive(
|
|
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"));
|
|
7608
7837
|
const needsIDEPrompts = config.storageMode === "workspace" && (config.tools.includes("copilot") || config.tools.includes("antigravity")) || config.tools.includes("opencode");
|
|
7609
7838
|
if (needsIDEPrompts) {
|
|
7610
7839
|
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
@@ -7633,11 +7862,11 @@ function createWorkspaceConfig(config, workspacePath, workspaceName) {
|
|
|
7633
7862
|
let configPath;
|
|
7634
7863
|
if (config.storageMode === "global") {
|
|
7635
7864
|
const rrceHome = config.globalPath || getDefaultRRCEHome2();
|
|
7636
|
-
configPath =
|
|
7865
|
+
configPath = path13.join(rrceHome, "workspaces", workspaceName, "config.yaml");
|
|
7637
7866
|
} else {
|
|
7638
|
-
configPath =
|
|
7867
|
+
configPath = path13.join(workspacePath, ".rrce-workflow", "config.yaml");
|
|
7639
7868
|
}
|
|
7640
|
-
ensureDir(
|
|
7869
|
+
ensureDir(path13.dirname(configPath));
|
|
7641
7870
|
let content = `# RRCE-Workflow Configuration
|
|
7642
7871
|
version: 1
|
|
7643
7872
|
|
|
@@ -7673,7 +7902,7 @@ linked_projects:
|
|
|
7673
7902
|
`;
|
|
7674
7903
|
});
|
|
7675
7904
|
}
|
|
7676
|
-
|
|
7905
|
+
fs11.writeFileSync(configPath, content);
|
|
7677
7906
|
}
|
|
7678
7907
|
async function registerWithMCP(config, workspacePath, workspaceName) {
|
|
7679
7908
|
if (!config.exposeToMCP) return;
|
|
@@ -7700,8 +7929,8 @@ You can configure MCP later: ${pc4.cyan("npx rrce-workflow mcp")}`,
|
|
|
7700
7929
|
}
|
|
7701
7930
|
}
|
|
7702
7931
|
function getDataPaths(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
7703
|
-
const globalPath =
|
|
7704
|
-
const workspacePath =
|
|
7932
|
+
const globalPath = path13.join(customGlobalPath || getDefaultRRCEHome2(), "workspaces", workspaceName);
|
|
7933
|
+
const workspacePath = path13.join(workspaceRoot, ".rrce-workflow");
|
|
7705
7934
|
switch (mode) {
|
|
7706
7935
|
case "global":
|
|
7707
7936
|
return [globalPath];
|
|
@@ -7988,7 +8217,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
7988
8217
|
init_paths();
|
|
7989
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";
|
|
7990
8219
|
import pc10 from "picocolors";
|
|
7991
|
-
import * as
|
|
8220
|
+
import * as fs30 from "fs";
|
|
7992
8221
|
init_detection();
|
|
7993
8222
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
7994
8223
|
const projects = scanForProjects({
|
|
@@ -8027,7 +8256,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
8027
8256
|
const s = spinner4();
|
|
8028
8257
|
s.start("Linking projects");
|
|
8029
8258
|
const configFilePath = getConfigPath(workspacePath);
|
|
8030
|
-
let configContent =
|
|
8259
|
+
let configContent = fs30.readFileSync(configFilePath, "utf-8");
|
|
8031
8260
|
if (configContent.includes("linked_projects:")) {
|
|
8032
8261
|
const lines = configContent.split("\n");
|
|
8033
8262
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -8054,7 +8283,7 @@ linked_projects:
|
|
|
8054
8283
|
`;
|
|
8055
8284
|
});
|
|
8056
8285
|
}
|
|
8057
|
-
|
|
8286
|
+
fs30.writeFileSync(configFilePath, configContent);
|
|
8058
8287
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
8059
8288
|
s.stop("Projects linked");
|
|
8060
8289
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -8090,15 +8319,15 @@ init_paths();
|
|
|
8090
8319
|
init_utils();
|
|
8091
8320
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
8092
8321
|
import pc11 from "picocolors";
|
|
8093
|
-
import * as
|
|
8094
|
-
import * as
|
|
8322
|
+
import * as fs31 from "fs";
|
|
8323
|
+
import * as path31 from "path";
|
|
8095
8324
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
8096
8325
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
8097
8326
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
8098
|
-
const globalPath =
|
|
8327
|
+
const globalPath = path31.join(customGlobalPath, "workspaces", workspaceName);
|
|
8099
8328
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
8100
8329
|
const existingDirs = subdirs.filter(
|
|
8101
|
-
(dir) =>
|
|
8330
|
+
(dir) => fs31.existsSync(path31.join(localPath, dir))
|
|
8102
8331
|
);
|
|
8103
8332
|
if (existingDirs.length === 0) {
|
|
8104
8333
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -8124,8 +8353,8 @@ Destination: ${pc11.cyan(globalPath)}`,
|
|
|
8124
8353
|
try {
|
|
8125
8354
|
ensureDir(globalPath);
|
|
8126
8355
|
for (const dir of existingDirs) {
|
|
8127
|
-
const srcDir =
|
|
8128
|
-
const destDir =
|
|
8356
|
+
const srcDir = path31.join(localPath, dir);
|
|
8357
|
+
const destDir = path31.join(globalPath, dir);
|
|
8129
8358
|
ensureDir(destDir);
|
|
8130
8359
|
copyDirRecursive(srcDir, destDir);
|
|
8131
8360
|
}
|
|
@@ -8153,7 +8382,7 @@ init_update_flow();
|
|
|
8153
8382
|
// src/commands/wizard/delete-flow.ts
|
|
8154
8383
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
8155
8384
|
import pc12 from "picocolors";
|
|
8156
|
-
import * as
|
|
8385
|
+
import * as fs32 from "fs";
|
|
8157
8386
|
init_detection();
|
|
8158
8387
|
init_config();
|
|
8159
8388
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -8197,8 +8426,8 @@ Are you sure?`,
|
|
|
8197
8426
|
for (const projectName of projectsToDelete) {
|
|
8198
8427
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
8199
8428
|
if (!project) continue;
|
|
8200
|
-
if (
|
|
8201
|
-
|
|
8429
|
+
if (fs32.existsSync(project.dataPath)) {
|
|
8430
|
+
fs32.rmSync(project.dataPath, { recursive: true, force: true });
|
|
8202
8431
|
}
|
|
8203
8432
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
8204
8433
|
configChanged = true;
|
|
@@ -8220,9 +8449,9 @@ init_config();
|
|
|
8220
8449
|
function getPackageVersion3() {
|
|
8221
8450
|
try {
|
|
8222
8451
|
const agentCoreDir = getAgentCoreDir();
|
|
8223
|
-
const packageJsonPath =
|
|
8224
|
-
if (
|
|
8225
|
-
return JSON.parse(
|
|
8452
|
+
const packageJsonPath = path32.join(path32.dirname(agentCoreDir), "package.json");
|
|
8453
|
+
if (fs33.existsSync(packageJsonPath)) {
|
|
8454
|
+
return JSON.parse(fs33.readFileSync(packageJsonPath, "utf8")).version;
|
|
8226
8455
|
}
|
|
8227
8456
|
} catch (e) {
|
|
8228
8457
|
}
|
|
@@ -8230,9 +8459,9 @@ function getPackageVersion3() {
|
|
|
8230
8459
|
}
|
|
8231
8460
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
8232
8461
|
const configFilePath = getConfigPath(workspacePath);
|
|
8233
|
-
if (
|
|
8462
|
+
if (fs33.existsSync(configFilePath)) {
|
|
8234
8463
|
try {
|
|
8235
|
-
const content =
|
|
8464
|
+
const content = fs33.readFileSync(configFilePath, "utf-8");
|
|
8236
8465
|
const config = parse2(content);
|
|
8237
8466
|
if (config.last_synced_version) {
|
|
8238
8467
|
return config.last_synced_version;
|
|
@@ -8241,10 +8470,10 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
8241
8470
|
}
|
|
8242
8471
|
}
|
|
8243
8472
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
8244
|
-
const mcpPath =
|
|
8245
|
-
if (
|
|
8473
|
+
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
8474
|
+
if (fs33.existsSync(mcpPath)) {
|
|
8246
8475
|
try {
|
|
8247
|
-
const content =
|
|
8476
|
+
const content = fs33.readFileSync(mcpPath, "utf-8");
|
|
8248
8477
|
const config = parse2(content);
|
|
8249
8478
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
8250
8479
|
if (project?.last_synced_version) {
|
|
@@ -8314,11 +8543,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8314
8543
|
workspacePath
|
|
8315
8544
|
});
|
|
8316
8545
|
const configFilePath = getConfigPath(workspacePath);
|
|
8317
|
-
let isAlreadyConfigured =
|
|
8546
|
+
let isAlreadyConfigured = fs33.existsSync(configFilePath);
|
|
8318
8547
|
let currentStorageMode = null;
|
|
8319
8548
|
if (isAlreadyConfigured) {
|
|
8320
8549
|
try {
|
|
8321
|
-
const configContent =
|
|
8550
|
+
const configContent = fs33.readFileSync(configFilePath, "utf-8");
|
|
8322
8551
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
8323
8552
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
8324
8553
|
} catch {
|
|
@@ -8335,7 +8564,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8335
8564
|
}
|
|
8336
8565
|
}
|
|
8337
8566
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
8338
|
-
const hasLocalData =
|
|
8567
|
+
const hasLocalData = fs33.existsSync(localDataPath);
|
|
8339
8568
|
if (isAlreadyConfigured) {
|
|
8340
8569
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
8341
8570
|
if (!continueToMenu) {
|