rrce-workflow 0.3.14 → 0.3.16
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 +24 -3
- package/agent-core/prompts/executor.md +30 -13
- package/agent-core/prompts/orchestrator.md +28 -11
- package/agent-core/prompts/planning_discussion.md +26 -10
- package/agent-core/prompts/research_discussion.md +26 -8
- package/dist/index.js +1564 -294
- package/package.json +1 -1
- package/docs/AI_AGENT_GUIDE.md +0 -65
package/dist/index.js
CHANGED
|
@@ -2524,6 +2524,234 @@ var init_dependency_graph = __esm({
|
|
|
2524
2524
|
}
|
|
2525
2525
|
});
|
|
2526
2526
|
|
|
2527
|
+
// src/mcp/services/symbol-extractor.ts
|
|
2528
|
+
function extractSymbols(content, filePath) {
|
|
2529
|
+
const lines = content.split("\n");
|
|
2530
|
+
const symbols = [];
|
|
2531
|
+
const exports = [];
|
|
2532
|
+
const imports = [];
|
|
2533
|
+
const language = getLanguageFromPath(filePath);
|
|
2534
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2535
|
+
const line = lines[i] ?? "";
|
|
2536
|
+
const lineNum = i + 1;
|
|
2537
|
+
const trimmed = line.trim();
|
|
2538
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*") || !trimmed) {
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
const importMatch = line.match(/^import\s+(?:\{([^}]+)\}|(\w+))\s+from\s+['"]([^'"]+)['"]/);
|
|
2542
|
+
if (importMatch) {
|
|
2543
|
+
imports.push(importMatch[3] ?? "");
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
const reExportMatch = line.match(/^export\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
|
|
2547
|
+
if (reExportMatch) {
|
|
2548
|
+
const items = (reExportMatch[1] ?? "").split(",").map((s) => s.trim().split(" as ")[0]?.trim() ?? "");
|
|
2549
|
+
exports.push(...items.filter(Boolean));
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
2552
|
+
const isExported = trimmed.startsWith("export ");
|
|
2553
|
+
const cleanLine = isExported ? trimmed.replace(/^export\s+(default\s+)?/, "") : trimmed;
|
|
2554
|
+
const funcMatch = cleanLine.match(/^(?:async\s+)?function\s+(\w+)\s*(\([^)]*\))/);
|
|
2555
|
+
if (funcMatch && funcMatch[1]) {
|
|
2556
|
+
const name = funcMatch[1];
|
|
2557
|
+
const params = funcMatch[2] ?? "()";
|
|
2558
|
+
symbols.push({
|
|
2559
|
+
name,
|
|
2560
|
+
type: "function",
|
|
2561
|
+
line: lineNum,
|
|
2562
|
+
signature: `function ${name}${params}`,
|
|
2563
|
+
exported: isExported
|
|
2564
|
+
});
|
|
2565
|
+
if (isExported) exports.push(name);
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
const arrowMatch = cleanLine.match(/^(const|let|var)\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/);
|
|
2569
|
+
if (arrowMatch && arrowMatch[2]) {
|
|
2570
|
+
const name = arrowMatch[2];
|
|
2571
|
+
symbols.push({
|
|
2572
|
+
name,
|
|
2573
|
+
type: "function",
|
|
2574
|
+
line: lineNum,
|
|
2575
|
+
signature: `const ${name} = (...)`,
|
|
2576
|
+
exported: isExported
|
|
2577
|
+
});
|
|
2578
|
+
if (isExported) exports.push(name);
|
|
2579
|
+
continue;
|
|
2580
|
+
}
|
|
2581
|
+
const classMatch = cleanLine.match(/^(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?/);
|
|
2582
|
+
if (classMatch && classMatch[1]) {
|
|
2583
|
+
const name = classMatch[1];
|
|
2584
|
+
const extendsClass = classMatch[2];
|
|
2585
|
+
let signature = `class ${name}`;
|
|
2586
|
+
if (extendsClass) signature += ` extends ${extendsClass}`;
|
|
2587
|
+
symbols.push({
|
|
2588
|
+
name,
|
|
2589
|
+
type: "class",
|
|
2590
|
+
line: lineNum,
|
|
2591
|
+
signature,
|
|
2592
|
+
exported: isExported
|
|
2593
|
+
});
|
|
2594
|
+
if (isExported) exports.push(name);
|
|
2595
|
+
continue;
|
|
2596
|
+
}
|
|
2597
|
+
const interfaceMatch = cleanLine.match(/^interface\s+(\w+)(?:<[^>]+>)?(?:\s+extends\s+([^{]+))?/);
|
|
2598
|
+
if (interfaceMatch && interfaceMatch[1]) {
|
|
2599
|
+
const name = interfaceMatch[1];
|
|
2600
|
+
symbols.push({
|
|
2601
|
+
name,
|
|
2602
|
+
type: "interface",
|
|
2603
|
+
line: lineNum,
|
|
2604
|
+
signature: `interface ${name}`,
|
|
2605
|
+
exported: isExported
|
|
2606
|
+
});
|
|
2607
|
+
if (isExported) exports.push(name);
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
const typeMatch = cleanLine.match(/^type\s+(\w+)(?:<[^>]+>)?\s*=/);
|
|
2611
|
+
if (typeMatch && typeMatch[1]) {
|
|
2612
|
+
const name = typeMatch[1];
|
|
2613
|
+
symbols.push({
|
|
2614
|
+
name,
|
|
2615
|
+
type: "type",
|
|
2616
|
+
line: lineNum,
|
|
2617
|
+
signature: `type ${name}`,
|
|
2618
|
+
exported: isExported
|
|
2619
|
+
});
|
|
2620
|
+
if (isExported) exports.push(name);
|
|
2621
|
+
continue;
|
|
2622
|
+
}
|
|
2623
|
+
const enumMatch = cleanLine.match(/^(?:const\s+)?enum\s+(\w+)/);
|
|
2624
|
+
if (enumMatch && enumMatch[1]) {
|
|
2625
|
+
const name = enumMatch[1];
|
|
2626
|
+
symbols.push({
|
|
2627
|
+
name,
|
|
2628
|
+
type: "enum",
|
|
2629
|
+
line: lineNum,
|
|
2630
|
+
signature: `enum ${name}`,
|
|
2631
|
+
exported: isExported
|
|
2632
|
+
});
|
|
2633
|
+
if (isExported) exports.push(name);
|
|
2634
|
+
continue;
|
|
2635
|
+
}
|
|
2636
|
+
const varMatch = cleanLine.match(/^(const|let|var)\s+(\w+)\s*(?::\s*([^=]+))?\s*=/);
|
|
2637
|
+
if (varMatch && varMatch[2]) {
|
|
2638
|
+
if (line.includes("=>")) continue;
|
|
2639
|
+
const name = varMatch[2];
|
|
2640
|
+
const varType = varMatch[1];
|
|
2641
|
+
symbols.push({
|
|
2642
|
+
name,
|
|
2643
|
+
type: varType === "const" ? "const" : "variable",
|
|
2644
|
+
line: lineNum,
|
|
2645
|
+
signature: `${varMatch[1]} ${name}`,
|
|
2646
|
+
exported: isExported
|
|
2647
|
+
});
|
|
2648
|
+
if (isExported) exports.push(name);
|
|
2649
|
+
continue;
|
|
2650
|
+
}
|
|
2651
|
+
const defaultExportMatch = trimmed.match(/^export\s+default\s+(?:class|function)?\s*(\w+)?/);
|
|
2652
|
+
if (defaultExportMatch && defaultExportMatch[1]) {
|
|
2653
|
+
exports.push(defaultExportMatch[1]);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
return {
|
|
2657
|
+
filePath,
|
|
2658
|
+
language,
|
|
2659
|
+
symbols,
|
|
2660
|
+
exports: Array.from(new Set(exports)),
|
|
2661
|
+
imports: Array.from(new Set(imports))
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
function fuzzyMatchScore(query, symbolName) {
|
|
2665
|
+
const q = query.toLowerCase();
|
|
2666
|
+
const s = symbolName.toLowerCase();
|
|
2667
|
+
if (q === s) return 1;
|
|
2668
|
+
if (s.startsWith(q)) return 0.9;
|
|
2669
|
+
if (s.includes(q)) return 0.7;
|
|
2670
|
+
if (q.startsWith(s)) return 0.6;
|
|
2671
|
+
const distance = levenshteinDistance(q, s);
|
|
2672
|
+
const maxLen = Math.max(q.length, s.length);
|
|
2673
|
+
const similarity = 1 - distance / maxLen;
|
|
2674
|
+
return Math.max(0, similarity * 0.5);
|
|
2675
|
+
}
|
|
2676
|
+
function levenshteinDistance(a, b) {
|
|
2677
|
+
const matrix = [];
|
|
2678
|
+
for (let i = 0; i <= b.length; i++) {
|
|
2679
|
+
matrix[i] = [i];
|
|
2680
|
+
}
|
|
2681
|
+
for (let j = 0; j <= a.length; j++) {
|
|
2682
|
+
matrix[0][j] = j;
|
|
2683
|
+
}
|
|
2684
|
+
for (let i = 1; i <= b.length; i++) {
|
|
2685
|
+
for (let j = 1; j <= a.length; j++) {
|
|
2686
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
2687
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
2688
|
+
} else {
|
|
2689
|
+
matrix[i][j] = Math.min(
|
|
2690
|
+
matrix[i - 1][j - 1] + 1,
|
|
2691
|
+
// substitution
|
|
2692
|
+
matrix[i][j - 1] + 1,
|
|
2693
|
+
// insertion
|
|
2694
|
+
matrix[i - 1][j] + 1
|
|
2695
|
+
// deletion
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return matrix[b.length][a.length];
|
|
2701
|
+
}
|
|
2702
|
+
function getLanguageFromPath(filePath) {
|
|
2703
|
+
const ext = filePath.toLowerCase().split(".").pop() ?? "";
|
|
2704
|
+
const langMap = {
|
|
2705
|
+
"ts": "typescript",
|
|
2706
|
+
"tsx": "typescript",
|
|
2707
|
+
"js": "javascript",
|
|
2708
|
+
"jsx": "javascript",
|
|
2709
|
+
"mjs": "javascript",
|
|
2710
|
+
"cjs": "javascript",
|
|
2711
|
+
"py": "python",
|
|
2712
|
+
"go": "go",
|
|
2713
|
+
"rs": "rust",
|
|
2714
|
+
"java": "java",
|
|
2715
|
+
"kt": "kotlin",
|
|
2716
|
+
"rb": "ruby",
|
|
2717
|
+
"php": "php",
|
|
2718
|
+
"swift": "swift",
|
|
2719
|
+
"c": "c",
|
|
2720
|
+
"cpp": "cpp",
|
|
2721
|
+
"h": "c",
|
|
2722
|
+
"hpp": "cpp",
|
|
2723
|
+
"cs": "csharp"
|
|
2724
|
+
};
|
|
2725
|
+
return langMap[ext] ?? "unknown";
|
|
2726
|
+
}
|
|
2727
|
+
function searchSymbols(symbolResults, query, options = {}) {
|
|
2728
|
+
const { type = "any", fuzzy = true, limit = 10, minScore = 0.3 } = options;
|
|
2729
|
+
const matches = [];
|
|
2730
|
+
for (const result of symbolResults) {
|
|
2731
|
+
for (const symbol of result.symbols) {
|
|
2732
|
+
if (type !== "any" && symbol.type !== type) continue;
|
|
2733
|
+
const score = fuzzy ? fuzzyMatchScore(query, symbol.name) : symbol.name.toLowerCase().includes(query.toLowerCase()) ? 1 : 0;
|
|
2734
|
+
if (score >= minScore) {
|
|
2735
|
+
matches.push({
|
|
2736
|
+
...symbol,
|
|
2737
|
+
file: result.filePath,
|
|
2738
|
+
score
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
matches.sort((a, b) => {
|
|
2744
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
2745
|
+
return a.name.length - b.name.length;
|
|
2746
|
+
});
|
|
2747
|
+
return matches.slice(0, limit);
|
|
2748
|
+
}
|
|
2749
|
+
var init_symbol_extractor = __esm({
|
|
2750
|
+
"src/mcp/services/symbol-extractor.ts"() {
|
|
2751
|
+
"use strict";
|
|
2752
|
+
}
|
|
2753
|
+
});
|
|
2754
|
+
|
|
2527
2755
|
// src/mcp/resources.ts
|
|
2528
2756
|
import * as fs15 from "fs";
|
|
2529
2757
|
import * as path17 from "path";
|
|
@@ -2689,7 +2917,10 @@ function getProjectTasks(projectName) {
|
|
|
2689
2917
|
}
|
|
2690
2918
|
return tasks;
|
|
2691
2919
|
}
|
|
2692
|
-
|
|
2920
|
+
function estimateTokens(text2) {
|
|
2921
|
+
return Math.ceil(text2.length / 4);
|
|
2922
|
+
}
|
|
2923
|
+
async function searchCode(query, projectFilter, limit = 10, options) {
|
|
2693
2924
|
const config = loadMCPConfig();
|
|
2694
2925
|
const projects = getExposedProjects();
|
|
2695
2926
|
const results = [];
|
|
@@ -2697,8 +2928,8 @@ async function searchCode(query, projectFilter, limit = 10) {
|
|
|
2697
2928
|
if (projectFilter && project.name !== projectFilter) continue;
|
|
2698
2929
|
const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
|
|
2699
2930
|
if (!permissions.knowledge || !project.knowledgePath) continue;
|
|
2700
|
-
const
|
|
2701
|
-
const
|
|
2931
|
+
const indexingInProgress2 = indexingJobs.isRunning(project.name);
|
|
2932
|
+
const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
2702
2933
|
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
|
|
2703
2934
|
const useRAG = projConfig?.semanticSearch?.enabled;
|
|
2704
2935
|
if (!useRAG) {
|
|
@@ -2724,8 +2955,8 @@ async function searchCode(query, projectFilter, limit = 10) {
|
|
|
2724
2955
|
context: codeChunk.context,
|
|
2725
2956
|
language: codeChunk.language,
|
|
2726
2957
|
score: codeChunk.score,
|
|
2727
|
-
indexingInProgress:
|
|
2728
|
-
advisoryMessage
|
|
2958
|
+
indexingInProgress: indexingInProgress2 || void 0,
|
|
2959
|
+
advisoryMessage: advisoryMessage2
|
|
2729
2960
|
});
|
|
2730
2961
|
}
|
|
2731
2962
|
} catch (e) {
|
|
@@ -2733,9 +2964,56 @@ async function searchCode(query, projectFilter, limit = 10) {
|
|
|
2733
2964
|
}
|
|
2734
2965
|
}
|
|
2735
2966
|
results.sort((a, b) => b.score - a.score);
|
|
2736
|
-
|
|
2967
|
+
let filteredResults = results;
|
|
2968
|
+
if (options?.min_score !== void 0 && options.min_score > 0) {
|
|
2969
|
+
filteredResults = results.filter((r) => r.score >= options.min_score);
|
|
2970
|
+
}
|
|
2971
|
+
let limitedResults = filteredResults.slice(0, limit);
|
|
2972
|
+
let truncated = false;
|
|
2973
|
+
let tokenCount = 0;
|
|
2974
|
+
if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
|
|
2975
|
+
const budgetedResults = [];
|
|
2976
|
+
for (const result of limitedResults) {
|
|
2977
|
+
const resultTokens = estimateTokens(result.snippet + (result.context || ""));
|
|
2978
|
+
if (tokenCount + resultTokens > options.max_tokens) {
|
|
2979
|
+
truncated = true;
|
|
2980
|
+
break;
|
|
2981
|
+
}
|
|
2982
|
+
budgetedResults.push(result);
|
|
2983
|
+
tokenCount += resultTokens;
|
|
2984
|
+
}
|
|
2985
|
+
limitedResults = budgetedResults;
|
|
2986
|
+
} else {
|
|
2987
|
+
tokenCount = limitedResults.reduce((sum, r) => sum + estimateTokens(r.snippet + (r.context || "")), 0);
|
|
2988
|
+
}
|
|
2989
|
+
let indexAgeSeconds;
|
|
2990
|
+
let lastIndexedAt;
|
|
2991
|
+
let indexingInProgress;
|
|
2992
|
+
let advisoryMessage;
|
|
2993
|
+
if (projectFilter) {
|
|
2994
|
+
const project = projects.find((p) => p.name === projectFilter);
|
|
2995
|
+
if (project) {
|
|
2996
|
+
indexingInProgress = indexingJobs.isRunning(project.name);
|
|
2997
|
+
advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
2998
|
+
const progress = indexingJobs.getProgress(project.name);
|
|
2999
|
+
if (progress.completedAt) {
|
|
3000
|
+
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3001
|
+
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
const cleanResults = limitedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3006
|
+
return {
|
|
3007
|
+
results: cleanResults,
|
|
3008
|
+
token_count: tokenCount,
|
|
3009
|
+
truncated,
|
|
3010
|
+
index_age_seconds: indexAgeSeconds,
|
|
3011
|
+
last_indexed_at: lastIndexedAt,
|
|
3012
|
+
indexingInProgress,
|
|
3013
|
+
advisoryMessage
|
|
3014
|
+
};
|
|
2737
3015
|
}
|
|
2738
|
-
async function searchKnowledge(query, projectFilter) {
|
|
3016
|
+
async function searchKnowledge(query, projectFilter, options) {
|
|
2739
3017
|
const config = loadMCPConfig();
|
|
2740
3018
|
const projects = getExposedProjects();
|
|
2741
3019
|
const results = [];
|
|
@@ -2744,8 +3022,8 @@ async function searchKnowledge(query, projectFilter) {
|
|
|
2744
3022
|
if (projectFilter && project.name !== projectFilter) continue;
|
|
2745
3023
|
const permissions = getProjectPermissions(config, project.name, project.sourcePath || project.path);
|
|
2746
3024
|
if (!permissions.knowledge || !project.knowledgePath) continue;
|
|
2747
|
-
const
|
|
2748
|
-
const
|
|
3025
|
+
const indexingInProgress2 = indexingJobs.isRunning(project.name);
|
|
3026
|
+
const advisoryMessage2 = indexingInProgress2 ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
2749
3027
|
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path });
|
|
2750
3028
|
const useRAG = projConfig?.semanticSearch?.enabled;
|
|
2751
3029
|
if (useRAG) {
|
|
@@ -2761,8 +3039,8 @@ async function searchKnowledge(query, projectFilter) {
|
|
|
2761
3039
|
matches: [r.content],
|
|
2762
3040
|
// The chunk content is the match
|
|
2763
3041
|
score: r.score,
|
|
2764
|
-
indexingInProgress:
|
|
2765
|
-
advisoryMessage
|
|
3042
|
+
indexingInProgress: indexingInProgress2 || void 0,
|
|
3043
|
+
advisoryMessage: advisoryMessage2
|
|
2766
3044
|
});
|
|
2767
3045
|
}
|
|
2768
3046
|
continue;
|
|
@@ -2789,8 +3067,8 @@ async function searchKnowledge(query, projectFilter) {
|
|
|
2789
3067
|
file,
|
|
2790
3068
|
matches: matches.slice(0, 5),
|
|
2791
3069
|
// Limit to 5 matches per file
|
|
2792
|
-
indexingInProgress:
|
|
2793
|
-
advisoryMessage
|
|
3070
|
+
indexingInProgress: indexingInProgress2 || void 0,
|
|
3071
|
+
advisoryMessage: advisoryMessage2
|
|
2794
3072
|
});
|
|
2795
3073
|
}
|
|
2796
3074
|
}
|
|
@@ -2798,7 +3076,54 @@ async function searchKnowledge(query, projectFilter) {
|
|
|
2798
3076
|
logger.error(`[searchKnowledge] Failed to read knowledge directory ${project.knowledgePath}`, err);
|
|
2799
3077
|
}
|
|
2800
3078
|
}
|
|
2801
|
-
|
|
3079
|
+
let filteredResults = results;
|
|
3080
|
+
if (options?.min_score !== void 0 && options.min_score > 0) {
|
|
3081
|
+
filteredResults = results.filter((r) => (r.score ?? 1) >= options.min_score);
|
|
3082
|
+
}
|
|
3083
|
+
filteredResults.sort((a, b) => (b.score ?? 1) - (a.score ?? 1));
|
|
3084
|
+
let truncated = false;
|
|
3085
|
+
let tokenCount = 0;
|
|
3086
|
+
let budgetedResults = filteredResults;
|
|
3087
|
+
if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
|
|
3088
|
+
budgetedResults = [];
|
|
3089
|
+
for (const result of filteredResults) {
|
|
3090
|
+
const resultTokens = estimateTokens(result.matches.join("\n"));
|
|
3091
|
+
if (tokenCount + resultTokens > options.max_tokens) {
|
|
3092
|
+
truncated = true;
|
|
3093
|
+
break;
|
|
3094
|
+
}
|
|
3095
|
+
budgetedResults.push(result);
|
|
3096
|
+
tokenCount += resultTokens;
|
|
3097
|
+
}
|
|
3098
|
+
} else {
|
|
3099
|
+
tokenCount = filteredResults.reduce((sum, r) => sum + estimateTokens(r.matches.join("\n")), 0);
|
|
3100
|
+
}
|
|
3101
|
+
let indexAgeSeconds;
|
|
3102
|
+
let lastIndexedAt;
|
|
3103
|
+
let indexingInProgress;
|
|
3104
|
+
let advisoryMessage;
|
|
3105
|
+
if (projectFilter) {
|
|
3106
|
+
const project = projects.find((p) => p.name === projectFilter);
|
|
3107
|
+
if (project) {
|
|
3108
|
+
indexingInProgress = indexingJobs.isRunning(project.name);
|
|
3109
|
+
advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
3110
|
+
const progress = indexingJobs.getProgress(project.name);
|
|
3111
|
+
if (progress.completedAt) {
|
|
3112
|
+
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3113
|
+
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
const cleanResults = budgetedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3118
|
+
return {
|
|
3119
|
+
results: cleanResults,
|
|
3120
|
+
token_count: tokenCount,
|
|
3121
|
+
truncated,
|
|
3122
|
+
index_age_seconds: indexAgeSeconds,
|
|
3123
|
+
last_indexed_at: lastIndexedAt,
|
|
3124
|
+
indexingInProgress,
|
|
3125
|
+
advisoryMessage
|
|
3126
|
+
};
|
|
2802
3127
|
}
|
|
2803
3128
|
function getScanContext(project, scanRoot) {
|
|
2804
3129
|
const gitignorePath = path17.join(scanRoot, ".gitignore");
|
|
@@ -3160,6 +3485,453 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3160
3485
|
};
|
|
3161
3486
|
}
|
|
3162
3487
|
}
|
|
3488
|
+
async function searchSymbols2(name, projectName, options = {}) {
|
|
3489
|
+
const config = loadMCPConfig();
|
|
3490
|
+
const projects = getExposedProjects();
|
|
3491
|
+
const project = projects.find((p) => p.name === projectName);
|
|
3492
|
+
if (!project) {
|
|
3493
|
+
return {
|
|
3494
|
+
success: false,
|
|
3495
|
+
project: projectName,
|
|
3496
|
+
results: [],
|
|
3497
|
+
message: `Project '${projectName}' not found`
|
|
3498
|
+
};
|
|
3499
|
+
}
|
|
3500
|
+
const projectRoot = project.sourcePath || project.path || "";
|
|
3501
|
+
if (!fs15.existsSync(projectRoot)) {
|
|
3502
|
+
return {
|
|
3503
|
+
success: false,
|
|
3504
|
+
project: projectName,
|
|
3505
|
+
results: [],
|
|
3506
|
+
message: `Project root not found: ${projectRoot}`
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3509
|
+
try {
|
|
3510
|
+
const codeFiles = [];
|
|
3511
|
+
const scanDir = (dir) => {
|
|
3512
|
+
const entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
3513
|
+
for (const entry of entries) {
|
|
3514
|
+
const fullPath = path17.join(dir, entry.name);
|
|
3515
|
+
if (entry.isDirectory()) {
|
|
3516
|
+
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
3517
|
+
scanDir(fullPath);
|
|
3518
|
+
} else if (entry.isFile()) {
|
|
3519
|
+
const ext = path17.extname(entry.name).toLowerCase();
|
|
3520
|
+
if (CODE_EXTENSIONS.includes(ext)) {
|
|
3521
|
+
codeFiles.push(fullPath);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
};
|
|
3526
|
+
scanDir(projectRoot);
|
|
3527
|
+
const symbolResults = [];
|
|
3528
|
+
for (const file of codeFiles.slice(0, 500)) {
|
|
3529
|
+
try {
|
|
3530
|
+
const content = fs15.readFileSync(file, "utf-8");
|
|
3531
|
+
const result = extractSymbols(content, file);
|
|
3532
|
+
symbolResults.push(result);
|
|
3533
|
+
} catch (e) {
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
const matches = searchSymbols(symbolResults, name, {
|
|
3537
|
+
type: options.type,
|
|
3538
|
+
fuzzy: options.fuzzy ?? true,
|
|
3539
|
+
limit: options.limit ?? 10,
|
|
3540
|
+
minScore: 0.3
|
|
3541
|
+
});
|
|
3542
|
+
const results = matches.map((m) => ({
|
|
3543
|
+
name: m.name,
|
|
3544
|
+
type: m.type,
|
|
3545
|
+
file: path17.relative(projectRoot, m.file),
|
|
3546
|
+
line: m.line,
|
|
3547
|
+
signature: m.signature,
|
|
3548
|
+
exported: m.exported,
|
|
3549
|
+
score: m.score
|
|
3550
|
+
}));
|
|
3551
|
+
return {
|
|
3552
|
+
success: true,
|
|
3553
|
+
project: projectName,
|
|
3554
|
+
results
|
|
3555
|
+
};
|
|
3556
|
+
} catch (e) {
|
|
3557
|
+
logger.error(`[searchSymbols] Error searching symbols in ${projectName}`, e);
|
|
3558
|
+
return {
|
|
3559
|
+
success: false,
|
|
3560
|
+
project: projectName,
|
|
3561
|
+
results: [],
|
|
3562
|
+
message: `Error searching symbols: ${e instanceof Error ? e.message : String(e)}`
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
async function getFileSummary(filePath, projectName) {
|
|
3567
|
+
const config = loadMCPConfig();
|
|
3568
|
+
const projects = getExposedProjects();
|
|
3569
|
+
const project = projects.find((p) => p.name === projectName);
|
|
3570
|
+
if (!project) {
|
|
3571
|
+
return {
|
|
3572
|
+
success: false,
|
|
3573
|
+
message: `Project '${projectName}' not found`
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3576
|
+
const projectRoot = project.sourcePath || project.path || "";
|
|
3577
|
+
let absolutePath = filePath;
|
|
3578
|
+
if (!path17.isAbsolute(filePath)) {
|
|
3579
|
+
absolutePath = path17.resolve(projectRoot, filePath);
|
|
3580
|
+
}
|
|
3581
|
+
if (!fs15.existsSync(absolutePath)) {
|
|
3582
|
+
return {
|
|
3583
|
+
success: false,
|
|
3584
|
+
message: `File not found: ${filePath}`
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
try {
|
|
3588
|
+
const stat = fs15.statSync(absolutePath);
|
|
3589
|
+
const content = fs15.readFileSync(absolutePath, "utf-8");
|
|
3590
|
+
const lines = content.split("\n");
|
|
3591
|
+
const symbolResult = extractSymbols(content, absolutePath);
|
|
3592
|
+
return {
|
|
3593
|
+
success: true,
|
|
3594
|
+
summary: {
|
|
3595
|
+
path: path17.relative(projectRoot, absolutePath),
|
|
3596
|
+
language: symbolResult.language,
|
|
3597
|
+
lines: lines.length,
|
|
3598
|
+
size_bytes: stat.size,
|
|
3599
|
+
last_modified: stat.mtime.toISOString(),
|
|
3600
|
+
exports: symbolResult.exports,
|
|
3601
|
+
imports: symbolResult.imports,
|
|
3602
|
+
symbols: symbolResult.symbols.map((s) => ({
|
|
3603
|
+
name: s.name,
|
|
3604
|
+
type: s.type,
|
|
3605
|
+
line: s.line
|
|
3606
|
+
}))
|
|
3607
|
+
}
|
|
3608
|
+
};
|
|
3609
|
+
} catch (e) {
|
|
3610
|
+
logger.error(`[getFileSummary] Error reading ${filePath}`, e);
|
|
3611
|
+
return {
|
|
3612
|
+
success: false,
|
|
3613
|
+
message: `Error reading file: ${e instanceof Error ? e.message : String(e)}`
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
async function getContextBundle(query, projectName, options = {}) {
|
|
3618
|
+
const maxTokens = options.max_tokens ?? 4e3;
|
|
3619
|
+
const include = {
|
|
3620
|
+
project_context: options.include?.project_context ?? true,
|
|
3621
|
+
knowledge: options.include?.knowledge ?? true,
|
|
3622
|
+
code: options.include?.code ?? true,
|
|
3623
|
+
related_files: options.include?.related_files ?? false
|
|
3624
|
+
};
|
|
3625
|
+
const contextBudget = Math.floor(maxTokens * 0.4);
|
|
3626
|
+
const knowledgeBudget = Math.floor(maxTokens * 0.3);
|
|
3627
|
+
const codeBudget = Math.floor(maxTokens * 0.3);
|
|
3628
|
+
let totalTokens = 0;
|
|
3629
|
+
let truncated = false;
|
|
3630
|
+
let indexAgeSeconds;
|
|
3631
|
+
let projectContext = null;
|
|
3632
|
+
if (include.project_context) {
|
|
3633
|
+
const rawContext = getProjectContext(projectName);
|
|
3634
|
+
if (rawContext) {
|
|
3635
|
+
const contextTokens = estimateTokens(rawContext);
|
|
3636
|
+
if (contextTokens <= contextBudget) {
|
|
3637
|
+
projectContext = rawContext;
|
|
3638
|
+
totalTokens += contextTokens;
|
|
3639
|
+
} else {
|
|
3640
|
+
const maxChars = contextBudget * 4;
|
|
3641
|
+
projectContext = rawContext.slice(0, maxChars) + "\n\n[truncated]";
|
|
3642
|
+
totalTokens += contextBudget;
|
|
3643
|
+
truncated = true;
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
const knowledgeResults = [];
|
|
3648
|
+
if (include.knowledge) {
|
|
3649
|
+
const knowledgeSearch = await searchKnowledge(query, projectName, { max_tokens: knowledgeBudget });
|
|
3650
|
+
for (const r of knowledgeSearch.results) {
|
|
3651
|
+
knowledgeResults.push({
|
|
3652
|
+
file: r.file,
|
|
3653
|
+
matches: r.matches,
|
|
3654
|
+
score: r.score
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
totalTokens += knowledgeSearch.token_count;
|
|
3658
|
+
if (knowledgeSearch.truncated) truncated = true;
|
|
3659
|
+
if (knowledgeSearch.index_age_seconds !== void 0) {
|
|
3660
|
+
indexAgeSeconds = knowledgeSearch.index_age_seconds;
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
const codeResults = [];
|
|
3664
|
+
if (include.code) {
|
|
3665
|
+
const codeSearch = await searchCode(query, projectName, 10, { max_tokens: codeBudget });
|
|
3666
|
+
for (const r of codeSearch.results) {
|
|
3667
|
+
codeResults.push({
|
|
3668
|
+
file: r.file,
|
|
3669
|
+
snippet: r.snippet,
|
|
3670
|
+
lineStart: r.lineStart,
|
|
3671
|
+
lineEnd: r.lineEnd,
|
|
3672
|
+
context: r.context,
|
|
3673
|
+
score: r.score
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
3676
|
+
totalTokens += codeSearch.token_count;
|
|
3677
|
+
if (codeSearch.truncated) truncated = true;
|
|
3678
|
+
if (codeSearch.index_age_seconds !== void 0 && indexAgeSeconds === void 0) {
|
|
3679
|
+
indexAgeSeconds = codeSearch.index_age_seconds;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
const relatedFiles = [];
|
|
3683
|
+
if (include.related_files && codeResults.length > 0) {
|
|
3684
|
+
const topFile = codeResults[0]?.file;
|
|
3685
|
+
if (topFile) {
|
|
3686
|
+
const related = await findRelatedFiles2(topFile, projectName, { depth: 1 });
|
|
3687
|
+
if (related.success) {
|
|
3688
|
+
for (const r of related.relationships.slice(0, 5)) {
|
|
3689
|
+
relatedFiles.push(r.file);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
return {
|
|
3695
|
+
success: true,
|
|
3696
|
+
project_context: projectContext,
|
|
3697
|
+
knowledge_results: knowledgeResults,
|
|
3698
|
+
code_results: codeResults,
|
|
3699
|
+
related_files: relatedFiles,
|
|
3700
|
+
token_count: totalTokens,
|
|
3701
|
+
truncated,
|
|
3702
|
+
index_age_seconds: indexAgeSeconds
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
async function prefetchTaskContext(projectName, taskSlug, options = {}) {
|
|
3706
|
+
const maxTokens = options.max_tokens ?? 6e3;
|
|
3707
|
+
const task = getTask(projectName, taskSlug);
|
|
3708
|
+
if (!task) {
|
|
3709
|
+
return {
|
|
3710
|
+
success: false,
|
|
3711
|
+
task: null,
|
|
3712
|
+
project_context: null,
|
|
3713
|
+
referenced_files: [],
|
|
3714
|
+
knowledge_matches: [],
|
|
3715
|
+
code_matches: [],
|
|
3716
|
+
token_count: 0,
|
|
3717
|
+
truncated: false,
|
|
3718
|
+
message: `Task '${taskSlug}' not found in project '${projectName}'`
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
const searchQuery = `${task.title || ""} ${task.summary || ""}`.trim();
|
|
3722
|
+
const bundle = await getContextBundle(searchQuery, projectName, {
|
|
3723
|
+
max_tokens: Math.floor(maxTokens * 0.7),
|
|
3724
|
+
// Reserve 30% for referenced files
|
|
3725
|
+
include: {
|
|
3726
|
+
project_context: true,
|
|
3727
|
+
knowledge: true,
|
|
3728
|
+
code: true,
|
|
3729
|
+
related_files: false
|
|
3730
|
+
}
|
|
3731
|
+
});
|
|
3732
|
+
const referencedFiles = [];
|
|
3733
|
+
const references = task.references;
|
|
3734
|
+
if (references && Array.isArray(references)) {
|
|
3735
|
+
for (const ref of references.slice(0, 5)) {
|
|
3736
|
+
const summary = await getFileSummary(ref, projectName);
|
|
3737
|
+
if (summary.success && summary.summary) {
|
|
3738
|
+
referencedFiles.push({
|
|
3739
|
+
path: summary.summary.path,
|
|
3740
|
+
language: summary.summary.language,
|
|
3741
|
+
lines: summary.summary.lines,
|
|
3742
|
+
exports: summary.summary.exports
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
const taskTokens = estimateTokens(JSON.stringify(task));
|
|
3748
|
+
const refTokens = estimateTokens(JSON.stringify(referencedFiles));
|
|
3749
|
+
return {
|
|
3750
|
+
success: true,
|
|
3751
|
+
task,
|
|
3752
|
+
project_context: bundle.project_context,
|
|
3753
|
+
referenced_files: referencedFiles,
|
|
3754
|
+
knowledge_matches: bundle.knowledge_results,
|
|
3755
|
+
code_matches: bundle.code_results,
|
|
3756
|
+
token_count: bundle.token_count + taskTokens + refTokens,
|
|
3757
|
+
truncated: bundle.truncated
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
function searchTasks(projectName, options = {}) {
|
|
3761
|
+
const allTasks = getProjectTasks(projectName);
|
|
3762
|
+
const limit = options.limit ?? 20;
|
|
3763
|
+
let filtered = allTasks;
|
|
3764
|
+
if (options.status) {
|
|
3765
|
+
filtered = filtered.filter((t) => t.status === options.status);
|
|
3766
|
+
}
|
|
3767
|
+
if (options.agent) {
|
|
3768
|
+
filtered = filtered.filter((t) => {
|
|
3769
|
+
const agents = t.agents;
|
|
3770
|
+
if (!agents) return false;
|
|
3771
|
+
return agents[options.agent]?.status !== void 0;
|
|
3772
|
+
});
|
|
3773
|
+
}
|
|
3774
|
+
if (options.since) {
|
|
3775
|
+
const sinceDate = new Date(options.since).getTime();
|
|
3776
|
+
filtered = filtered.filter((t) => {
|
|
3777
|
+
const updatedAt = t.updated_at;
|
|
3778
|
+
if (!updatedAt) return false;
|
|
3779
|
+
return new Date(updatedAt).getTime() >= sinceDate;
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
if (options.keyword) {
|
|
3783
|
+
const kw = options.keyword.toLowerCase();
|
|
3784
|
+
filtered = filtered.map((t) => {
|
|
3785
|
+
const title = (t.title || "").toLowerCase();
|
|
3786
|
+
const summary = (t.summary || "").toLowerCase();
|
|
3787
|
+
let relevance = 0;
|
|
3788
|
+
if (title.includes(kw)) relevance += 2;
|
|
3789
|
+
if (summary.includes(kw)) relevance += 1;
|
|
3790
|
+
if (t.task_slug.toLowerCase().includes(kw)) relevance += 1;
|
|
3791
|
+
return { ...t, relevance };
|
|
3792
|
+
}).filter((t) => t.relevance > 0);
|
|
3793
|
+
filtered.sort((a, b) => (b.relevance ?? 0) - (a.relevance ?? 0));
|
|
3794
|
+
} else {
|
|
3795
|
+
filtered.sort((a, b) => {
|
|
3796
|
+
const aDate = new Date(a.updated_at || 0).getTime();
|
|
3797
|
+
const bDate = new Date(b.updated_at || 0).getTime();
|
|
3798
|
+
return bDate - aDate;
|
|
3799
|
+
});
|
|
3800
|
+
}
|
|
3801
|
+
return filtered.slice(0, limit);
|
|
3802
|
+
}
|
|
3803
|
+
function validatePhase(projectName, taskSlug, phase) {
|
|
3804
|
+
const task = getTask(projectName, taskSlug);
|
|
3805
|
+
if (!task) {
|
|
3806
|
+
return {
|
|
3807
|
+
valid: false,
|
|
3808
|
+
phase,
|
|
3809
|
+
status: "not_found",
|
|
3810
|
+
missing_items: ["Task does not exist"],
|
|
3811
|
+
suggestions: [`Create task with: create_task(project: "${projectName}", task_slug: "${taskSlug}")`]
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
const agents = task.agents;
|
|
3815
|
+
const phaseData = agents?.[phase === "execution" ? "executor" : phase];
|
|
3816
|
+
const status = phaseData?.status || "pending";
|
|
3817
|
+
const missing = [];
|
|
3818
|
+
const suggestions = [];
|
|
3819
|
+
switch (phase) {
|
|
3820
|
+
case "research":
|
|
3821
|
+
if (status !== "complete") {
|
|
3822
|
+
missing.push("Research phase not complete");
|
|
3823
|
+
suggestions.push(`Run research phase: /rrce_research ${taskSlug}`);
|
|
3824
|
+
}
|
|
3825
|
+
if (!phaseData?.artifact) {
|
|
3826
|
+
missing.push("Research artifact not saved");
|
|
3827
|
+
suggestions.push("Save research brief to complete the phase");
|
|
3828
|
+
}
|
|
3829
|
+
break;
|
|
3830
|
+
case "planning":
|
|
3831
|
+
const researchStatus = agents?.research?.status;
|
|
3832
|
+
if (researchStatus !== "complete") {
|
|
3833
|
+
missing.push("Research phase not complete");
|
|
3834
|
+
suggestions.push(`Complete research first: /rrce_research ${taskSlug}`);
|
|
3835
|
+
}
|
|
3836
|
+
if (status !== "complete") {
|
|
3837
|
+
missing.push("Planning phase not complete");
|
|
3838
|
+
suggestions.push(`Run planning phase: /rrce_plan ${taskSlug}`);
|
|
3839
|
+
}
|
|
3840
|
+
if (!phaseData?.artifact) {
|
|
3841
|
+
missing.push("Planning artifact not saved");
|
|
3842
|
+
}
|
|
3843
|
+
if (!phaseData?.task_count) {
|
|
3844
|
+
missing.push("Task breakdown not defined");
|
|
3845
|
+
}
|
|
3846
|
+
break;
|
|
3847
|
+
case "execution":
|
|
3848
|
+
const planningStatus = agents?.planning?.status;
|
|
3849
|
+
if (planningStatus !== "complete") {
|
|
3850
|
+
missing.push("Planning phase not complete");
|
|
3851
|
+
suggestions.push(`Complete planning first: /rrce_plan ${taskSlug}`);
|
|
3852
|
+
}
|
|
3853
|
+
if (status !== "complete") {
|
|
3854
|
+
missing.push("Execution phase not complete");
|
|
3855
|
+
suggestions.push(`Run execution phase: /rrce_execute ${taskSlug}`);
|
|
3856
|
+
}
|
|
3857
|
+
break;
|
|
3858
|
+
case "documentation":
|
|
3859
|
+
const executorStatus = agents?.executor?.status;
|
|
3860
|
+
if (executorStatus !== "complete") {
|
|
3861
|
+
missing.push("Execution phase not complete");
|
|
3862
|
+
suggestions.push(`Complete execution first: /rrce_execute ${taskSlug}`);
|
|
3863
|
+
}
|
|
3864
|
+
if (status !== "complete") {
|
|
3865
|
+
missing.push("Documentation phase not complete");
|
|
3866
|
+
suggestions.push(`Run documentation phase: /rrce_docs ${taskSlug}`);
|
|
3867
|
+
}
|
|
3868
|
+
break;
|
|
3869
|
+
}
|
|
3870
|
+
return {
|
|
3871
|
+
valid: missing.length === 0,
|
|
3872
|
+
phase,
|
|
3873
|
+
status,
|
|
3874
|
+
missing_items: missing,
|
|
3875
|
+
suggestions
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
function startSession(projectName, taskSlug, agent, phase) {
|
|
3879
|
+
const config = loadMCPConfig();
|
|
3880
|
+
const projects = projectService.scan();
|
|
3881
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3882
|
+
if (!project || !project.tasksPath) {
|
|
3883
|
+
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3884
|
+
}
|
|
3885
|
+
const taskDir = path17.join(project.tasksPath, taskSlug);
|
|
3886
|
+
if (!fs15.existsSync(taskDir)) {
|
|
3887
|
+
return { success: false, message: `Task '${taskSlug}' not found.` };
|
|
3888
|
+
}
|
|
3889
|
+
const session = {
|
|
3890
|
+
agent,
|
|
3891
|
+
phase,
|
|
3892
|
+
task_slug: taskSlug,
|
|
3893
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3894
|
+
heartbeat: (/* @__PURE__ */ new Date()).toISOString()
|
|
3895
|
+
};
|
|
3896
|
+
const sessionPath = path17.join(taskDir, "session.json");
|
|
3897
|
+
fs15.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
|
|
3898
|
+
return { success: true, message: `Session started for ${agent} agent on task '${taskSlug}' (phase: ${phase})` };
|
|
3899
|
+
}
|
|
3900
|
+
function endSession(projectName, taskSlug) {
|
|
3901
|
+
const config = loadMCPConfig();
|
|
3902
|
+
const projects = projectService.scan();
|
|
3903
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3904
|
+
if (!project || !project.tasksPath) {
|
|
3905
|
+
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3906
|
+
}
|
|
3907
|
+
const sessionPath = path17.join(project.tasksPath, taskSlug, "session.json");
|
|
3908
|
+
if (!fs15.existsSync(sessionPath)) {
|
|
3909
|
+
return { success: true, message: `No active session for task '${taskSlug}'.` };
|
|
3910
|
+
}
|
|
3911
|
+
fs15.unlinkSync(sessionPath);
|
|
3912
|
+
return { success: true, message: `Session ended for task '${taskSlug}'.` };
|
|
3913
|
+
}
|
|
3914
|
+
function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
3915
|
+
const config = loadMCPConfig();
|
|
3916
|
+
const projects = projectService.scan();
|
|
3917
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3918
|
+
if (!project || !project.tasksPath) {
|
|
3919
|
+
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3920
|
+
}
|
|
3921
|
+
const taskDir = path17.join(project.tasksPath, taskSlug);
|
|
3922
|
+
if (!fs15.existsSync(taskDir)) {
|
|
3923
|
+
fs15.mkdirSync(taskDir, { recursive: true });
|
|
3924
|
+
}
|
|
3925
|
+
const todos = {
|
|
3926
|
+
phase,
|
|
3927
|
+
agent,
|
|
3928
|
+
items,
|
|
3929
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3930
|
+
};
|
|
3931
|
+
const todosPath = path17.join(taskDir, "agent-todos.json");
|
|
3932
|
+
fs15.writeFileSync(todosPath, JSON.stringify(todos, null, 2));
|
|
3933
|
+
return { success: true, message: `Updated ${items.length} todo items for task '${taskSlug}'.`, count: items.length };
|
|
3934
|
+
}
|
|
3163
3935
|
var INDEXABLE_EXTENSIONS, CODE_EXTENSIONS, SKIP_DIRS;
|
|
3164
3936
|
var init_resources = __esm({
|
|
3165
3937
|
"src/mcp/resources.ts"() {
|
|
@@ -3173,6 +3945,7 @@ var init_resources = __esm({
|
|
|
3173
3945
|
init_indexing_jobs();
|
|
3174
3946
|
init_context_extractor();
|
|
3175
3947
|
init_dependency_graph();
|
|
3948
|
+
init_symbol_extractor();
|
|
3176
3949
|
init_paths();
|
|
3177
3950
|
INDEXABLE_EXTENSIONS = [
|
|
3178
3951
|
".ts",
|
|
@@ -3476,25 +4249,29 @@ function registerToolHandlers(server) {
|
|
|
3476
4249
|
},
|
|
3477
4250
|
{
|
|
3478
4251
|
name: "search_knowledge",
|
|
3479
|
-
description: "Search across all exposed project knowledge bases",
|
|
4252
|
+
description: "Search across all exposed project knowledge bases. Returns results with token count and optional truncation.",
|
|
3480
4253
|
inputSchema: {
|
|
3481
4254
|
type: "object",
|
|
3482
4255
|
properties: {
|
|
3483
4256
|
query: { type: "string", description: "Search query to find in knowledge files" },
|
|
3484
|
-
project: { type: "string", description: "Optional: limit search to specific project name" }
|
|
4257
|
+
project: { type: "string", description: "Optional: limit search to specific project name" },
|
|
4258
|
+
max_tokens: { type: "number", description: "Optional: maximum tokens for response (truncates by relevance)" },
|
|
4259
|
+
min_score: { type: "number", description: "Optional: minimum relevance score threshold (0-1)" }
|
|
3485
4260
|
},
|
|
3486
4261
|
required: ["query"]
|
|
3487
4262
|
}
|
|
3488
4263
|
},
|
|
3489
4264
|
{
|
|
3490
4265
|
name: "search_code",
|
|
3491
|
-
description: "Semantic search across code files. Returns code snippets with line numbers
|
|
4266
|
+
description: "Semantic search across code files. Returns code snippets with line numbers, function/class context, and token budget support.",
|
|
3492
4267
|
inputSchema: {
|
|
3493
4268
|
type: "object",
|
|
3494
4269
|
properties: {
|
|
3495
4270
|
query: { type: "string", description: 'Search query (e.g., "error handling", "authentication logic", "database connection")' },
|
|
3496
4271
|
project: { type: "string", description: "Optional: limit search to specific project name" },
|
|
3497
|
-
limit: { type: "number", description: "Maximum number of results (default 10)" }
|
|
4272
|
+
limit: { type: "number", description: "Maximum number of results (default 10)" },
|
|
4273
|
+
max_tokens: { type: "number", description: "Optional: maximum tokens for response (truncates by relevance)" },
|
|
4274
|
+
min_score: { type: "number", description: "Optional: minimum relevance score threshold (0-1)" }
|
|
3498
4275
|
},
|
|
3499
4276
|
required: ["query"]
|
|
3500
4277
|
}
|
|
@@ -3514,6 +4291,99 @@ function registerToolHandlers(server) {
|
|
|
3514
4291
|
required: ["file", "project"]
|
|
3515
4292
|
}
|
|
3516
4293
|
},
|
|
4294
|
+
{
|
|
4295
|
+
name: "search_symbols",
|
|
4296
|
+
description: "Search for code symbols (functions, classes, types, variables) by name. Uses fuzzy matching. Faster than search_code for finding definitions.",
|
|
4297
|
+
inputSchema: {
|
|
4298
|
+
type: "object",
|
|
4299
|
+
properties: {
|
|
4300
|
+
name: { type: "string", description: "Symbol name to search for" },
|
|
4301
|
+
project: { type: "string", description: "Name of the project" },
|
|
4302
|
+
type: { type: "string", enum: ["function", "class", "type", "interface", "variable", "const", "enum", "any"], description: "Filter by symbol type (default: any)" },
|
|
4303
|
+
fuzzy: { type: "boolean", description: "Use fuzzy matching (default: true)" },
|
|
4304
|
+
limit: { type: "number", description: "Maximum results (default: 10)" }
|
|
4305
|
+
},
|
|
4306
|
+
required: ["name", "project"]
|
|
4307
|
+
}
|
|
4308
|
+
},
|
|
4309
|
+
{
|
|
4310
|
+
name: "get_file_summary",
|
|
4311
|
+
description: "Get a quick summary of a file without reading full content. Returns: language, LOC, exports, imports, key symbols.",
|
|
4312
|
+
inputSchema: {
|
|
4313
|
+
type: "object",
|
|
4314
|
+
properties: {
|
|
4315
|
+
file: { type: "string", description: "Path to the file (absolute or project-relative)" },
|
|
4316
|
+
project: { type: "string", description: "Name of the project" }
|
|
4317
|
+
},
|
|
4318
|
+
required: ["file", "project"]
|
|
4319
|
+
}
|
|
4320
|
+
},
|
|
4321
|
+
{
|
|
4322
|
+
name: "get_context_bundle",
|
|
4323
|
+
description: "Get bundled context for a query: project context + knowledge + code in one call. Reduces multi-tool chaining. Respects token budget.",
|
|
4324
|
+
inputSchema: {
|
|
4325
|
+
type: "object",
|
|
4326
|
+
properties: {
|
|
4327
|
+
query: { type: "string", description: "Natural language query or task description" },
|
|
4328
|
+
project: { type: "string", description: "Name of the project" },
|
|
4329
|
+
task_slug: { type: "string", description: "Optional: task slug to include task-specific context" },
|
|
4330
|
+
max_tokens: { type: "number", description: "Max tokens for response (default: 4000)" },
|
|
4331
|
+
include: {
|
|
4332
|
+
type: "object",
|
|
4333
|
+
description: "What to include in the bundle",
|
|
4334
|
+
properties: {
|
|
4335
|
+
project_context: { type: "boolean", description: "Include project context (default: true)" },
|
|
4336
|
+
knowledge: { type: "boolean", description: "Include knowledge search results (default: true)" },
|
|
4337
|
+
code: { type: "boolean", description: "Include code search results (default: true)" },
|
|
4338
|
+
related_files: { type: "boolean", description: "Include related files (default: false)" }
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
},
|
|
4342
|
+
required: ["query", "project"]
|
|
4343
|
+
}
|
|
4344
|
+
},
|
|
4345
|
+
{
|
|
4346
|
+
name: "prefetch_task_context",
|
|
4347
|
+
description: "Pre-gather all context for a task: task meta, referenced files, knowledge matches, code matches. Single call for task-aware context.",
|
|
4348
|
+
inputSchema: {
|
|
4349
|
+
type: "object",
|
|
4350
|
+
properties: {
|
|
4351
|
+
project: { type: "string", description: "Name of the project" },
|
|
4352
|
+
task_slug: { type: "string", description: "The task slug" },
|
|
4353
|
+
max_tokens: { type: "number", description: "Max tokens for response (default: 6000)" }
|
|
4354
|
+
},
|
|
4355
|
+
required: ["project", "task_slug"]
|
|
4356
|
+
}
|
|
4357
|
+
},
|
|
4358
|
+
{
|
|
4359
|
+
name: "search_tasks",
|
|
4360
|
+
description: "Search across all tasks by keyword, status, agent phase, or date. Returns matching tasks sorted by relevance.",
|
|
4361
|
+
inputSchema: {
|
|
4362
|
+
type: "object",
|
|
4363
|
+
properties: {
|
|
4364
|
+
project: { type: "string", description: "Name of the project" },
|
|
4365
|
+
keyword: { type: "string", description: "Search in title/summary" },
|
|
4366
|
+
status: { type: "string", description: "Filter by status (draft, in_progress, complete, etc.)" },
|
|
4367
|
+
agent: { type: "string", description: "Filter by agent phase (research, planning, executor, documentation)" },
|
|
4368
|
+
since: { type: "string", description: "ISO date - tasks updated after this date" },
|
|
4369
|
+
limit: { type: "number", description: "Max results (default: 20)" }
|
|
4370
|
+
},
|
|
4371
|
+
required: ["project"]
|
|
4372
|
+
}
|
|
4373
|
+
},
|
|
4374
|
+
{
|
|
4375
|
+
name: "validate_phase",
|
|
4376
|
+
description: "Check if a task phase has all prerequisites complete. Returns validation result with missing items and suggestions.",
|
|
4377
|
+
inputSchema: {
|
|
4378
|
+
type: "object",
|
|
4379
|
+
properties: {
|
|
4380
|
+
project: { type: "string", description: "Name of the project" },
|
|
4381
|
+
task_slug: { type: "string", description: "The task slug" },
|
|
4382
|
+
phase: { type: "string", enum: ["research", "planning", "execution", "documentation"], description: "Phase to validate" }
|
|
4383
|
+
},
|
|
4384
|
+
required: ["project", "task_slug", "phase"]
|
|
4385
|
+
}
|
|
4386
|
+
},
|
|
3517
4387
|
{
|
|
3518
4388
|
name: "index_knowledge",
|
|
3519
4389
|
description: "Update the semantic search index for a specific project",
|
|
@@ -3614,7 +4484,61 @@ function registerToolHandlers(server) {
|
|
|
3614
4484
|
project: { type: "string", description: "Name of the project" },
|
|
3615
4485
|
task_slug: { type: "string", description: "The slug of the task to delete" }
|
|
3616
4486
|
},
|
|
3617
|
-
required: ["project", "task_slug"]
|
|
4487
|
+
required: ["project", "task_slug"]
|
|
4488
|
+
}
|
|
4489
|
+
},
|
|
4490
|
+
{
|
|
4491
|
+
name: "start_session",
|
|
4492
|
+
description: "Start an agent session for active task tracking. Call this when beginning work on a task phase.",
|
|
4493
|
+
inputSchema: {
|
|
4494
|
+
type: "object",
|
|
4495
|
+
properties: {
|
|
4496
|
+
project: { type: "string", description: "Name of the project" },
|
|
4497
|
+
task_slug: { type: "string", description: "The slug of the task" },
|
|
4498
|
+
agent: { type: "string", description: "Agent type: research, planning, executor, or documentation" },
|
|
4499
|
+
phase: { type: "string", description: 'Current phase description (e.g., "clarification", "task breakdown")' }
|
|
4500
|
+
},
|
|
4501
|
+
required: ["project", "task_slug", "agent", "phase"]
|
|
4502
|
+
}
|
|
4503
|
+
},
|
|
4504
|
+
{
|
|
4505
|
+
name: "end_session",
|
|
4506
|
+
description: "End an agent session. Call this before emitting completion signal.",
|
|
4507
|
+
inputSchema: {
|
|
4508
|
+
type: "object",
|
|
4509
|
+
properties: {
|
|
4510
|
+
project: { type: "string", description: "Name of the project" },
|
|
4511
|
+
task_slug: { type: "string", description: "The slug of the task" }
|
|
4512
|
+
},
|
|
4513
|
+
required: ["project", "task_slug"]
|
|
4514
|
+
}
|
|
4515
|
+
},
|
|
4516
|
+
{
|
|
4517
|
+
name: "update_agent_todos",
|
|
4518
|
+
description: "Update the agent todo list for a task. Use this to track granular work items during a phase.",
|
|
4519
|
+
inputSchema: {
|
|
4520
|
+
type: "object",
|
|
4521
|
+
properties: {
|
|
4522
|
+
project: { type: "string", description: "Name of the project" },
|
|
4523
|
+
task_slug: { type: "string", description: "The slug of the task" },
|
|
4524
|
+
phase: { type: "string", description: "Current phase" },
|
|
4525
|
+
agent: { type: "string", description: "Agent type" },
|
|
4526
|
+
items: {
|
|
4527
|
+
type: "array",
|
|
4528
|
+
description: "Todo items array",
|
|
4529
|
+
items: {
|
|
4530
|
+
type: "object",
|
|
4531
|
+
properties: {
|
|
4532
|
+
id: { type: "string" },
|
|
4533
|
+
content: { type: "string" },
|
|
4534
|
+
status: { type: "string", enum: ["pending", "in_progress", "completed"] },
|
|
4535
|
+
priority: { type: "string", enum: ["high", "medium", "low"] }
|
|
4536
|
+
},
|
|
4537
|
+
required: ["id", "content", "status", "priority"]
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
},
|
|
4541
|
+
required: ["project", "task_slug", "phase", "agent", "items"]
|
|
3618
4542
|
}
|
|
3619
4543
|
}
|
|
3620
4544
|
];
|
|
@@ -3640,13 +4564,19 @@ function registerToolHandlers(server) {
|
|
|
3640
4564
|
}
|
|
3641
4565
|
case "search_knowledge": {
|
|
3642
4566
|
const params = args;
|
|
3643
|
-
const
|
|
3644
|
-
|
|
4567
|
+
const result = await searchKnowledge(params.query, params.project, {
|
|
4568
|
+
max_tokens: params.max_tokens,
|
|
4569
|
+
min_score: params.min_score
|
|
4570
|
+
});
|
|
4571
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
3645
4572
|
}
|
|
3646
4573
|
case "search_code": {
|
|
3647
4574
|
const params = args;
|
|
3648
|
-
const
|
|
3649
|
-
|
|
4575
|
+
const result = await searchCode(params.query, params.project, params.limit, {
|
|
4576
|
+
max_tokens: params.max_tokens,
|
|
4577
|
+
min_score: params.min_score
|
|
4578
|
+
});
|
|
4579
|
+
if (result.results.length === 0) {
|
|
3650
4580
|
return {
|
|
3651
4581
|
content: [{
|
|
3652
4582
|
type: "text",
|
|
@@ -3654,7 +4584,7 @@ function registerToolHandlers(server) {
|
|
|
3654
4584
|
}]
|
|
3655
4585
|
};
|
|
3656
4586
|
}
|
|
3657
|
-
return { content: [{ type: "text", text: JSON.stringify(
|
|
4587
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
3658
4588
|
}
|
|
3659
4589
|
case "find_related_files": {
|
|
3660
4590
|
const params = args;
|
|
@@ -3665,6 +4595,35 @@ function registerToolHandlers(server) {
|
|
|
3665
4595
|
});
|
|
3666
4596
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
3667
4597
|
}
|
|
4598
|
+
case "search_symbols": {
|
|
4599
|
+
const params = args;
|
|
4600
|
+
const result = await searchSymbols2(params.name, params.project, {
|
|
4601
|
+
type: params.type,
|
|
4602
|
+
fuzzy: params.fuzzy,
|
|
4603
|
+
limit: params.limit
|
|
4604
|
+
});
|
|
4605
|
+
if (!result.success) {
|
|
4606
|
+
return { content: [{ type: "text", text: result.message || "Search failed" }], isError: true };
|
|
4607
|
+
}
|
|
4608
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4609
|
+
}
|
|
4610
|
+
case "get_file_summary": {
|
|
4611
|
+
const params = args;
|
|
4612
|
+
const result = await getFileSummary(params.file, params.project);
|
|
4613
|
+
if (!result.success) {
|
|
4614
|
+
return { content: [{ type: "text", text: result.message || "Failed to get file summary" }], isError: true };
|
|
4615
|
+
}
|
|
4616
|
+
return { content: [{ type: "text", text: JSON.stringify(result.summary, null, 2) }] };
|
|
4617
|
+
}
|
|
4618
|
+
case "get_context_bundle": {
|
|
4619
|
+
const params = args;
|
|
4620
|
+
const result = await getContextBundle(params.query, params.project, {
|
|
4621
|
+
task_slug: params.task_slug,
|
|
4622
|
+
max_tokens: params.max_tokens,
|
|
4623
|
+
include: params.include
|
|
4624
|
+
});
|
|
4625
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4626
|
+
}
|
|
3668
4627
|
case "index_knowledge": {
|
|
3669
4628
|
const params = args;
|
|
3670
4629
|
const result = await indexKnowledge(params.project, params.force);
|
|
@@ -3756,6 +4715,47 @@ ${JSON.stringify(task, null, 2)}` }] };
|
|
|
3756
4715
|
const success = deleteTask(params.project, params.task_slug);
|
|
3757
4716
|
return { content: [{ type: "text", text: success ? `\u2713 Task '${params.task_slug}' deleted.` : `\u2717 Failed to delete '${params.task_slug}'.` }] };
|
|
3758
4717
|
}
|
|
4718
|
+
case "start_session": {
|
|
4719
|
+
const params = args;
|
|
4720
|
+
const result = startSession(params.project, params.task_slug, params.agent, params.phase);
|
|
4721
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4722
|
+
}
|
|
4723
|
+
case "end_session": {
|
|
4724
|
+
const params = args;
|
|
4725
|
+
const result = endSession(params.project, params.task_slug);
|
|
4726
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4727
|
+
}
|
|
4728
|
+
case "update_agent_todos": {
|
|
4729
|
+
const params = args;
|
|
4730
|
+
const result = updateAgentTodos(params.project, params.task_slug, params.phase, params.agent, params.items);
|
|
4731
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
4732
|
+
}
|
|
4733
|
+
case "prefetch_task_context": {
|
|
4734
|
+
const params = args;
|
|
4735
|
+
const result = await prefetchTaskContext(params.project, params.task_slug, {
|
|
4736
|
+
max_tokens: params.max_tokens
|
|
4737
|
+
});
|
|
4738
|
+
if (!result.success) {
|
|
4739
|
+
return { content: [{ type: "text", text: result.message || "Failed to prefetch task context" }], isError: true };
|
|
4740
|
+
}
|
|
4741
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4742
|
+
}
|
|
4743
|
+
case "search_tasks": {
|
|
4744
|
+
const params = args;
|
|
4745
|
+
const results = searchTasks(params.project, {
|
|
4746
|
+
keyword: params.keyword,
|
|
4747
|
+
status: params.status,
|
|
4748
|
+
agent: params.agent,
|
|
4749
|
+
since: params.since,
|
|
4750
|
+
limit: params.limit
|
|
4751
|
+
});
|
|
4752
|
+
return { content: [{ type: "text", text: JSON.stringify({ count: results.length, tasks: results }, null, 2) }] };
|
|
4753
|
+
}
|
|
4754
|
+
case "validate_phase": {
|
|
4755
|
+
const params = args;
|
|
4756
|
+
const result = validatePhase(params.project, params.task_slug, params.phase);
|
|
4757
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4758
|
+
}
|
|
3759
4759
|
case "help_setup": {
|
|
3760
4760
|
const msg = `
|
|
3761
4761
|
RRCE MCP Server is running, but no projects are configured/exposed.
|
|
@@ -4008,8 +5008,8 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
4008
5008
|
}
|
|
4009
5009
|
async function handleConfigureGlobalPath() {
|
|
4010
5010
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
4011
|
-
const
|
|
4012
|
-
const
|
|
5011
|
+
const fs27 = await import("fs");
|
|
5012
|
+
const path26 = await import("path");
|
|
4013
5013
|
note3(
|
|
4014
5014
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
4015
5015
|
and coordinate across projects.
|
|
@@ -4023,8 +5023,8 @@ locally in each project. MCP needs a central location.`,
|
|
|
4023
5023
|
return false;
|
|
4024
5024
|
}
|
|
4025
5025
|
try {
|
|
4026
|
-
if (!
|
|
4027
|
-
|
|
5026
|
+
if (!fs27.existsSync(resolvedPath)) {
|
|
5027
|
+
fs27.mkdirSync(resolvedPath, { recursive: true });
|
|
4028
5028
|
}
|
|
4029
5029
|
const config = loadMCPConfig();
|
|
4030
5030
|
saveMCPConfig(config);
|
|
@@ -4032,7 +5032,7 @@ locally in each project. MCP needs a central location.`,
|
|
|
4032
5032
|
`${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
|
|
4033
5033
|
|
|
4034
5034
|
MCP config will be stored at:
|
|
4035
|
-
${
|
|
5035
|
+
${path26.join(resolvedPath, "mcp.yaml")}`,
|
|
4036
5036
|
"Configuration Saved"
|
|
4037
5037
|
);
|
|
4038
5038
|
return true;
|
|
@@ -4312,6 +5312,37 @@ var init_ConfigContext = __esm({
|
|
|
4312
5312
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
4313
5313
|
import * as fs19 from "fs";
|
|
4314
5314
|
import * as path21 from "path";
|
|
5315
|
+
function readSession(project, taskSlug) {
|
|
5316
|
+
const rrceData = getProjectRRCEData(project);
|
|
5317
|
+
const sessionPath = path21.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5318
|
+
if (!fs19.existsSync(sessionPath)) {
|
|
5319
|
+
return null;
|
|
5320
|
+
}
|
|
5321
|
+
try {
|
|
5322
|
+
const raw = fs19.readFileSync(sessionPath, "utf-8");
|
|
5323
|
+
return JSON.parse(raw);
|
|
5324
|
+
} catch {
|
|
5325
|
+
return null;
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
5329
|
+
const heartbeatTime = Date.parse(session.heartbeat);
|
|
5330
|
+
if (isNaN(heartbeatTime)) return true;
|
|
5331
|
+
return Date.now() - heartbeatTime > thresholdMs;
|
|
5332
|
+
}
|
|
5333
|
+
function readAgentTodos(project, taskSlug) {
|
|
5334
|
+
const rrceData = getProjectRRCEData(project);
|
|
5335
|
+
const todosPath = path21.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5336
|
+
if (!fs19.existsSync(todosPath)) {
|
|
5337
|
+
return null;
|
|
5338
|
+
}
|
|
5339
|
+
try {
|
|
5340
|
+
const raw = fs19.readFileSync(todosPath, "utf-8");
|
|
5341
|
+
return JSON.parse(raw);
|
|
5342
|
+
} catch {
|
|
5343
|
+
return null;
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
4315
5346
|
function detectStorageModeFromConfig(workspaceRoot) {
|
|
4316
5347
|
const configPath = getConfigPath(workspaceRoot);
|
|
4317
5348
|
try {
|
|
@@ -4387,10 +5418,104 @@ function updateTaskStatus(project, taskSlug, status) {
|
|
|
4387
5418
|
return { ok: false, error: String(e) };
|
|
4388
5419
|
}
|
|
4389
5420
|
}
|
|
5421
|
+
var SESSION_STALE_THRESHOLD_MS;
|
|
4390
5422
|
var init_tasks_fs = __esm({
|
|
4391
5423
|
"src/mcp/ui/lib/tasks-fs.ts"() {
|
|
4392
5424
|
"use strict";
|
|
4393
5425
|
init_paths();
|
|
5426
|
+
SESSION_STALE_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
5427
|
+
}
|
|
5428
|
+
});
|
|
5429
|
+
|
|
5430
|
+
// src/mcp/ui/ui-helpers.ts
|
|
5431
|
+
var getStatusIcon, getStatusColor, getChecklistProgress, getCheckbox, getProgressBar, getFolderIcon, getAgentStatusIcon, getPhaseIcon, getTreeBranch, formatRelativeTime, getTodoStatusIcon;
|
|
5432
|
+
var init_ui_helpers = __esm({
|
|
5433
|
+
"src/mcp/ui/ui-helpers.ts"() {
|
|
5434
|
+
"use strict";
|
|
5435
|
+
getStatusIcon = (status) => {
|
|
5436
|
+
const icons = {
|
|
5437
|
+
pending: "\u23F3",
|
|
5438
|
+
in_progress: "\u{1F504}",
|
|
5439
|
+
blocked: "\u{1F6AB}",
|
|
5440
|
+
complete: "\u2705"
|
|
5441
|
+
};
|
|
5442
|
+
return icons[status] || "\u25CB";
|
|
5443
|
+
};
|
|
5444
|
+
getStatusColor = (status) => {
|
|
5445
|
+
const colors = {
|
|
5446
|
+
pending: "yellow",
|
|
5447
|
+
in_progress: "yellow",
|
|
5448
|
+
blocked: "red",
|
|
5449
|
+
complete: "green"
|
|
5450
|
+
};
|
|
5451
|
+
return colors[status] || "white";
|
|
5452
|
+
};
|
|
5453
|
+
getChecklistProgress = (checklist) => {
|
|
5454
|
+
if (!checklist || checklist.length === 0) {
|
|
5455
|
+
return { completed: 0, total: 0, percentage: 0 };
|
|
5456
|
+
}
|
|
5457
|
+
const completed = checklist.filter((item) => item.status === "done").length;
|
|
5458
|
+
return {
|
|
5459
|
+
completed,
|
|
5460
|
+
total: checklist.length,
|
|
5461
|
+
percentage: Math.round(completed / checklist.length * 100)
|
|
5462
|
+
};
|
|
5463
|
+
};
|
|
5464
|
+
getCheckbox = (status) => {
|
|
5465
|
+
return status === "done" ? "\u2611" : "\u2610";
|
|
5466
|
+
};
|
|
5467
|
+
getProgressBar = (percentage, length = 10) => {
|
|
5468
|
+
const filled = Math.floor(percentage / 100 * length);
|
|
5469
|
+
const empty = length - filled;
|
|
5470
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
5471
|
+
};
|
|
5472
|
+
getFolderIcon = (isOpen) => {
|
|
5473
|
+
return isOpen ? "\u{1F4C2}" : "\u{1F4C1}";
|
|
5474
|
+
};
|
|
5475
|
+
getAgentStatusIcon = (status) => {
|
|
5476
|
+
const icons = {
|
|
5477
|
+
complete: "\u2713",
|
|
5478
|
+
in_progress: "\u27F3",
|
|
5479
|
+
pending: "\u25CB",
|
|
5480
|
+
blocked: "\u2715"
|
|
5481
|
+
};
|
|
5482
|
+
return icons[status] || "\u2014";
|
|
5483
|
+
};
|
|
5484
|
+
getPhaseIcon = (agent) => {
|
|
5485
|
+
const icons = {
|
|
5486
|
+
research: "\u{1F52C}",
|
|
5487
|
+
planning: "\u{1F4DD}",
|
|
5488
|
+
executor: "\u26A1",
|
|
5489
|
+
documentation: "\u{1F4DA}"
|
|
5490
|
+
};
|
|
5491
|
+
return icons[agent] || "\u{1F527}";
|
|
5492
|
+
};
|
|
5493
|
+
getTreeBranch = (isLast) => {
|
|
5494
|
+
return isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
5495
|
+
};
|
|
5496
|
+
formatRelativeTime = (dateString) => {
|
|
5497
|
+
if (!dateString) return "\u2014";
|
|
5498
|
+
const date = Date.parse(dateString);
|
|
5499
|
+
if (isNaN(date)) return "\u2014";
|
|
5500
|
+
const now = Date.now();
|
|
5501
|
+
const diffMs = now - date;
|
|
5502
|
+
const diffMins = Math.floor(diffMs / (1e3 * 60));
|
|
5503
|
+
const diffHours = Math.floor(diffMs / (1e3 * 60 * 60));
|
|
5504
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
5505
|
+
if (diffMins < 1) return "just now";
|
|
5506
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
5507
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
5508
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
5509
|
+
return `${Math.floor(diffDays / 7)}w ago`;
|
|
5510
|
+
};
|
|
5511
|
+
getTodoStatusIcon = (status) => {
|
|
5512
|
+
const icons = {
|
|
5513
|
+
completed: "\u2713",
|
|
5514
|
+
in_progress: "\u27F3",
|
|
5515
|
+
pending: "\u25CB"
|
|
5516
|
+
};
|
|
5517
|
+
return icons[status] || "\u25CB";
|
|
5518
|
+
};
|
|
4394
5519
|
}
|
|
4395
5520
|
});
|
|
4396
5521
|
|
|
@@ -4405,6 +5530,7 @@ var init_Overview = __esm({
|
|
|
4405
5530
|
init_Header();
|
|
4406
5531
|
init_ConfigContext();
|
|
4407
5532
|
init_tasks_fs();
|
|
5533
|
+
init_ui_helpers();
|
|
4408
5534
|
Overview = ({ serverStatus, stats, logs }) => {
|
|
4409
5535
|
const { projects } = useConfig();
|
|
4410
5536
|
const activeTasks = useMemo2(() => {
|
|
@@ -4413,7 +5539,16 @@ var init_Overview = __esm({
|
|
|
4413
5539
|
const { tasks } = listProjectTasks(p);
|
|
4414
5540
|
const inProgress = tasks.filter((t) => t.status === "in_progress");
|
|
4415
5541
|
for (const t of inProgress) {
|
|
4416
|
-
|
|
5542
|
+
const session = readSession(p, t.task_slug);
|
|
5543
|
+
const todos = readAgentTodos(p, t.task_slug);
|
|
5544
|
+
active.push({
|
|
5545
|
+
project: p.name,
|
|
5546
|
+
title: t.title || t.task_slug,
|
|
5547
|
+
slug: t.task_slug,
|
|
5548
|
+
session,
|
|
5549
|
+
todos,
|
|
5550
|
+
isStale: session ? isSessionStale(session) : false
|
|
5551
|
+
});
|
|
4417
5552
|
}
|
|
4418
5553
|
}
|
|
4419
5554
|
return active;
|
|
@@ -4421,6 +5556,15 @@ var init_Overview = __esm({
|
|
|
4421
5556
|
const recentLogs = useMemo2(() => {
|
|
4422
5557
|
return logs.slice(-5).reverse();
|
|
4423
5558
|
}, [logs]);
|
|
5559
|
+
const getElapsedTime = (startedAt) => {
|
|
5560
|
+
const start = Date.parse(startedAt);
|
|
5561
|
+
if (isNaN(start)) return "";
|
|
5562
|
+
const elapsed = Date.now() - start;
|
|
5563
|
+
const mins = Math.floor(elapsed / 6e4);
|
|
5564
|
+
if (mins < 60) return `${mins}m`;
|
|
5565
|
+
const hours = Math.floor(mins / 60);
|
|
5566
|
+
return `${hours}h ${mins % 60}m`;
|
|
5567
|
+
};
|
|
4424
5568
|
return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", flexGrow: 1, children: [
|
|
4425
5569
|
/* @__PURE__ */ jsx3(Header, {}),
|
|
4426
5570
|
/* @__PURE__ */ jsxs(Box2, { borderStyle: "round", paddingX: 1, borderColor: "white", flexDirection: "column", flexGrow: 1, children: [
|
|
@@ -4477,13 +5621,35 @@ var init_Overview = __esm({
|
|
|
4477
5621
|
] }),
|
|
4478
5622
|
/* @__PURE__ */ jsxs(Box2, { marginTop: 1, borderStyle: "single", borderColor: "blue", flexDirection: "column", paddingX: 1, children: [
|
|
4479
5623
|
/* @__PURE__ */ jsx3(Text2, { bold: true, color: "blue", children: "\u{1F3C3} Active Tasks" }),
|
|
4480
|
-
activeTasks.length === 0 ? /* @__PURE__ */ jsx3(Box2, { paddingY: 1, children: /* @__PURE__ */ jsx3(Text2, { color: "dim", children: "No tasks currently in progress." }) }) : activeTasks.slice(0, 3).map((t, i) => /* @__PURE__ */ jsxs(Box2, { marginTop: i === 0 ? 0 :
|
|
4481
|
-
/* @__PURE__ */ jsxs(
|
|
4482
|
-
"\u{1F504} ",
|
|
4483
|
-
t.project,
|
|
4484
|
-
": "
|
|
5624
|
+
activeTasks.length === 0 ? /* @__PURE__ */ jsx3(Box2, { paddingY: 1, children: /* @__PURE__ */ jsx3(Text2, { color: "dim", children: "No tasks currently in progress." }) }) : activeTasks.slice(0, 3).map((t, i) => /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", marginTop: i === 0 ? 0 : 1, children: [
|
|
5625
|
+
/* @__PURE__ */ jsxs(Box2, { children: [
|
|
5626
|
+
/* @__PURE__ */ jsx3(Text2, { color: "yellow", children: "\u{1F504} " }),
|
|
5627
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, color: "white", children: t.project }),
|
|
5628
|
+
/* @__PURE__ */ jsx3(Text2, { color: "dim", children: ": " }),
|
|
5629
|
+
/* @__PURE__ */ jsx3(Text2, { color: "white", children: t.title })
|
|
5630
|
+
] }),
|
|
5631
|
+
t.session && /* @__PURE__ */ jsxs(Box2, { marginLeft: 3, children: [
|
|
5632
|
+
/* @__PURE__ */ jsxs(Text2, { children: [
|
|
5633
|
+
getPhaseIcon(t.session.agent),
|
|
5634
|
+
" "
|
|
5635
|
+
] }),
|
|
5636
|
+
/* @__PURE__ */ jsx3(Text2, { color: t.isStale ? "dim" : "cyan", children: t.session.agent }),
|
|
5637
|
+
/* @__PURE__ */ jsx3(Text2, { color: "dim", children: " \u2022 " }),
|
|
5638
|
+
/* @__PURE__ */ jsx3(Text2, { color: t.isStale ? "dim" : "white", children: t.session.phase }),
|
|
5639
|
+
/* @__PURE__ */ jsx3(Text2, { color: "dim", children: " \u2022 " }),
|
|
5640
|
+
/* @__PURE__ */ jsx3(Text2, { color: t.isStale ? "red" : "green", children: t.isStale ? "stale" : `${getElapsedTime(t.session.started_at)} elapsed` })
|
|
4485
5641
|
] }),
|
|
4486
|
-
/* @__PURE__ */ jsx3(
|
|
5642
|
+
t.todos && t.todos.items.length > 0 && /* @__PURE__ */ jsx3(Box2, { flexDirection: "column", marginLeft: 3, children: t.todos.items.slice(0, 3).map((item, idx) => /* @__PURE__ */ jsxs(Box2, { children: [
|
|
5643
|
+
/* @__PURE__ */ jsxs(Text2, { color: "dim", children: [
|
|
5644
|
+
getTreeBranch(idx === Math.min(2, t.todos.items.length - 1)),
|
|
5645
|
+
" "
|
|
5646
|
+
] }),
|
|
5647
|
+
/* @__PURE__ */ jsx3(Text2, { color: item.status === "completed" ? "green" : item.status === "in_progress" ? "yellow" : "dim", children: getTodoStatusIcon(item.status) }),
|
|
5648
|
+
/* @__PURE__ */ jsxs(Text2, { color: item.status === "completed" ? "dim" : "white", children: [
|
|
5649
|
+
" ",
|
|
5650
|
+
item.content.length > 40 ? item.content.substring(0, 37) + "..." : item.content
|
|
5651
|
+
] })
|
|
5652
|
+
] }, item.id)) })
|
|
4487
5653
|
] }, `${t.project}-${t.slug}`)),
|
|
4488
5654
|
activeTasks.length > 3 && /* @__PURE__ */ jsxs(Text2, { color: "dim", children: [
|
|
4489
5655
|
" ...and ",
|
|
@@ -4497,7 +5663,7 @@ var init_Overview = __esm({
|
|
|
4497
5663
|
] }),
|
|
4498
5664
|
/* @__PURE__ */ jsxs(Box2, { marginTop: 0, justifyContent: "space-between", children: [
|
|
4499
5665
|
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsx3(Text2, { color: "dim", children: "r:Restart q:Quit 1-4/\u25C4 \u25BA:Tabs" }) }),
|
|
4500
|
-
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsx3(Text2, { color: "dim", children: "RRCE MCP Hub v0.3.
|
|
5666
|
+
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsx3(Text2, { color: "dim", children: "RRCE MCP Hub v0.3.14" }) })
|
|
4501
5667
|
] })
|
|
4502
5668
|
] })
|
|
4503
5669
|
] });
|
|
@@ -4607,7 +5773,37 @@ var init_project_utils = __esm({
|
|
|
4607
5773
|
// src/mcp/ui/ProjectsView.tsx
|
|
4608
5774
|
import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
4609
5775
|
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
5776
|
+
import * as fs20 from "fs";
|
|
5777
|
+
import * as path22 from "path";
|
|
4610
5778
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
5779
|
+
function getIndexStats(project) {
|
|
5780
|
+
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
5781
|
+
try {
|
|
5782
|
+
const knowledgePath = project.knowledgePath;
|
|
5783
|
+
if (knowledgePath) {
|
|
5784
|
+
const embPath = path22.join(knowledgePath, "embeddings.json");
|
|
5785
|
+
const codeEmbPath = path22.join(knowledgePath, "code-embeddings.json");
|
|
5786
|
+
if (fs20.existsSync(embPath)) {
|
|
5787
|
+
const stat = fs20.statSync(embPath);
|
|
5788
|
+
stats.lastIndexed = stat.mtime.toISOString();
|
|
5789
|
+
try {
|
|
5790
|
+
const data = JSON.parse(fs20.readFileSync(embPath, "utf-8"));
|
|
5791
|
+
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5792
|
+
} catch {
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
if (fs20.existsSync(codeEmbPath)) {
|
|
5796
|
+
try {
|
|
5797
|
+
const data = JSON.parse(fs20.readFileSync(codeEmbPath, "utf-8"));
|
|
5798
|
+
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5799
|
+
} catch {
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
} catch {
|
|
5804
|
+
}
|
|
5805
|
+
return stats;
|
|
5806
|
+
}
|
|
4611
5807
|
var ProjectsView;
|
|
4612
5808
|
var init_ProjectsView = __esm({
|
|
4613
5809
|
"src/mcp/ui/ProjectsView.tsx"() {
|
|
@@ -4618,6 +5814,7 @@ var init_ProjectsView = __esm({
|
|
|
4618
5814
|
init_indexing_jobs();
|
|
4619
5815
|
init_config_utils();
|
|
4620
5816
|
init_project_utils();
|
|
5817
|
+
init_ui_helpers();
|
|
4621
5818
|
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
|
|
4622
5819
|
const { driftReports, checkAllDrift } = useConfig();
|
|
4623
5820
|
const [config, setConfig] = useState3(initialConfig);
|
|
@@ -4664,29 +5861,41 @@ var init_ProjectsView = __esm({
|
|
|
4664
5861
|
const isExposed = projectConfig ? projectConfig.expose : config.defaults.includeNew;
|
|
4665
5862
|
const drift = driftReports[p.path];
|
|
4666
5863
|
const idx = indexingStats[p.name];
|
|
5864
|
+
const stats = getIndexStats(p);
|
|
5865
|
+
const isCurrentProject = p.path === workspacePath;
|
|
4667
5866
|
let label = formatProjectLabel(p);
|
|
5867
|
+
if (isCurrentProject) {
|
|
5868
|
+
label += " (current)";
|
|
5869
|
+
}
|
|
4668
5870
|
if (drift?.hasDrift) {
|
|
4669
|
-
label +=
|
|
5871
|
+
label += " \u26A0";
|
|
4670
5872
|
}
|
|
5873
|
+
let statsRow = "";
|
|
4671
5874
|
if (idx?.state === "running") {
|
|
4672
|
-
|
|
4673
|
-
\u27F3 Indexing ${idx.itemsDone}/${idx.itemsTotal ?? "?"}`;
|
|
5875
|
+
statsRow = ` \u27F3 Indexing... ${idx.itemsDone}/${idx.itemsTotal ?? "?"}`;
|
|
4674
5876
|
} else if (idx?.state === "failed") {
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
5877
|
+
statsRow = " \u2715 Index failed";
|
|
5878
|
+
} else if (stats.knowledgeCount > 0 || stats.codeCount > 0) {
|
|
5879
|
+
const parts = [];
|
|
5880
|
+
if (stats.knowledgeCount > 0) parts.push(`\u{1F4DA} ${stats.knowledgeCount} docs`);
|
|
5881
|
+
if (stats.codeCount > 0) parts.push(`\u{1F4BB} ${stats.codeCount} files`);
|
|
5882
|
+
if (stats.lastIndexed) parts.push(`\u{1F550} ${formatRelativeTime(stats.lastIndexed)}`);
|
|
5883
|
+
statsRow = ` \u{1F4CA} ${parts.join(" \u2502 ")}`;
|
|
5884
|
+
} else if (isExposed) {
|
|
5885
|
+
statsRow = " \u{1F4CA} Not indexed";
|
|
4680
5886
|
}
|
|
5887
|
+
const fullLabel = statsRow ? `${label}
|
|
5888
|
+
${statsRow}` : label;
|
|
4681
5889
|
return {
|
|
4682
|
-
label,
|
|
5890
|
+
label: fullLabel,
|
|
4683
5891
|
value: p.path,
|
|
4684
5892
|
key: p.path,
|
|
4685
5893
|
exposed: isExposed,
|
|
4686
|
-
indexing: idx
|
|
5894
|
+
indexing: idx,
|
|
5895
|
+
isCurrentProject
|
|
4687
5896
|
};
|
|
4688
5897
|
});
|
|
4689
|
-
}, [sortedProjects, config, driftReports, indexingStats]);
|
|
5898
|
+
}, [sortedProjects, config, driftReports, indexingStats, workspacePath]);
|
|
4690
5899
|
const initialSelected = useMemo3(() => {
|
|
4691
5900
|
return projectItems.filter((p) => p.exposed).map((p) => p.value);
|
|
4692
5901
|
}, [projectItems]);
|
|
@@ -4745,87 +5954,103 @@ var init_ProjectsView = __esm({
|
|
|
4745
5954
|
}
|
|
4746
5955
|
});
|
|
4747
5956
|
|
|
4748
|
-
// src/mcp/ui/ui-helpers.ts
|
|
4749
|
-
var getStatusIcon, getStatusColor, getChecklistProgress, getCheckbox, getProgressBar, getFolderIcon;
|
|
4750
|
-
var init_ui_helpers = __esm({
|
|
4751
|
-
"src/mcp/ui/ui-helpers.ts"() {
|
|
4752
|
-
"use strict";
|
|
4753
|
-
getStatusIcon = (status) => {
|
|
4754
|
-
const icons = {
|
|
4755
|
-
pending: "\u23F3",
|
|
4756
|
-
in_progress: "\u{1F504}",
|
|
4757
|
-
blocked: "\u{1F6AB}",
|
|
4758
|
-
complete: "\u2705"
|
|
4759
|
-
};
|
|
4760
|
-
return icons[status] || "\u25CB";
|
|
4761
|
-
};
|
|
4762
|
-
getStatusColor = (status) => {
|
|
4763
|
-
const colors = {
|
|
4764
|
-
pending: "yellow",
|
|
4765
|
-
in_progress: "yellow",
|
|
4766
|
-
blocked: "red",
|
|
4767
|
-
complete: "green"
|
|
4768
|
-
};
|
|
4769
|
-
return colors[status] || "white";
|
|
4770
|
-
};
|
|
4771
|
-
getChecklistProgress = (checklist) => {
|
|
4772
|
-
if (!checklist || checklist.length === 0) {
|
|
4773
|
-
return { completed: 0, total: 0, percentage: 0 };
|
|
4774
|
-
}
|
|
4775
|
-
const completed = checklist.filter((item) => item.status === "done").length;
|
|
4776
|
-
return {
|
|
4777
|
-
completed,
|
|
4778
|
-
total: checklist.length,
|
|
4779
|
-
percentage: Math.round(completed / checklist.length * 100)
|
|
4780
|
-
};
|
|
4781
|
-
};
|
|
4782
|
-
getCheckbox = (status) => {
|
|
4783
|
-
return status === "done" ? "\u2611" : "\u2610";
|
|
4784
|
-
};
|
|
4785
|
-
getProgressBar = (percentage, length = 10) => {
|
|
4786
|
-
const filled = Math.floor(percentage / 100 * length);
|
|
4787
|
-
const empty = length - filled;
|
|
4788
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
4789
|
-
};
|
|
4790
|
-
getFolderIcon = (isOpen) => {
|
|
4791
|
-
return isOpen ? "\u{1F4C2}" : "\u{1F4C1}";
|
|
4792
|
-
};
|
|
4793
|
-
}
|
|
4794
|
-
});
|
|
4795
|
-
|
|
4796
5957
|
// src/mcp/ui/components/TaskRow.tsx
|
|
4797
5958
|
import "react";
|
|
4798
5959
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
4799
5960
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
5961
|
+
function getActiveAgent(task) {
|
|
5962
|
+
if (!task.agents) return null;
|
|
5963
|
+
for (const [agent, info] of Object.entries(task.agents)) {
|
|
5964
|
+
if (info?.status === "in_progress") {
|
|
5965
|
+
return { agent, status: "in_progress" };
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
const agentOrder = ["documentation", "executor", "planning", "research"];
|
|
5969
|
+
for (const agent of agentOrder) {
|
|
5970
|
+
if (task.agents[agent]?.status === "complete") {
|
|
5971
|
+
return { agent, status: "complete" };
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
return null;
|
|
5975
|
+
}
|
|
4800
5976
|
var TaskRow;
|
|
4801
5977
|
var init_TaskRow = __esm({
|
|
4802
5978
|
"src/mcp/ui/components/TaskRow.tsx"() {
|
|
4803
5979
|
"use strict";
|
|
4804
5980
|
init_ui_helpers();
|
|
4805
5981
|
init_project_utils();
|
|
4806
|
-
TaskRow = ({
|
|
5982
|
+
TaskRow = ({
|
|
5983
|
+
row,
|
|
5984
|
+
isSelected,
|
|
5985
|
+
isExpanded,
|
|
5986
|
+
taskCount,
|
|
5987
|
+
hasDrift,
|
|
5988
|
+
isCurrentProject = false,
|
|
5989
|
+
isLastTask = false
|
|
5990
|
+
}) => {
|
|
4807
5991
|
if (row.kind === "project") {
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
5992
|
+
const projectColor = isCurrentProject ? "cyan" : isSelected ? "yellow" : "white";
|
|
5993
|
+
const isBold = isCurrentProject || isSelected;
|
|
5994
|
+
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
5995
|
+
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8 " : " " }),
|
|
5996
|
+
/* @__PURE__ */ jsxs4(Text5, { bold: isBold, color: projectColor, children: [
|
|
4811
5997
|
getFolderIcon(isExpanded),
|
|
4812
5998
|
" ",
|
|
4813
5999
|
formatProjectLabel(row.project)
|
|
4814
6000
|
] }),
|
|
6001
|
+
isCurrentProject && /* @__PURE__ */ jsx6(Text5, { color: "cyan", dimColor: true, children: " (current)" }),
|
|
4815
6002
|
hasDrift && /* @__PURE__ */ jsx6(Text5, { color: "magenta", children: " \u26A0" }),
|
|
4816
6003
|
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
4817
6004
|
" ",
|
|
4818
|
-
taskCount > 0 ? `
|
|
6005
|
+
taskCount > 0 ? `[${taskCount}]` : ""
|
|
4819
6006
|
] })
|
|
4820
|
-
] })
|
|
6007
|
+
] });
|
|
6008
|
+
}
|
|
6009
|
+
const task = row.task;
|
|
6010
|
+
const taskLabel = task.title || task.task_slug;
|
|
6011
|
+
const status = task.status || "";
|
|
6012
|
+
const isPlaceholder = task.task_slug === "__none__";
|
|
6013
|
+
const branch = getTreeBranch(isLastTask);
|
|
6014
|
+
const activeAgent = getActiveAgent(task);
|
|
6015
|
+
const progress = getChecklistProgress(task.checklist || []);
|
|
6016
|
+
const relativeTime = task.updated_at ? formatRelativeTime(task.updated_at) : "";
|
|
6017
|
+
if (isPlaceholder) {
|
|
6018
|
+
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
6019
|
+
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6020
|
+
" ",
|
|
6021
|
+
branch,
|
|
6022
|
+
" "
|
|
6023
|
+
] }),
|
|
6024
|
+
/* @__PURE__ */ jsx6(Text5, { color: "dim", italic: true, children: taskLabel })
|
|
6025
|
+
] });
|
|
4821
6026
|
}
|
|
4822
|
-
const taskLabel = row.task.title || row.task.task_slug;
|
|
4823
|
-
const status = row.task.status || "";
|
|
4824
6027
|
return /* @__PURE__ */ jsxs4(Box5, { children: [
|
|
4825
|
-
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "
|
|
4826
|
-
/* @__PURE__ */
|
|
4827
|
-
|
|
4828
|
-
|
|
6028
|
+
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "dim", children: isSelected ? "\u25B8" : " " }),
|
|
6029
|
+
/* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6030
|
+
" ",
|
|
6031
|
+
branch,
|
|
6032
|
+
" "
|
|
6033
|
+
] }),
|
|
6034
|
+
/* @__PURE__ */ jsx6(Text5, { color: isSelected ? "cyan" : "white", children: "\u{1F4CB} " }),
|
|
6035
|
+
/* @__PURE__ */ jsx6(Text5, { bold: isSelected, color: isSelected ? "cyan" : "white", children: taskLabel.length > 25 ? taskLabel.substring(0, 22) + "..." : taskLabel }),
|
|
6036
|
+
activeAgent && /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
6037
|
+
/* @__PURE__ */ jsx6(Text5, { color: "dim", children: " " }),
|
|
6038
|
+
/* @__PURE__ */ jsx6(Text5, { children: getPhaseIcon(activeAgent.agent) }),
|
|
6039
|
+
/* @__PURE__ */ jsx6(Text5, { color: activeAgent.status === "in_progress" ? "yellow" : "green", children: getAgentStatusIcon(activeAgent.status) })
|
|
6040
|
+
] }),
|
|
6041
|
+
progress.total > 0 && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6042
|
+
" ",
|
|
6043
|
+
getProgressBar(progress.percentage, 6),
|
|
6044
|
+
" ",
|
|
6045
|
+
progress.completed,
|
|
6046
|
+
"/",
|
|
6047
|
+
progress.total
|
|
6048
|
+
] }),
|
|
6049
|
+
relativeTime && relativeTime !== "\u2014" && /* @__PURE__ */ jsxs4(Text5, { color: "dim", children: [
|
|
6050
|
+
" ",
|
|
6051
|
+
relativeTime
|
|
6052
|
+
] }),
|
|
6053
|
+
!activeAgent && status && /* @__PURE__ */ jsx6(Text5, { backgroundColor: getStatusColor(status), color: "black", children: ` ${getStatusIcon(status)} ` })
|
|
4829
6054
|
] });
|
|
4830
6055
|
};
|
|
4831
6056
|
}
|
|
@@ -4834,102 +6059,124 @@ var init_TaskRow = __esm({
|
|
|
4834
6059
|
// src/mcp/ui/components/TaskDetails.tsx
|
|
4835
6060
|
import "react";
|
|
4836
6061
|
import { Box as Box6, Text as Text6 } from "ink";
|
|
4837
|
-
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
6062
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4838
6063
|
var TaskDetails;
|
|
4839
6064
|
var init_TaskDetails = __esm({
|
|
4840
6065
|
"src/mcp/ui/components/TaskDetails.tsx"() {
|
|
4841
6066
|
"use strict";
|
|
4842
6067
|
init_ui_helpers();
|
|
4843
|
-
TaskDetails = ({ task }) => {
|
|
6068
|
+
TaskDetails = ({ task, agentTodos }) => {
|
|
4844
6069
|
if (!task) {
|
|
4845
6070
|
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", justifyContent: "center", alignItems: "center", gap: 1, flexGrow: 1, children: [
|
|
4846
6071
|
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "dim", children: "\u2500 No Task Selected \u2500" }),
|
|
4847
|
-
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Use \u2191/\u2193 to navigate
|
|
4848
|
-
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "
|
|
6072
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Use \u2191/\u2193 to navigate" }),
|
|
6073
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Enter to expand projects" }),
|
|
6074
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "'s' to cycle task status" })
|
|
4849
6075
|
] });
|
|
4850
6076
|
}
|
|
6077
|
+
const progress = getChecklistProgress(task.checklist || []);
|
|
4851
6078
|
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
4852
6079
|
/* @__PURE__ */ jsxs5(Box6, { marginBottom: 1, flexDirection: "column", children: [
|
|
4853
6080
|
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "cyan", children: task.title || task.task_slug }),
|
|
4854
|
-
task.summary && /* @__PURE__ */ jsx7(Text6, { color: "white", children: task.summary })
|
|
6081
|
+
task.summary && /* @__PURE__ */ jsx7(Text6, { color: "white", wrap: "wrap", children: task.summary.length > 100 ? task.summary.substring(0, 97) + "..." : task.summary })
|
|
4855
6082
|
] }),
|
|
4856
|
-
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4857
|
-
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1,
|
|
6083
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
6084
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
4858
6085
|
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F4CB} STATUS" }),
|
|
4859
|
-
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
4860
|
-
/* @__PURE__ */ jsxs5(
|
|
4861
|
-
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Status:
|
|
4862
|
-
" ",
|
|
6086
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 0, marginLeft: 1, children: [
|
|
6087
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6088
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Status: " }),
|
|
4863
6089
|
/* @__PURE__ */ jsx7(Text6, { color: getStatusColor(task.status || ""), children: task.status || "unknown" })
|
|
4864
6090
|
] }),
|
|
4865
|
-
/* @__PURE__ */ jsxs5(
|
|
4866
|
-
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Updated:" }),
|
|
4867
|
-
"
|
|
4868
|
-
/* @__PURE__ */ jsx7(Text6, { children: task.updated_at || "\u2014" })
|
|
6091
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6092
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Updated: " }),
|
|
6093
|
+
/* @__PURE__ */ jsx7(Text6, { children: formatRelativeTime(task.updated_at || "") })
|
|
4869
6094
|
] }),
|
|
4870
|
-
/* @__PURE__ */ jsxs5(
|
|
4871
|
-
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Tags:
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
return tags.length > 0 ? tags.map((tag, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
4877
|
-
/* @__PURE__ */ jsx7(Text6, { color: "cyan", children: tag }),
|
|
4878
|
-
i < tags.length - 1 && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: ", " })
|
|
4879
|
-
] }, tag)) : /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u2014" });
|
|
4880
|
-
})()
|
|
6095
|
+
task.tags && task.tags.length > 0 && /* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6096
|
+
/* @__PURE__ */ jsx7(Text6, { color: "dim", children: "Tags: " }),
|
|
6097
|
+
task.tags.map((tag, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
6098
|
+
/* @__PURE__ */ jsx7(Text6, { color: "cyan", children: tag }),
|
|
6099
|
+
i < task.tags.length - 1 && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: ", " })
|
|
6100
|
+
] }, tag))
|
|
4881
6101
|
] })
|
|
4882
6102
|
] })
|
|
4883
6103
|
] }),
|
|
4884
|
-
/* @__PURE__ */ jsx7(
|
|
4885
|
-
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1,
|
|
4886
|
-
/* @__PURE__ */
|
|
4887
|
-
|
|
4888
|
-
/* @__PURE__ */
|
|
4889
|
-
|
|
4890
|
-
" ",
|
|
6104
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
6105
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
6106
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6107
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F4CB} CHECKLIST " }),
|
|
6108
|
+
progress.total > 0 && /* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
|
|
6109
|
+
getProgressBar(progress.percentage, 8),
|
|
4891
6110
|
" ",
|
|
4892
|
-
|
|
6111
|
+
progress.completed,
|
|
4893
6112
|
"/",
|
|
4894
|
-
|
|
4895
|
-
" (",
|
|
4896
|
-
getChecklistProgress(task.checklist).percentage,
|
|
4897
|
-
"%)"
|
|
6113
|
+
progress.total
|
|
4898
6114
|
] })
|
|
4899
|
-
] })
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
/* @__PURE__ */ jsxs5(Text6, { color: isDone ? "
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
6115
|
+
] }),
|
|
6116
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginLeft: 1, children: [
|
|
6117
|
+
(task.checklist || []).length === 0 ? /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "No checklist items" }) : (task.checklist || []).slice(0, 8).map((c, i) => {
|
|
6118
|
+
const isDone = c.status === "done";
|
|
6119
|
+
return /* @__PURE__ */ jsxs5(Text6, { color: isDone ? "dim" : "white", children: [
|
|
6120
|
+
/* @__PURE__ */ jsxs5(Text6, { color: isDone ? "green" : "dim", children: [
|
|
6121
|
+
getCheckbox(c.status || "pending"),
|
|
6122
|
+
" "
|
|
6123
|
+
] }),
|
|
6124
|
+
(c.label || c.id || "item").substring(0, 35)
|
|
6125
|
+
] }, c.id || i);
|
|
6126
|
+
}),
|
|
6127
|
+
(task.checklist || []).length > 8 && /* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
|
|
6128
|
+
"...and ",
|
|
6129
|
+
(task.checklist || []).length - 8,
|
|
6130
|
+
" more"
|
|
6131
|
+
] })
|
|
6132
|
+
] })
|
|
6133
|
+
] }),
|
|
6134
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
6135
|
+
agentTodos && agentTodos.items.length > 0 && /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
6136
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
6137
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6138
|
+
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F3AF} AGENT TODOS " }),
|
|
6139
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
|
|
6140
|
+
"(",
|
|
6141
|
+
agentTodos.agent,
|
|
6142
|
+
" \u2022 ",
|
|
6143
|
+
agentTodos.phase,
|
|
6144
|
+
")"
|
|
6145
|
+
] })
|
|
6146
|
+
] }),
|
|
6147
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginLeft: 1, children: [
|
|
6148
|
+
agentTodos.items.slice(0, 5).map((item, i) => /* @__PURE__ */ jsxs5(Text6, { children: [
|
|
6149
|
+
/* @__PURE__ */ jsxs5(Text6, { color: item.status === "completed" ? "green" : item.status === "in_progress" ? "yellow" : "dim", children: [
|
|
6150
|
+
getTodoStatusIcon(item.status),
|
|
6151
|
+
" "
|
|
6152
|
+
] }),
|
|
6153
|
+
/* @__PURE__ */ jsx7(Text6, { color: item.status === "completed" ? "dim" : "white", children: item.content.substring(0, 35) })
|
|
6154
|
+
] }, item.id || i)),
|
|
6155
|
+
agentTodos.items.length > 5 && /* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
|
|
6156
|
+
"...and ",
|
|
6157
|
+
agentTodos.items.length - 5,
|
|
6158
|
+
" more"
|
|
6159
|
+
] })
|
|
6160
|
+
] })
|
|
6161
|
+
] }),
|
|
6162
|
+
/* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" })
|
|
4910
6163
|
] }),
|
|
4911
|
-
/* @__PURE__ */
|
|
4912
|
-
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
|
|
6164
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
4913
6165
|
/* @__PURE__ */ jsx7(Text6, { bold: true, color: "white", children: "\u{1F916} AGENTS" }),
|
|
4914
|
-
/* @__PURE__ */ jsx7(Box6, {
|
|
6166
|
+
/* @__PURE__ */ jsx7(Box6, { flexDirection: "column", marginLeft: 1, children: !task.agents || Object.keys(task.agents).length === 0 ? /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "No agent activity yet" }) : Object.entries(task.agents).map(([agent, info]) => /* @__PURE__ */ jsxs5(Box6, { children: [
|
|
6167
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
6168
|
+
getPhaseIcon(agent),
|
|
6169
|
+
" "
|
|
6170
|
+
] }),
|
|
4915
6171
|
/* @__PURE__ */ jsxs5(Text6, { color: "dim", children: [
|
|
4916
|
-
"- ",
|
|
4917
6172
|
agent,
|
|
4918
6173
|
": "
|
|
4919
6174
|
] }),
|
|
4920
|
-
info?.status === "complete" && /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u2713" }),
|
|
4921
|
-
info?.status === "in_progress" && /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "\u27F3" }),
|
|
4922
|
-
info?.status === "pending" && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u25CB" }),
|
|
4923
|
-
info?.blocked && /* @__PURE__ */ jsx7(Text6, { color: "red", children: "\u2715" }),
|
|
4924
|
-
/* @__PURE__ */
|
|
4925
|
-
" ",
|
|
4926
|
-
info?.status || "\u2014"
|
|
4927
|
-
] }),
|
|
4928
|
-
info?.artifact && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
4929
|
-
" (",
|
|
4930
|
-
info.artifact,
|
|
4931
|
-
")"
|
|
4932
|
-
] })
|
|
6175
|
+
info?.status === "complete" && /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u2713 " }),
|
|
6176
|
+
info?.status === "in_progress" && /* @__PURE__ */ jsx7(Text6, { color: "yellow", children: "\u27F3 " }),
|
|
6177
|
+
info?.status === "pending" && /* @__PURE__ */ jsx7(Text6, { color: "dim", children: "\u25CB " }),
|
|
6178
|
+
info?.blocked && /* @__PURE__ */ jsx7(Text6, { color: "red", children: "\u2715 " }),
|
|
6179
|
+
/* @__PURE__ */ jsx7(Text6, { color: info?.status === "complete" ? "dim" : "white", children: info?.status || "pending" })
|
|
4933
6180
|
] }, agent)) })
|
|
4934
6181
|
] })
|
|
4935
6182
|
] });
|
|
@@ -4993,19 +6240,30 @@ var init_TasksView = __esm({
|
|
|
4993
6240
|
const flattenedRows = useMemo4(() => {
|
|
4994
6241
|
const rows = [];
|
|
4995
6242
|
for (const p of sortedProjects) {
|
|
4996
|
-
|
|
6243
|
+
const isCurrentProject = p.path === workspacePath;
|
|
6244
|
+
rows.push({ kind: "project", project: p, isCurrentProject });
|
|
4997
6245
|
const k = projectKey(p);
|
|
4998
6246
|
if (!expanded.has(k)) continue;
|
|
4999
6247
|
const tasks = taskCache[k] || [];
|
|
5000
|
-
|
|
5001
|
-
rows.push({
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
6248
|
+
tasks.forEach((t, idx) => {
|
|
6249
|
+
rows.push({
|
|
6250
|
+
kind: "task",
|
|
6251
|
+
project: p,
|
|
6252
|
+
task: t,
|
|
6253
|
+
isLastTask: idx === tasks.length - 1
|
|
6254
|
+
});
|
|
6255
|
+
});
|
|
6256
|
+
if (tasks.length === 0) {
|
|
6257
|
+
rows.push({
|
|
6258
|
+
kind: "task",
|
|
6259
|
+
project: p,
|
|
6260
|
+
task: { task_slug: "__none__", title: "(no tasks)", status: "" },
|
|
6261
|
+
isLastTask: true
|
|
6262
|
+
});
|
|
5005
6263
|
}
|
|
5006
6264
|
}
|
|
5007
6265
|
return rows;
|
|
5008
|
-
}, [sortedProjects, expanded, taskCache]);
|
|
6266
|
+
}, [sortedProjects, expanded, taskCache, workspacePath]);
|
|
5009
6267
|
useInput3((input, key) => {
|
|
5010
6268
|
if (input === "R") {
|
|
5011
6269
|
setErrorLine(null);
|
|
@@ -5037,7 +6295,7 @@ var init_TasksView = __esm({
|
|
|
5037
6295
|
}
|
|
5038
6296
|
if (input === "s") {
|
|
5039
6297
|
const row = flattenedRows[selectedIndex];
|
|
5040
|
-
if (row?.kind === "task") {
|
|
6298
|
+
if (row?.kind === "task" && row.task.task_slug !== "__none__") {
|
|
5041
6299
|
setErrorLine(null);
|
|
5042
6300
|
const desired = nextStatus(row.task.status);
|
|
5043
6301
|
const result = updateTaskStatus(row.project, row.task.task_slug, desired);
|
|
@@ -5063,43 +6321,55 @@ var init_TasksView = __esm({
|
|
|
5063
6321
|
}, [flattenedRows]);
|
|
5064
6322
|
const selectedRow = flattenedRows[selectedIndex];
|
|
5065
6323
|
const selectedTask = selectedRow?.kind === "task" && selectedRow.task.task_slug !== "__none__" ? selectedRow.task : null;
|
|
5066
|
-
|
|
5067
|
-
|
|
6324
|
+
const totalTasks = Object.values(taskCache).flat().length;
|
|
6325
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
|
|
6326
|
+
/* @__PURE__ */ jsxs6(Box7, { paddingX: 1, justifyContent: "space-between", borderBottom: true, children: [
|
|
5068
6327
|
/* @__PURE__ */ jsxs6(Box7, { children: [
|
|
5069
6328
|
/* @__PURE__ */ jsx8(Text7, { bold: true, color: "cyan", children: "\u2699 Tasks" }),
|
|
5070
|
-
/* @__PURE__ */ jsx8(Text7, {
|
|
6329
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2502 " }),
|
|
5071
6330
|
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
5072
6331
|
sortedProjects.length,
|
|
5073
6332
|
" projects"
|
|
5074
6333
|
] }),
|
|
5075
|
-
/* @__PURE__ */ jsx8(Text7, {
|
|
6334
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: " \u2022 " }),
|
|
5076
6335
|
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
5077
|
-
|
|
6336
|
+
totalTasks,
|
|
5078
6337
|
" tasks"
|
|
5079
6338
|
] })
|
|
5080
6339
|
] }),
|
|
5081
|
-
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "\u2191
|
|
6340
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "\u2191\u2193:Nav Enter:Expand s:Status R:Refresh" })
|
|
5082
6341
|
] }),
|
|
5083
|
-
errorLine && /* @__PURE__ */ jsx8(Box7, {
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
6342
|
+
errorLine && /* @__PURE__ */ jsx8(Box7, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
|
|
6343
|
+
"\u26A0 ",
|
|
6344
|
+
errorLine
|
|
6345
|
+
] }) }),
|
|
6346
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", flexGrow: 1, paddingX: 1, paddingY: 1, children: [
|
|
6347
|
+
/* @__PURE__ */ jsx8(Box7, { flexDirection: "column", width: "55%", borderStyle: "single", borderColor: "dim", borderRight: true, paddingRight: 1, children: flattenedRows.length === 0 ? /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: [
|
|
6348
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "No projects detected." }),
|
|
6349
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "Run the wizard to set up projects." })
|
|
6350
|
+
] }) : /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", children: flattenedRows.map((row, idx) => {
|
|
6351
|
+
const k = projectKey(row.project);
|
|
6352
|
+
const isCurrentProject = row.kind === "project" ? row.isCurrentProject : false;
|
|
6353
|
+
const isLastTask = row.kind === "task" ? row.isLastTask : false;
|
|
6354
|
+
return /* @__PURE__ */ jsx8(
|
|
6355
|
+
TaskRow,
|
|
6356
|
+
{
|
|
6357
|
+
row,
|
|
6358
|
+
isSelected: idx === selectedIndex,
|
|
6359
|
+
isExpanded: expanded.has(k),
|
|
6360
|
+
taskCount: (taskCache[k] || []).length,
|
|
6361
|
+
hasDrift: !!driftReports[row.project.path]?.hasDrift,
|
|
6362
|
+
isCurrentProject,
|
|
6363
|
+
isLastTask
|
|
6364
|
+
},
|
|
6365
|
+
row.kind === "project" ? `p:${k}` : `t:${k}:${row.task.task_slug}`
|
|
6366
|
+
);
|
|
6367
|
+
}) }) }),
|
|
6368
|
+
/* @__PURE__ */ jsx8(Box7, { flexDirection: "column", width: "45%", paddingLeft: 1, children: /* @__PURE__ */ jsx8(TaskDetails, { task: selectedTask }) })
|
|
6369
|
+
] }),
|
|
6370
|
+
/* @__PURE__ */ jsxs6(Box7, { paddingX: 1, justifyContent: "space-between", borderTop: true, children: [
|
|
6371
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "s:Cycle Status R:Refresh ?:Help" }),
|
|
6372
|
+
/* @__PURE__ */ jsx8(Text7, { color: "dim", children: "RRCE MCP Hub v0.3.14" })
|
|
5103
6373
|
] })
|
|
5104
6374
|
] });
|
|
5105
6375
|
};
|
|
@@ -5227,7 +6497,7 @@ __export(App_exports, {
|
|
|
5227
6497
|
});
|
|
5228
6498
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
5229
6499
|
import { Box as Box11, useInput as useInput5, useApp } from "ink";
|
|
5230
|
-
import
|
|
6500
|
+
import fs21 from "fs";
|
|
5231
6501
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5232
6502
|
var App;
|
|
5233
6503
|
var init_App = __esm({
|
|
@@ -5300,18 +6570,18 @@ var init_App = __esm({
|
|
|
5300
6570
|
useEffect6(() => {
|
|
5301
6571
|
const logPath = getLogFilePath();
|
|
5302
6572
|
let lastSize = 0;
|
|
5303
|
-
if (
|
|
5304
|
-
const stats =
|
|
6573
|
+
if (fs21.existsSync(logPath)) {
|
|
6574
|
+
const stats = fs21.statSync(logPath);
|
|
5305
6575
|
lastSize = stats.size;
|
|
5306
6576
|
}
|
|
5307
6577
|
const interval = setInterval(() => {
|
|
5308
|
-
if (
|
|
5309
|
-
const stats =
|
|
6578
|
+
if (fs21.existsSync(logPath)) {
|
|
6579
|
+
const stats = fs21.statSync(logPath);
|
|
5310
6580
|
if (stats.size > lastSize) {
|
|
5311
6581
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
5312
|
-
const fd =
|
|
5313
|
-
|
|
5314
|
-
|
|
6582
|
+
const fd = fs21.openSync(logPath, "r");
|
|
6583
|
+
fs21.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
6584
|
+
fs21.closeSync(fd);
|
|
5315
6585
|
const newContent = buffer.toString("utf-8");
|
|
5316
6586
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
5317
6587
|
setLogs((prev) => {
|
|
@@ -5536,15 +6806,15 @@ __export(update_flow_exports, {
|
|
|
5536
6806
|
});
|
|
5537
6807
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
5538
6808
|
import pc8 from "picocolors";
|
|
5539
|
-
import * as
|
|
5540
|
-
import * as
|
|
6809
|
+
import * as fs22 from "fs";
|
|
6810
|
+
import * as path23 from "path";
|
|
5541
6811
|
import { stringify as stringify2, parse } from "yaml";
|
|
5542
6812
|
function backupFile(filePath) {
|
|
5543
|
-
if (!
|
|
6813
|
+
if (!fs22.existsSync(filePath)) return null;
|
|
5544
6814
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
5545
6815
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
5546
6816
|
try {
|
|
5547
|
-
|
|
6817
|
+
fs22.copyFileSync(filePath, backupPath);
|
|
5548
6818
|
return backupPath;
|
|
5549
6819
|
} catch (e) {
|
|
5550
6820
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -5554,9 +6824,9 @@ function backupFile(filePath) {
|
|
|
5554
6824
|
function getPackageVersion2() {
|
|
5555
6825
|
try {
|
|
5556
6826
|
const agentCoreDir = getAgentCoreDir();
|
|
5557
|
-
const packageJsonPath =
|
|
5558
|
-
if (
|
|
5559
|
-
return JSON.parse(
|
|
6827
|
+
const packageJsonPath = path23.join(path23.dirname(agentCoreDir), "package.json");
|
|
6828
|
+
if (fs22.existsSync(packageJsonPath)) {
|
|
6829
|
+
return JSON.parse(fs22.readFileSync(packageJsonPath, "utf8")).version;
|
|
5560
6830
|
}
|
|
5561
6831
|
} catch (e) {
|
|
5562
6832
|
}
|
|
@@ -5571,9 +6841,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5571
6841
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
5572
6842
|
const configFilePath = getConfigPath(workspacePath);
|
|
5573
6843
|
let currentSyncedVersion;
|
|
5574
|
-
if (
|
|
6844
|
+
if (fs22.existsSync(configFilePath)) {
|
|
5575
6845
|
try {
|
|
5576
|
-
const content =
|
|
6846
|
+
const content = fs22.readFileSync(configFilePath, "utf-8");
|
|
5577
6847
|
const config = parse(content);
|
|
5578
6848
|
currentSyncedVersion = config.last_synced_version;
|
|
5579
6849
|
} catch (e) {
|
|
@@ -5581,8 +6851,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5581
6851
|
}
|
|
5582
6852
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
5583
6853
|
const ideTargets = [];
|
|
5584
|
-
if (
|
|
5585
|
-
const configContent =
|
|
6854
|
+
if (fs22.existsSync(configFilePath)) {
|
|
6855
|
+
const configContent = fs22.readFileSync(configFilePath, "utf-8");
|
|
5586
6856
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
5587
6857
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
5588
6858
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -5591,14 +6861,14 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5591
6861
|
const dirs = ["templates", "prompts", "docs"];
|
|
5592
6862
|
const updatedFiles = [];
|
|
5593
6863
|
for (const dir of dirs) {
|
|
5594
|
-
const srcDir =
|
|
5595
|
-
if (!
|
|
6864
|
+
const srcDir = path23.join(agentCoreDir, dir);
|
|
6865
|
+
if (!fs22.existsSync(srcDir)) continue;
|
|
5596
6866
|
const syncFiles = (src, rel) => {
|
|
5597
|
-
const entries =
|
|
6867
|
+
const entries = fs22.readdirSync(src, { withFileTypes: true });
|
|
5598
6868
|
for (const entry of entries) {
|
|
5599
|
-
const entrySrc =
|
|
5600
|
-
const entryRel =
|
|
5601
|
-
const entryDest =
|
|
6869
|
+
const entrySrc = path23.join(src, entry.name);
|
|
6870
|
+
const entryRel = path23.join(rel, entry.name);
|
|
6871
|
+
const entryDest = path23.join(dataPath, entryRel);
|
|
5602
6872
|
if (entry.isDirectory()) {
|
|
5603
6873
|
ensureDir(entryDest);
|
|
5604
6874
|
syncFiles(entrySrc, entryRel);
|
|
@@ -5606,7 +6876,7 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5606
6876
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
5607
6877
|
backupFile(entryDest);
|
|
5608
6878
|
}
|
|
5609
|
-
|
|
6879
|
+
fs22.copyFileSync(entrySrc, entryDest);
|
|
5610
6880
|
updatedFiles.push(entryRel);
|
|
5611
6881
|
}
|
|
5612
6882
|
}
|
|
@@ -5617,12 +6887,12 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5617
6887
|
DriftService.saveManifest(dataPath, manifest);
|
|
5618
6888
|
}
|
|
5619
6889
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
5620
|
-
ensureDir(
|
|
5621
|
-
ensureDir(
|
|
5622
|
-
copyDirRecursive(
|
|
5623
|
-
copyDirRecursive(
|
|
5624
|
-
if (
|
|
5625
|
-
const configContent =
|
|
6890
|
+
ensureDir(path23.join(rrceHome, "templates"));
|
|
6891
|
+
ensureDir(path23.join(rrceHome, "docs"));
|
|
6892
|
+
copyDirRecursive(path23.join(agentCoreDir, "templates"), path23.join(rrceHome, "templates"));
|
|
6893
|
+
copyDirRecursive(path23.join(agentCoreDir, "docs"), path23.join(rrceHome, "docs"));
|
|
6894
|
+
if (fs22.existsSync(configFilePath)) {
|
|
6895
|
+
const configContent = fs22.readFileSync(configFilePath, "utf-8");
|
|
5626
6896
|
if (configContent.includes("copilot: true")) {
|
|
5627
6897
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
5628
6898
|
ensureDir(copilotPath);
|
|
@@ -5644,21 +6914,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
5644
6914
|
try {
|
|
5645
6915
|
const yaml = parse(configContent);
|
|
5646
6916
|
yaml.last_synced_version = runningVersion;
|
|
5647
|
-
|
|
6917
|
+
fs22.writeFileSync(configFilePath, stringify2(yaml));
|
|
5648
6918
|
} catch (e) {
|
|
5649
6919
|
console.error("Failed to update config.yaml version:", e);
|
|
5650
6920
|
}
|
|
5651
6921
|
}
|
|
5652
|
-
const mcpPath =
|
|
5653
|
-
if (
|
|
6922
|
+
const mcpPath = path23.join(rrceHome, "mcp.yaml");
|
|
6923
|
+
if (fs22.existsSync(mcpPath)) {
|
|
5654
6924
|
try {
|
|
5655
|
-
const content =
|
|
6925
|
+
const content = fs22.readFileSync(mcpPath, "utf-8");
|
|
5656
6926
|
const yaml = parse(content);
|
|
5657
6927
|
if (yaml.projects) {
|
|
5658
6928
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
5659
6929
|
if (project) {
|
|
5660
6930
|
project.last_synced_version = runningVersion;
|
|
5661
|
-
|
|
6931
|
+
fs22.writeFileSync(mcpPath, stringify2(yaml));
|
|
5662
6932
|
}
|
|
5663
6933
|
}
|
|
5664
6934
|
} catch (e) {
|
|
@@ -5694,9 +6964,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
5694
6964
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
5695
6965
|
const configFilePath = getConfigPath(workspacePath);
|
|
5696
6966
|
let currentSyncedVersion;
|
|
5697
|
-
if (
|
|
6967
|
+
if (fs22.existsSync(configFilePath)) {
|
|
5698
6968
|
try {
|
|
5699
|
-
const content =
|
|
6969
|
+
const content = fs22.readFileSync(configFilePath, "utf-8");
|
|
5700
6970
|
const config = parse(content);
|
|
5701
6971
|
currentSyncedVersion = config.last_synced_version;
|
|
5702
6972
|
} catch (e) {
|
|
@@ -5720,8 +6990,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
5720
6990
|
` \u2022 docs/ (documentation)`
|
|
5721
6991
|
];
|
|
5722
6992
|
const ideTargets = [];
|
|
5723
|
-
if (
|
|
5724
|
-
const configContent =
|
|
6993
|
+
if (fs22.existsSync(configFilePath)) {
|
|
6994
|
+
const configContent = fs22.readFileSync(configFilePath, "utf-8");
|
|
5725
6995
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
5726
6996
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
5727
6997
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -5750,14 +7020,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
5750
7020
|
const dirs = ["templates", "prompts", "docs"];
|
|
5751
7021
|
const updatedFiles = [];
|
|
5752
7022
|
for (const dir of dirs) {
|
|
5753
|
-
const srcDir =
|
|
5754
|
-
if (!
|
|
7023
|
+
const srcDir = path23.join(agentCoreDir, dir);
|
|
7024
|
+
if (!fs22.existsSync(srcDir)) continue;
|
|
5755
7025
|
const syncFiles = (src, rel) => {
|
|
5756
|
-
const entries =
|
|
7026
|
+
const entries = fs22.readdirSync(src, { withFileTypes: true });
|
|
5757
7027
|
for (const entry of entries) {
|
|
5758
|
-
const entrySrc =
|
|
5759
|
-
const entryRel =
|
|
5760
|
-
const entryDest =
|
|
7028
|
+
const entrySrc = path23.join(src, entry.name);
|
|
7029
|
+
const entryRel = path23.join(rel, entry.name);
|
|
7030
|
+
const entryDest = path23.join(dataPath, entryRel);
|
|
5761
7031
|
if (entry.isDirectory()) {
|
|
5762
7032
|
ensureDir(entryDest);
|
|
5763
7033
|
syncFiles(entrySrc, entryRel);
|
|
@@ -5765,7 +7035,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
5765
7035
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
5766
7036
|
backupFile(entryDest);
|
|
5767
7037
|
}
|
|
5768
|
-
|
|
7038
|
+
fs22.copyFileSync(entrySrc, entryDest);
|
|
5769
7039
|
updatedFiles.push(entryRel);
|
|
5770
7040
|
}
|
|
5771
7041
|
}
|
|
@@ -5776,12 +7046,12 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
5776
7046
|
DriftService.saveManifest(dataPath, manifest);
|
|
5777
7047
|
}
|
|
5778
7048
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
5779
|
-
ensureDir(
|
|
5780
|
-
ensureDir(
|
|
5781
|
-
copyDirRecursive(
|
|
5782
|
-
copyDirRecursive(
|
|
5783
|
-
if (
|
|
5784
|
-
const configContent =
|
|
7049
|
+
ensureDir(path23.join(rrceHome, "templates"));
|
|
7050
|
+
ensureDir(path23.join(rrceHome, "docs"));
|
|
7051
|
+
copyDirRecursive(path23.join(agentCoreDir, "templates"), path23.join(rrceHome, "templates"));
|
|
7052
|
+
copyDirRecursive(path23.join(agentCoreDir, "docs"), path23.join(rrceHome, "docs"));
|
|
7053
|
+
if (fs22.existsSync(configFilePath)) {
|
|
7054
|
+
const configContent = fs22.readFileSync(configFilePath, "utf-8");
|
|
5785
7055
|
if (configContent.includes("copilot: true")) {
|
|
5786
7056
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
5787
7057
|
ensureDir(copilotPath);
|
|
@@ -5803,21 +7073,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
5803
7073
|
try {
|
|
5804
7074
|
const yaml = parse(configContent);
|
|
5805
7075
|
yaml.last_synced_version = runningVersion;
|
|
5806
|
-
|
|
7076
|
+
fs22.writeFileSync(configFilePath, stringify2(yaml));
|
|
5807
7077
|
} catch (e) {
|
|
5808
7078
|
console.error("Failed to update config.yaml version:", e);
|
|
5809
7079
|
}
|
|
5810
7080
|
}
|
|
5811
|
-
const mcpPath =
|
|
5812
|
-
if (
|
|
7081
|
+
const mcpPath = path23.join(rrceHome, "mcp.yaml");
|
|
7082
|
+
if (fs22.existsSync(mcpPath)) {
|
|
5813
7083
|
try {
|
|
5814
|
-
const content =
|
|
7084
|
+
const content = fs22.readFileSync(mcpPath, "utf-8");
|
|
5815
7085
|
const yaml = parse(content);
|
|
5816
7086
|
if (yaml.projects) {
|
|
5817
7087
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
5818
7088
|
if (project) {
|
|
5819
7089
|
project.last_synced_version = runningVersion;
|
|
5820
|
-
|
|
7090
|
+
fs22.writeFileSync(mcpPath, stringify2(yaml));
|
|
5821
7091
|
}
|
|
5822
7092
|
}
|
|
5823
7093
|
} catch (e) {
|
|
@@ -5852,8 +7122,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
5852
7122
|
}
|
|
5853
7123
|
}
|
|
5854
7124
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
5855
|
-
const globalPath =
|
|
5856
|
-
const workspacePath =
|
|
7125
|
+
const globalPath = path23.join(customGlobalPath, "workspaces", workspaceName);
|
|
7126
|
+
const workspacePath = path23.join(workspaceRoot, ".rrce-workflow");
|
|
5857
7127
|
switch (mode) {
|
|
5858
7128
|
case "global":
|
|
5859
7129
|
return [globalPath];
|
|
@@ -5876,8 +7146,8 @@ var init_update_flow = __esm({
|
|
|
5876
7146
|
// src/commands/wizard/index.ts
|
|
5877
7147
|
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";
|
|
5878
7148
|
import pc13 from "picocolors";
|
|
5879
|
-
import * as
|
|
5880
|
-
import * as
|
|
7149
|
+
import * as fs26 from "fs";
|
|
7150
|
+
import * as path25 from "path";
|
|
5881
7151
|
import { parse as parse2 } from "yaml";
|
|
5882
7152
|
|
|
5883
7153
|
// src/lib/git.ts
|
|
@@ -6554,7 +7824,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
6554
7824
|
init_paths();
|
|
6555
7825
|
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";
|
|
6556
7826
|
import pc10 from "picocolors";
|
|
6557
|
-
import * as
|
|
7827
|
+
import * as fs23 from "fs";
|
|
6558
7828
|
init_detection();
|
|
6559
7829
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
6560
7830
|
const projects = scanForProjects({
|
|
@@ -6593,7 +7863,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
6593
7863
|
const s = spinner4();
|
|
6594
7864
|
s.start("Linking projects");
|
|
6595
7865
|
const configFilePath = getConfigPath(workspacePath);
|
|
6596
|
-
let configContent =
|
|
7866
|
+
let configContent = fs23.readFileSync(configFilePath, "utf-8");
|
|
6597
7867
|
if (configContent.includes("linked_projects:")) {
|
|
6598
7868
|
const lines = configContent.split("\n");
|
|
6599
7869
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -6620,7 +7890,7 @@ linked_projects:
|
|
|
6620
7890
|
`;
|
|
6621
7891
|
});
|
|
6622
7892
|
}
|
|
6623
|
-
|
|
7893
|
+
fs23.writeFileSync(configFilePath, configContent);
|
|
6624
7894
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
6625
7895
|
s.stop("Projects linked");
|
|
6626
7896
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -6656,15 +7926,15 @@ init_paths();
|
|
|
6656
7926
|
init_utils();
|
|
6657
7927
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
6658
7928
|
import pc11 from "picocolors";
|
|
6659
|
-
import * as
|
|
6660
|
-
import * as
|
|
7929
|
+
import * as fs24 from "fs";
|
|
7930
|
+
import * as path24 from "path";
|
|
6661
7931
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
6662
7932
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
6663
7933
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
6664
|
-
const globalPath =
|
|
7934
|
+
const globalPath = path24.join(customGlobalPath, "workspaces", workspaceName);
|
|
6665
7935
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
6666
7936
|
const existingDirs = subdirs.filter(
|
|
6667
|
-
(dir) =>
|
|
7937
|
+
(dir) => fs24.existsSync(path24.join(localPath, dir))
|
|
6668
7938
|
);
|
|
6669
7939
|
if (existingDirs.length === 0) {
|
|
6670
7940
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -6690,8 +7960,8 @@ Destination: ${pc11.cyan(globalPath)}`,
|
|
|
6690
7960
|
try {
|
|
6691
7961
|
ensureDir(globalPath);
|
|
6692
7962
|
for (const dir of existingDirs) {
|
|
6693
|
-
const srcDir =
|
|
6694
|
-
const destDir =
|
|
7963
|
+
const srcDir = path24.join(localPath, dir);
|
|
7964
|
+
const destDir = path24.join(globalPath, dir);
|
|
6695
7965
|
ensureDir(destDir);
|
|
6696
7966
|
copyDirRecursive(srcDir, destDir);
|
|
6697
7967
|
}
|
|
@@ -6719,7 +7989,7 @@ init_update_flow();
|
|
|
6719
7989
|
// src/commands/wizard/delete-flow.ts
|
|
6720
7990
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
6721
7991
|
import pc12 from "picocolors";
|
|
6722
|
-
import * as
|
|
7992
|
+
import * as fs25 from "fs";
|
|
6723
7993
|
init_detection();
|
|
6724
7994
|
init_config();
|
|
6725
7995
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -6763,8 +8033,8 @@ Are you sure?`,
|
|
|
6763
8033
|
for (const projectName of projectsToDelete) {
|
|
6764
8034
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
6765
8035
|
if (!project) continue;
|
|
6766
|
-
if (
|
|
6767
|
-
|
|
8036
|
+
if (fs25.existsSync(project.dataPath)) {
|
|
8037
|
+
fs25.rmSync(project.dataPath, { recursive: true, force: true });
|
|
6768
8038
|
}
|
|
6769
8039
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
6770
8040
|
configChanged = true;
|
|
@@ -6786,9 +8056,9 @@ init_config();
|
|
|
6786
8056
|
function getPackageVersion3() {
|
|
6787
8057
|
try {
|
|
6788
8058
|
const agentCoreDir = getAgentCoreDir();
|
|
6789
|
-
const packageJsonPath =
|
|
6790
|
-
if (
|
|
6791
|
-
return JSON.parse(
|
|
8059
|
+
const packageJsonPath = path25.join(path25.dirname(agentCoreDir), "package.json");
|
|
8060
|
+
if (fs26.existsSync(packageJsonPath)) {
|
|
8061
|
+
return JSON.parse(fs26.readFileSync(packageJsonPath, "utf8")).version;
|
|
6792
8062
|
}
|
|
6793
8063
|
} catch (e) {
|
|
6794
8064
|
}
|
|
@@ -6796,9 +8066,9 @@ function getPackageVersion3() {
|
|
|
6796
8066
|
}
|
|
6797
8067
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
6798
8068
|
const configFilePath = getConfigPath(workspacePath);
|
|
6799
|
-
if (
|
|
8069
|
+
if (fs26.existsSync(configFilePath)) {
|
|
6800
8070
|
try {
|
|
6801
|
-
const content =
|
|
8071
|
+
const content = fs26.readFileSync(configFilePath, "utf-8");
|
|
6802
8072
|
const config = parse2(content);
|
|
6803
8073
|
if (config.last_synced_version) {
|
|
6804
8074
|
return config.last_synced_version;
|
|
@@ -6807,10 +8077,10 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
6807
8077
|
}
|
|
6808
8078
|
}
|
|
6809
8079
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
6810
|
-
const mcpPath =
|
|
6811
|
-
if (
|
|
8080
|
+
const mcpPath = path25.join(rrceHome, "mcp.yaml");
|
|
8081
|
+
if (fs26.existsSync(mcpPath)) {
|
|
6812
8082
|
try {
|
|
6813
|
-
const content =
|
|
8083
|
+
const content = fs26.readFileSync(mcpPath, "utf-8");
|
|
6814
8084
|
const config = parse2(content);
|
|
6815
8085
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
6816
8086
|
if (project?.last_synced_version) {
|
|
@@ -6880,11 +8150,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
6880
8150
|
workspacePath
|
|
6881
8151
|
});
|
|
6882
8152
|
const configFilePath = getConfigPath(workspacePath);
|
|
6883
|
-
let isAlreadyConfigured =
|
|
8153
|
+
let isAlreadyConfigured = fs26.existsSync(configFilePath);
|
|
6884
8154
|
let currentStorageMode = null;
|
|
6885
8155
|
if (isAlreadyConfigured) {
|
|
6886
8156
|
try {
|
|
6887
|
-
const configContent =
|
|
8157
|
+
const configContent = fs26.readFileSync(configFilePath, "utf-8");
|
|
6888
8158
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
6889
8159
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
6890
8160
|
} catch {
|
|
@@ -6901,7 +8171,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
6901
8171
|
}
|
|
6902
8172
|
}
|
|
6903
8173
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
6904
|
-
const hasLocalData =
|
|
8174
|
+
const hasLocalData = fs26.existsSync(localDataPath);
|
|
6905
8175
|
if (isAlreadyConfigured) {
|
|
6906
8176
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
6907
8177
|
if (!continueToMenu) {
|