rrce-workflow 0.3.19 → 0.3.20
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 +5 -0
- package/agent-core/prompts/design.md +1 -1
- package/agent-core/prompts/develop.md +1 -1
- package/agent-core/prompts/doctor.md +1 -1
- package/agent-core/prompts/documentation.md +1 -1
- package/agent-core/prompts/init.md +1 -1
- package/agent-core/prompts/sync.md +1 -1
- package/dist/index.js +1026 -873
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1646,9 +1646,434 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
1646
1646
|
}
|
|
1647
1647
|
});
|
|
1648
1648
|
|
|
1649
|
+
// src/mcp/resources/types.ts
|
|
1650
|
+
var init_types2 = __esm({
|
|
1651
|
+
"src/mcp/resources/types.ts"() {
|
|
1652
|
+
"use strict";
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
|
|
1656
|
+
// src/mcp/resources/constants.ts
|
|
1657
|
+
var INDEXABLE_EXTENSIONS, CODE_EXTENSIONS, SKIP_DIRS;
|
|
1658
|
+
var init_constants = __esm({
|
|
1659
|
+
"src/mcp/resources/constants.ts"() {
|
|
1660
|
+
"use strict";
|
|
1661
|
+
INDEXABLE_EXTENSIONS = [
|
|
1662
|
+
".ts",
|
|
1663
|
+
".tsx",
|
|
1664
|
+
".js",
|
|
1665
|
+
".jsx",
|
|
1666
|
+
".mjs",
|
|
1667
|
+
".cjs",
|
|
1668
|
+
".py",
|
|
1669
|
+
".pyw",
|
|
1670
|
+
".go",
|
|
1671
|
+
".rs",
|
|
1672
|
+
".java",
|
|
1673
|
+
".kt",
|
|
1674
|
+
".kts",
|
|
1675
|
+
".c",
|
|
1676
|
+
".cpp",
|
|
1677
|
+
".h",
|
|
1678
|
+
".hpp",
|
|
1679
|
+
".cs",
|
|
1680
|
+
".rb",
|
|
1681
|
+
".php",
|
|
1682
|
+
".swift",
|
|
1683
|
+
".md",
|
|
1684
|
+
".mdx",
|
|
1685
|
+
".json",
|
|
1686
|
+
".yaml",
|
|
1687
|
+
".yml",
|
|
1688
|
+
".toml",
|
|
1689
|
+
".sh",
|
|
1690
|
+
".bash",
|
|
1691
|
+
".zsh",
|
|
1692
|
+
".sql",
|
|
1693
|
+
".html",
|
|
1694
|
+
".css",
|
|
1695
|
+
".scss",
|
|
1696
|
+
".sass",
|
|
1697
|
+
".less"
|
|
1698
|
+
];
|
|
1699
|
+
CODE_EXTENSIONS = [
|
|
1700
|
+
".ts",
|
|
1701
|
+
".tsx",
|
|
1702
|
+
".js",
|
|
1703
|
+
".jsx",
|
|
1704
|
+
".mjs",
|
|
1705
|
+
".cjs",
|
|
1706
|
+
".py",
|
|
1707
|
+
".pyw",
|
|
1708
|
+
".go",
|
|
1709
|
+
".rs",
|
|
1710
|
+
".java",
|
|
1711
|
+
".kt",
|
|
1712
|
+
".kts",
|
|
1713
|
+
".c",
|
|
1714
|
+
".cpp",
|
|
1715
|
+
".h",
|
|
1716
|
+
".hpp",
|
|
1717
|
+
".cs",
|
|
1718
|
+
".rb",
|
|
1719
|
+
".php",
|
|
1720
|
+
".swift",
|
|
1721
|
+
".sh",
|
|
1722
|
+
".bash",
|
|
1723
|
+
".zsh",
|
|
1724
|
+
".sql"
|
|
1725
|
+
];
|
|
1726
|
+
SKIP_DIRS = [
|
|
1727
|
+
"node_modules",
|
|
1728
|
+
".git",
|
|
1729
|
+
"dist",
|
|
1730
|
+
"build",
|
|
1731
|
+
".next",
|
|
1732
|
+
"__pycache__",
|
|
1733
|
+
"venv",
|
|
1734
|
+
".venv",
|
|
1735
|
+
"target",
|
|
1736
|
+
"vendor"
|
|
1737
|
+
];
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
// src/mcp/resources/utils.ts
|
|
1742
|
+
import * as fs13 from "fs";
|
|
1743
|
+
import * as path15 from "path";
|
|
1744
|
+
import ignore from "ignore";
|
|
1745
|
+
function estimateTokens(text2) {
|
|
1746
|
+
return Math.ceil(text2.length / 4);
|
|
1747
|
+
}
|
|
1748
|
+
function getScanContext(project, scanRoot) {
|
|
1749
|
+
const gitignorePath = path15.join(scanRoot, ".gitignore");
|
|
1750
|
+
const ig = fs13.existsSync(gitignorePath) ? ignore().add(fs13.readFileSync(gitignorePath, "utf-8")) : null;
|
|
1751
|
+
const toPosixRelativePath = (absolutePath) => {
|
|
1752
|
+
const rel = path15.relative(scanRoot, absolutePath);
|
|
1753
|
+
return rel.split(path15.sep).join("/");
|
|
1754
|
+
};
|
|
1755
|
+
const isUnderGitDir = (absolutePath) => {
|
|
1756
|
+
const rel = toPosixRelativePath(absolutePath);
|
|
1757
|
+
return rel === ".git" || rel.startsWith(".git/");
|
|
1758
|
+
};
|
|
1759
|
+
const isIgnoredByGitignore = (absolutePath, isDir) => {
|
|
1760
|
+
if (!ig) return false;
|
|
1761
|
+
const rel = toPosixRelativePath(absolutePath);
|
|
1762
|
+
return ig.ignores(isDir ? `${rel}/` : rel);
|
|
1763
|
+
};
|
|
1764
|
+
const shouldSkipEntryDir = (absolutePath) => {
|
|
1765
|
+
const dirName = path15.basename(absolutePath);
|
|
1766
|
+
if (dirName === ".git") return true;
|
|
1767
|
+
if (SKIP_DIRS.includes(dirName)) return true;
|
|
1768
|
+
if (isIgnoredByGitignore(absolutePath, true)) return true;
|
|
1769
|
+
return false;
|
|
1770
|
+
};
|
|
1771
|
+
const shouldSkipEntryFile = (absolutePath) => {
|
|
1772
|
+
if (isUnderGitDir(absolutePath)) return true;
|
|
1773
|
+
if (isIgnoredByGitignore(absolutePath, false)) return true;
|
|
1774
|
+
return false;
|
|
1775
|
+
};
|
|
1776
|
+
return { shouldSkipEntryDir, shouldSkipEntryFile };
|
|
1777
|
+
}
|
|
1778
|
+
var init_utils2 = __esm({
|
|
1779
|
+
"src/mcp/resources/utils.ts"() {
|
|
1780
|
+
"use strict";
|
|
1781
|
+
init_constants();
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
// src/mcp/resources/paths.ts
|
|
1786
|
+
import * as fs14 from "fs";
|
|
1787
|
+
function resolveProjectPaths(project, pathInput) {
|
|
1788
|
+
const config = loadMCPConfig();
|
|
1789
|
+
let workspaceRoot = pathInput;
|
|
1790
|
+
let workspaceName = project;
|
|
1791
|
+
if (!workspaceRoot && project) {
|
|
1792
|
+
const projConfig = findProjectConfig(config, { name: project });
|
|
1793
|
+
if (projConfig?.path) {
|
|
1794
|
+
workspaceRoot = projConfig.path;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
if (!workspaceName && workspaceRoot) {
|
|
1798
|
+
const projConfig = findProjectConfig(config, { path: workspaceRoot });
|
|
1799
|
+
workspaceName = projConfig?.name || getWorkspaceName(workspaceRoot);
|
|
1800
|
+
}
|
|
1801
|
+
if (!workspaceName) {
|
|
1802
|
+
workspaceName = "unknown";
|
|
1803
|
+
}
|
|
1804
|
+
let rrceData = "";
|
|
1805
|
+
let mode = "global";
|
|
1806
|
+
let configFilePath = "";
|
|
1807
|
+
if (workspaceRoot) {
|
|
1808
|
+
configFilePath = getConfigPath(workspaceRoot);
|
|
1809
|
+
const rrceHome = getEffectiveGlobalPath();
|
|
1810
|
+
if (configFilePath.startsWith(rrceHome)) {
|
|
1811
|
+
mode = "global";
|
|
1812
|
+
} else {
|
|
1813
|
+
mode = "workspace";
|
|
1814
|
+
if (fs14.existsSync(configFilePath)) {
|
|
1815
|
+
const content = fs14.readFileSync(configFilePath, "utf-8");
|
|
1816
|
+
if (content.includes("mode: global")) mode = "global";
|
|
1817
|
+
if (content.includes("mode: workspace")) mode = "workspace";
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
rrceData = resolveDataPath(mode, workspaceName, workspaceRoot);
|
|
1821
|
+
} else {
|
|
1822
|
+
rrceData = resolveDataPath("global", workspaceName, "");
|
|
1823
|
+
}
|
|
1824
|
+
return {
|
|
1825
|
+
RRCE_HOME: getRRCEHome(),
|
|
1826
|
+
RRCE_DATA: rrceData,
|
|
1827
|
+
WORKSPACE_ROOT: workspaceRoot || null,
|
|
1828
|
+
WORKSPACE_NAME: workspaceName,
|
|
1829
|
+
storage_mode: mode,
|
|
1830
|
+
config_path: configFilePath || null
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
var init_paths2 = __esm({
|
|
1834
|
+
"src/mcp/resources/paths.ts"() {
|
|
1835
|
+
"use strict";
|
|
1836
|
+
init_config();
|
|
1837
|
+
init_config_utils();
|
|
1838
|
+
init_paths();
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
// src/mcp/resources/projects.ts
|
|
1843
|
+
import * as fs15 from "fs";
|
|
1844
|
+
import * as path16 from "path";
|
|
1845
|
+
function getExposedProjects() {
|
|
1846
|
+
const config = loadMCPConfig();
|
|
1847
|
+
const knownProjects = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
1848
|
+
const allProjects = projectService.scan({ knownProjects });
|
|
1849
|
+
const activeProject = detectActiveProject(allProjects);
|
|
1850
|
+
const potentialProjects = [...allProjects];
|
|
1851
|
+
if (activeProject) {
|
|
1852
|
+
let cfgContent = null;
|
|
1853
|
+
if (fs15.existsSync(path16.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"))) {
|
|
1854
|
+
cfgContent = fs15.readFileSync(path16.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"), "utf-8");
|
|
1855
|
+
} else if (fs15.existsSync(path16.join(activeProject.dataPath, ".rrce-workflow.yaml"))) {
|
|
1856
|
+
cfgContent = fs15.readFileSync(path16.join(activeProject.dataPath, ".rrce-workflow.yaml"), "utf-8");
|
|
1857
|
+
}
|
|
1858
|
+
if (cfgContent) {
|
|
1859
|
+
if (cfgContent.includes("linked_projects:")) {
|
|
1860
|
+
const lines = cfgContent.split("\n");
|
|
1861
|
+
let inLinked = false;
|
|
1862
|
+
for (const line of lines) {
|
|
1863
|
+
const trimmed = line.trim();
|
|
1864
|
+
if (trimmed.startsWith("linked_projects:")) {
|
|
1865
|
+
inLinked = true;
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
if (inLinked) {
|
|
1869
|
+
if (trimmed.startsWith("-") || trimmed.startsWith("linked_projects")) {
|
|
1870
|
+
} else if (trimmed !== "" && !trimmed.startsWith("#")) {
|
|
1871
|
+
inLinked = false;
|
|
1872
|
+
}
|
|
1873
|
+
if (inLinked && trimmed.startsWith("-")) {
|
|
1874
|
+
const val = trimmed.replace(/^-\s*/, "").trim();
|
|
1875
|
+
const [pName] = val.split(":");
|
|
1876
|
+
if (!potentialProjects.some((p) => p.name === pName)) {
|
|
1877
|
+
const found = allProjects.find((p) => p.name === pName);
|
|
1878
|
+
if (found) {
|
|
1879
|
+
potentialProjects.push(found);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return potentialProjects.filter((project) => isProjectExposed(config, project.name, project.sourcePath || project.path));
|
|
1889
|
+
}
|
|
1890
|
+
function detectActiveProject(knownProjects) {
|
|
1891
|
+
let scanList = knownProjects;
|
|
1892
|
+
if (!scanList) {
|
|
1893
|
+
const config = loadMCPConfig();
|
|
1894
|
+
const knownProjectsMap = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
1895
|
+
const all = projectService.scan({ knownProjects: knownProjectsMap });
|
|
1896
|
+
scanList = all.filter((project) => isProjectExposed(config, project.name, project.sourcePath || project.path));
|
|
1897
|
+
}
|
|
1898
|
+
return findClosestProject(scanList);
|
|
1899
|
+
}
|
|
1900
|
+
function getProjectContext(projectName) {
|
|
1901
|
+
const config = loadMCPConfig();
|
|
1902
|
+
const projects = projectService.scan();
|
|
1903
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1904
|
+
if (!project) {
|
|
1905
|
+
return null;
|
|
1906
|
+
}
|
|
1907
|
+
const permissions = getProjectPermissions(config, projectName, project.sourcePath || project.path);
|
|
1908
|
+
if (!permissions.knowledge) {
|
|
1909
|
+
return null;
|
|
1910
|
+
}
|
|
1911
|
+
if (!project.knowledgePath) {
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1914
|
+
const contextPath = path16.join(project.knowledgePath, "project-context.md");
|
|
1915
|
+
if (!fs15.existsSync(contextPath)) {
|
|
1916
|
+
return null;
|
|
1917
|
+
}
|
|
1918
|
+
return fs15.readFileSync(contextPath, "utf-8");
|
|
1919
|
+
}
|
|
1920
|
+
function getCodeIndexPath(project) {
|
|
1921
|
+
const scanRoot = project.path || project.dataPath;
|
|
1922
|
+
return path16.join(project.knowledgePath || path16.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
|
|
1923
|
+
}
|
|
1924
|
+
var init_projects = __esm({
|
|
1925
|
+
"src/mcp/resources/projects.ts"() {
|
|
1926
|
+
"use strict";
|
|
1927
|
+
init_logger();
|
|
1928
|
+
init_config();
|
|
1929
|
+
init_detection();
|
|
1930
|
+
init_detection_service();
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
// src/mcp/resources/tasks.ts
|
|
1935
|
+
import * as fs16 from "fs";
|
|
1936
|
+
import * as path17 from "path";
|
|
1937
|
+
import * as os3 from "os";
|
|
1938
|
+
import * as crypto from "crypto";
|
|
1939
|
+
function getProjectTasks(projectName) {
|
|
1940
|
+
const config = loadMCPConfig();
|
|
1941
|
+
const projects = projectService.scan();
|
|
1942
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1943
|
+
if (!project) {
|
|
1944
|
+
return [];
|
|
1945
|
+
}
|
|
1946
|
+
const permissions = getProjectPermissions(config, projectName, project.sourcePath || project.path);
|
|
1947
|
+
if (!permissions.tasks) {
|
|
1948
|
+
return [];
|
|
1949
|
+
}
|
|
1950
|
+
if (!project.tasksPath || !fs16.existsSync(project.tasksPath)) {
|
|
1951
|
+
return [];
|
|
1952
|
+
}
|
|
1953
|
+
const tasks = [];
|
|
1954
|
+
try {
|
|
1955
|
+
const taskDirs = fs16.readdirSync(project.tasksPath, { withFileTypes: true });
|
|
1956
|
+
for (const dir of taskDirs) {
|
|
1957
|
+
if (!dir.isDirectory()) continue;
|
|
1958
|
+
const metaPath = path17.join(project.tasksPath, dir.name, "meta.json");
|
|
1959
|
+
if (fs16.existsSync(metaPath)) {
|
|
1960
|
+
try {
|
|
1961
|
+
const meta = JSON.parse(fs16.readFileSync(metaPath, "utf-8"));
|
|
1962
|
+
tasks.push(meta);
|
|
1963
|
+
} catch (err) {
|
|
1964
|
+
logger.error(`[getProjectTasks] Failed to parse meta.json in ${dir.name}`, err);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
} catch (err) {
|
|
1969
|
+
logger.error(`[getProjectTasks] Failed to read tasks directory ${project.tasksPath}`, err);
|
|
1970
|
+
}
|
|
1971
|
+
return tasks;
|
|
1972
|
+
}
|
|
1973
|
+
function getTask(projectName, taskSlug) {
|
|
1974
|
+
const config = loadMCPConfig();
|
|
1975
|
+
const projects = projectService.scan();
|
|
1976
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1977
|
+
if (!project || !project.tasksPath) return null;
|
|
1978
|
+
const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
|
|
1979
|
+
if (!fs16.existsSync(metaPath)) return null;
|
|
1980
|
+
try {
|
|
1981
|
+
return JSON.parse(fs16.readFileSync(metaPath, "utf-8"));
|
|
1982
|
+
} catch (err) {
|
|
1983
|
+
logger.error(`[getTask] Failed to parse meta.json for task ${taskSlug}`, err);
|
|
1984
|
+
return null;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
async function createTask(projectName, taskSlug, taskData) {
|
|
1988
|
+
const config = loadMCPConfig();
|
|
1989
|
+
const projects = projectService.scan();
|
|
1990
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1991
|
+
if (!project || !project.tasksPath) {
|
|
1992
|
+
throw new Error(`Project '${projectName}' not found or not configured with a tasks path.`);
|
|
1993
|
+
}
|
|
1994
|
+
const taskDir = path17.join(project.tasksPath, taskSlug);
|
|
1995
|
+
if (fs16.existsSync(taskDir)) {
|
|
1996
|
+
throw new Error(`Task with slug '${taskSlug}' already exists.`);
|
|
1997
|
+
}
|
|
1998
|
+
fs16.mkdirSync(taskDir, { recursive: true });
|
|
1999
|
+
fs16.mkdirSync(path17.join(taskDir, "research"), { recursive: true });
|
|
2000
|
+
fs16.mkdirSync(path17.join(taskDir, "planning"), { recursive: true });
|
|
2001
|
+
fs16.mkdirSync(path17.join(taskDir, "execution"), { recursive: true });
|
|
2002
|
+
fs16.mkdirSync(path17.join(taskDir, "docs"), { recursive: true });
|
|
2003
|
+
const rrceHome = process.env.RRCE_HOME || path17.join(os3.homedir(), ".rrce-workflow");
|
|
2004
|
+
const templatePath = path17.join(rrceHome, "templates", "meta.template.json");
|
|
2005
|
+
let meta = {
|
|
2006
|
+
task_id: crypto.randomUUID(),
|
|
2007
|
+
task_slug: taskSlug,
|
|
2008
|
+
status: "draft",
|
|
2009
|
+
agents: {}
|
|
2010
|
+
};
|
|
2011
|
+
if (fs16.existsSync(templatePath)) {
|
|
2012
|
+
try {
|
|
2013
|
+
const template = JSON.parse(fs16.readFileSync(templatePath, "utf-8"));
|
|
2014
|
+
meta = { ...template, ...meta };
|
|
2015
|
+
} catch (e) {
|
|
2016
|
+
logger.error("Failed to load meta template", e);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
meta.created_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2020
|
+
meta.updated_at = meta.created_at;
|
|
2021
|
+
meta.workspace = {
|
|
2022
|
+
name: project.name,
|
|
2023
|
+
path: project.path || project.dataPath,
|
|
2024
|
+
hash: project.name
|
|
2025
|
+
};
|
|
2026
|
+
Object.assign(meta, taskData);
|
|
2027
|
+
const metaPath = path17.join(taskDir, "meta.json");
|
|
2028
|
+
fs16.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
2029
|
+
return meta;
|
|
2030
|
+
}
|
|
2031
|
+
async function updateTask(projectName, taskSlug, taskData) {
|
|
2032
|
+
const meta = getTask(projectName, taskSlug);
|
|
2033
|
+
if (!meta) throw new Error(`Task '${taskSlug}' not found.`);
|
|
2034
|
+
const updatedMeta = {
|
|
2035
|
+
...meta,
|
|
2036
|
+
...taskData,
|
|
2037
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2038
|
+
// Ensure nested objects are merged if they exist in taskData
|
|
2039
|
+
agents: taskData.agents ? { ...meta.agents, ...taskData.agents } : meta.agents,
|
|
2040
|
+
workspace: meta.workspace
|
|
2041
|
+
// Protect workspace metadata
|
|
2042
|
+
};
|
|
2043
|
+
const config = loadMCPConfig();
|
|
2044
|
+
const projects = projectService.scan();
|
|
2045
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2046
|
+
if (!project || !project.tasksPath) return null;
|
|
2047
|
+
const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
|
|
2048
|
+
fs16.writeFileSync(metaPath, JSON.stringify(updatedMeta, null, 2));
|
|
2049
|
+
return updatedMeta;
|
|
2050
|
+
}
|
|
2051
|
+
function deleteTask(projectName, taskSlug) {
|
|
2052
|
+
const config = loadMCPConfig();
|
|
2053
|
+
const projects = projectService.scan();
|
|
2054
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2055
|
+
if (!project || !project.tasksPath) return false;
|
|
2056
|
+
const taskDir = path17.join(project.tasksPath, taskSlug);
|
|
2057
|
+
if (!fs16.existsSync(taskDir)) return false;
|
|
2058
|
+
if (fs16.rmSync) {
|
|
2059
|
+
fs16.rmSync(taskDir, { recursive: true, force: true });
|
|
2060
|
+
} else {
|
|
2061
|
+
fs16.rmdirSync(taskDir, { recursive: true });
|
|
2062
|
+
}
|
|
2063
|
+
return true;
|
|
2064
|
+
}
|
|
2065
|
+
var init_tasks = __esm({
|
|
2066
|
+
"src/mcp/resources/tasks.ts"() {
|
|
2067
|
+
"use strict";
|
|
2068
|
+
init_logger();
|
|
2069
|
+
init_config();
|
|
2070
|
+
init_detection_service();
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
|
|
1649
2074
|
// src/mcp/services/rag.ts
|
|
1650
|
-
import * as
|
|
1651
|
-
import * as
|
|
2075
|
+
import * as fs17 from "fs";
|
|
2076
|
+
import * as path18 from "path";
|
|
1652
2077
|
var INDEX_VERSION, DEFAULT_MODEL, RAGService;
|
|
1653
2078
|
var init_rag = __esm({
|
|
1654
2079
|
"src/mcp/services/rag.ts"() {
|
|
@@ -1702,9 +2127,9 @@ var init_rag = __esm({
|
|
|
1702
2127
|
*/
|
|
1703
2128
|
loadIndex() {
|
|
1704
2129
|
if (this.index) return;
|
|
1705
|
-
if (
|
|
2130
|
+
if (fs17.existsSync(this.indexPath)) {
|
|
1706
2131
|
try {
|
|
1707
|
-
const data =
|
|
2132
|
+
const data = fs17.readFileSync(this.indexPath, "utf-8");
|
|
1708
2133
|
this.index = JSON.parse(data);
|
|
1709
2134
|
logger.info(`[RAG] Loaded index from ${this.indexPath} with ${this.index?.chunks.length} chunks.`);
|
|
1710
2135
|
} catch (error) {
|
|
@@ -1730,11 +2155,11 @@ var init_rag = __esm({
|
|
|
1730
2155
|
saveIndex() {
|
|
1731
2156
|
if (!this.index) return;
|
|
1732
2157
|
try {
|
|
1733
|
-
const dir =
|
|
1734
|
-
if (!
|
|
1735
|
-
|
|
2158
|
+
const dir = path18.dirname(this.indexPath);
|
|
2159
|
+
if (!fs17.existsSync(dir)) {
|
|
2160
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
1736
2161
|
}
|
|
1737
|
-
|
|
2162
|
+
fs17.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2));
|
|
1738
2163
|
logger.info(`[RAG] Saved index to ${this.indexPath} with ${this.index.chunks.length} chunks.`);
|
|
1739
2164
|
} catch (error) {
|
|
1740
2165
|
logger.error(`[RAG] Failed to save index to ${this.indexPath}`, error);
|
|
@@ -1745,13 +2170,13 @@ var init_rag = __esm({
|
|
|
1745
2170
|
*/
|
|
1746
2171
|
saveIndexAtomic() {
|
|
1747
2172
|
if (!this.index) return;
|
|
1748
|
-
const dir =
|
|
1749
|
-
if (!
|
|
1750
|
-
|
|
2173
|
+
const dir = path18.dirname(this.indexPath);
|
|
2174
|
+
if (!fs17.existsSync(dir)) {
|
|
2175
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
1751
2176
|
}
|
|
1752
2177
|
const tmpPath = `${this.indexPath}.tmp`;
|
|
1753
|
-
|
|
1754
|
-
|
|
2178
|
+
fs17.writeFileSync(tmpPath, JSON.stringify(this.index, null, 2));
|
|
2179
|
+
fs17.renameSync(tmpPath, this.indexPath);
|
|
1755
2180
|
}
|
|
1756
2181
|
/**
|
|
1757
2182
|
* Save index only if enough time passed since last save
|
|
@@ -2284,10 +2709,10 @@ var init_context_extractor = __esm({
|
|
|
2284
2709
|
});
|
|
2285
2710
|
|
|
2286
2711
|
// src/mcp/services/dependency-graph.ts
|
|
2287
|
-
import * as
|
|
2288
|
-
import * as
|
|
2712
|
+
import * as fs18 from "fs";
|
|
2713
|
+
import * as path19 from "path";
|
|
2289
2714
|
function parseImports(filePath, content) {
|
|
2290
|
-
const ext =
|
|
2715
|
+
const ext = path19.extname(filePath).toLowerCase();
|
|
2291
2716
|
const language = getLanguageFromExtension(ext);
|
|
2292
2717
|
const edges = [];
|
|
2293
2718
|
const patterns = IMPORT_PATTERNS[language] || IMPORT_PATTERNS.javascript || [];
|
|
@@ -2317,20 +2742,20 @@ function parseImports(filePath, content) {
|
|
|
2317
2742
|
return edges;
|
|
2318
2743
|
}
|
|
2319
2744
|
function resolveImport(fromFile, importPath, language) {
|
|
2320
|
-
const fromDir =
|
|
2745
|
+
const fromDir = path19.dirname(fromFile);
|
|
2321
2746
|
if (importPath.startsWith(".")) {
|
|
2322
|
-
const candidates = generateCandidates(
|
|
2747
|
+
const candidates = generateCandidates(path19.resolve(fromDir, importPath), language);
|
|
2323
2748
|
for (const candidate of candidates) {
|
|
2324
|
-
if (
|
|
2749
|
+
if (fs18.existsSync(candidate)) {
|
|
2325
2750
|
return { path: candidate, isResolved: true };
|
|
2326
2751
|
}
|
|
2327
2752
|
}
|
|
2328
|
-
return { path:
|
|
2753
|
+
return { path: path19.resolve(fromDir, importPath), isResolved: false };
|
|
2329
2754
|
}
|
|
2330
2755
|
if (importPath.startsWith("/")) {
|
|
2331
2756
|
const candidates = generateCandidates(importPath, language);
|
|
2332
2757
|
for (const candidate of candidates) {
|
|
2333
|
-
if (
|
|
2758
|
+
if (fs18.existsSync(candidate)) {
|
|
2334
2759
|
return { path: candidate, isResolved: true };
|
|
2335
2760
|
}
|
|
2336
2761
|
}
|
|
@@ -2340,7 +2765,7 @@ function resolveImport(fromFile, importPath, language) {
|
|
|
2340
2765
|
}
|
|
2341
2766
|
function generateCandidates(basePath, language) {
|
|
2342
2767
|
const candidates = [];
|
|
2343
|
-
if (
|
|
2768
|
+
if (path19.extname(basePath)) {
|
|
2344
2769
|
candidates.push(basePath);
|
|
2345
2770
|
}
|
|
2346
2771
|
switch (language) {
|
|
@@ -2455,18 +2880,18 @@ async function scanProjectDependencies(projectRoot, options = {}) {
|
|
|
2455
2880
|
const files = [];
|
|
2456
2881
|
function scanDir(dir) {
|
|
2457
2882
|
try {
|
|
2458
|
-
const entries =
|
|
2883
|
+
const entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
2459
2884
|
for (const entry of entries) {
|
|
2460
|
-
const fullPath =
|
|
2885
|
+
const fullPath = path19.join(dir, entry.name);
|
|
2461
2886
|
if (entry.isDirectory()) {
|
|
2462
2887
|
if (!skipDirs.includes(entry.name) && !entry.name.startsWith(".")) {
|
|
2463
2888
|
scanDir(fullPath);
|
|
2464
2889
|
}
|
|
2465
2890
|
} else if (entry.isFile()) {
|
|
2466
|
-
const ext =
|
|
2891
|
+
const ext = path19.extname(entry.name).toLowerCase();
|
|
2467
2892
|
if (extensions.includes(ext)) {
|
|
2468
2893
|
try {
|
|
2469
|
-
const content =
|
|
2894
|
+
const content = fs18.readFileSync(fullPath, "utf-8");
|
|
2470
2895
|
files.push({ path: fullPath, content });
|
|
2471
2896
|
} catch {
|
|
2472
2897
|
}
|
|
@@ -2715,222 +3140,57 @@ function getLanguageFromPath(filePath) {
|
|
|
2715
3140
|
const langMap = {
|
|
2716
3141
|
"ts": "typescript",
|
|
2717
3142
|
"tsx": "typescript",
|
|
2718
|
-
"js": "javascript",
|
|
2719
|
-
"jsx": "javascript",
|
|
2720
|
-
"mjs": "javascript",
|
|
2721
|
-
"cjs": "javascript",
|
|
2722
|
-
"py": "python",
|
|
2723
|
-
"go": "go",
|
|
2724
|
-
"rs": "rust",
|
|
2725
|
-
"java": "java",
|
|
2726
|
-
"kt": "kotlin",
|
|
2727
|
-
"rb": "ruby",
|
|
2728
|
-
"php": "php",
|
|
2729
|
-
"swift": "swift",
|
|
2730
|
-
"c": "c",
|
|
2731
|
-
"cpp": "cpp",
|
|
2732
|
-
"h": "c",
|
|
2733
|
-
"hpp": "cpp",
|
|
2734
|
-
"cs": "csharp"
|
|
2735
|
-
};
|
|
2736
|
-
return langMap[ext] ?? "unknown";
|
|
2737
|
-
}
|
|
2738
|
-
function searchSymbols(symbolResults, query, options = {}) {
|
|
2739
|
-
const { type = "any", fuzzy = true, limit = 10, minScore = 0.3 } = options;
|
|
2740
|
-
const matches = [];
|
|
2741
|
-
for (const result of symbolResults) {
|
|
2742
|
-
for (const symbol of result.symbols) {
|
|
2743
|
-
if (type !== "any" && symbol.type !== type) continue;
|
|
2744
|
-
const score = fuzzy ? fuzzyMatchScore(query, symbol.name) : symbol.name.toLowerCase().includes(query.toLowerCase()) ? 1 : 0;
|
|
2745
|
-
if (score >= minScore) {
|
|
2746
|
-
matches.push({
|
|
2747
|
-
...symbol,
|
|
2748
|
-
file: result.filePath,
|
|
2749
|
-
score
|
|
2750
|
-
});
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
matches.sort((a, b) => {
|
|
2755
|
-
if (b.score !== a.score) return b.score - a.score;
|
|
2756
|
-
return a.name.length - b.name.length;
|
|
2757
|
-
});
|
|
2758
|
-
return matches.slice(0, limit);
|
|
2759
|
-
}
|
|
2760
|
-
var init_symbol_extractor = __esm({
|
|
2761
|
-
"src/mcp/services/symbol-extractor.ts"() {
|
|
2762
|
-
"use strict";
|
|
2763
|
-
}
|
|
2764
|
-
});
|
|
2765
|
-
|
|
2766
|
-
// src/mcp/resources.ts
|
|
2767
|
-
import * as fs15 from "fs";
|
|
2768
|
-
import * as path17 from "path";
|
|
2769
|
-
import * as os3 from "os";
|
|
2770
|
-
import * as crypto from "crypto";
|
|
2771
|
-
import ignore from "ignore";
|
|
2772
|
-
function resolveProjectPaths(project, pathInput) {
|
|
2773
|
-
const config = loadMCPConfig();
|
|
2774
|
-
let workspaceRoot = pathInput;
|
|
2775
|
-
let workspaceName = project;
|
|
2776
|
-
if (!workspaceRoot && project) {
|
|
2777
|
-
const projConfig = findProjectConfig(config, { name: project });
|
|
2778
|
-
if (projConfig?.path) {
|
|
2779
|
-
workspaceRoot = projConfig.path;
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2782
|
-
if (!workspaceName && workspaceRoot) {
|
|
2783
|
-
const projConfig = findProjectConfig(config, { path: workspaceRoot });
|
|
2784
|
-
workspaceName = projConfig?.name || getWorkspaceName(workspaceRoot);
|
|
2785
|
-
}
|
|
2786
|
-
if (!workspaceName) {
|
|
2787
|
-
workspaceName = "unknown";
|
|
2788
|
-
}
|
|
2789
|
-
let rrceData = "";
|
|
2790
|
-
let mode = "global";
|
|
2791
|
-
let configFilePath = "";
|
|
2792
|
-
if (workspaceRoot) {
|
|
2793
|
-
configFilePath = getConfigPath(workspaceRoot);
|
|
2794
|
-
const rrceHome = getEffectiveGlobalPath();
|
|
2795
|
-
if (configFilePath.startsWith(rrceHome)) {
|
|
2796
|
-
mode = "global";
|
|
2797
|
-
} else {
|
|
2798
|
-
mode = "workspace";
|
|
2799
|
-
if (fs15.existsSync(configFilePath)) {
|
|
2800
|
-
const content = fs15.readFileSync(configFilePath, "utf-8");
|
|
2801
|
-
if (content.includes("mode: global")) mode = "global";
|
|
2802
|
-
if (content.includes("mode: workspace")) mode = "workspace";
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
rrceData = resolveDataPath(mode, workspaceName, workspaceRoot);
|
|
2806
|
-
} else {
|
|
2807
|
-
rrceData = resolveDataPath("global", workspaceName, "");
|
|
2808
|
-
}
|
|
2809
|
-
return {
|
|
2810
|
-
RRCE_HOME: getRRCEHome(),
|
|
2811
|
-
RRCE_DATA: rrceData,
|
|
2812
|
-
WORKSPACE_ROOT: workspaceRoot || null,
|
|
2813
|
-
WORKSPACE_NAME: workspaceName,
|
|
2814
|
-
storage_mode: mode,
|
|
2815
|
-
config_path: configFilePath || null
|
|
2816
|
-
};
|
|
2817
|
-
}
|
|
2818
|
-
function getExposedProjects() {
|
|
2819
|
-
const config = loadMCPConfig();
|
|
2820
|
-
const knownProjects = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
2821
|
-
const allProjects = projectService.scan({ knownProjects });
|
|
2822
|
-
const activeProject = detectActiveProject(allProjects);
|
|
2823
|
-
const potentialProjects = [...allProjects];
|
|
2824
|
-
if (activeProject) {
|
|
2825
|
-
let cfgContent = null;
|
|
2826
|
-
if (fs15.existsSync(path17.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"))) {
|
|
2827
|
-
cfgContent = fs15.readFileSync(path17.join(activeProject.dataPath, ".rrce-workflow", "config.yaml"), "utf-8");
|
|
2828
|
-
} else if (fs15.existsSync(path17.join(activeProject.dataPath, ".rrce-workflow.yaml"))) {
|
|
2829
|
-
cfgContent = fs15.readFileSync(path17.join(activeProject.dataPath, ".rrce-workflow.yaml"), "utf-8");
|
|
2830
|
-
}
|
|
2831
|
-
if (cfgContent) {
|
|
2832
|
-
if (cfgContent.includes("linked_projects:")) {
|
|
2833
|
-
const lines = cfgContent.split("\n");
|
|
2834
|
-
let inLinked = false;
|
|
2835
|
-
for (const line of lines) {
|
|
2836
|
-
const trimmed = line.trim();
|
|
2837
|
-
if (trimmed.startsWith("linked_projects:")) {
|
|
2838
|
-
inLinked = true;
|
|
2839
|
-
continue;
|
|
2840
|
-
}
|
|
2841
|
-
if (inLinked) {
|
|
2842
|
-
if (trimmed.startsWith("-") || trimmed.startsWith("linked_projects")) {
|
|
2843
|
-
} else if (trimmed !== "" && !trimmed.startsWith("#")) {
|
|
2844
|
-
inLinked = false;
|
|
2845
|
-
}
|
|
2846
|
-
if (inLinked && trimmed.startsWith("-")) {
|
|
2847
|
-
const val = trimmed.replace(/^-\s*/, "").trim();
|
|
2848
|
-
const [pName] = val.split(":");
|
|
2849
|
-
if (!potentialProjects.some((p) => p.name === pName)) {
|
|
2850
|
-
const found = allProjects.find((p) => p.name === pName);
|
|
2851
|
-
if (found) {
|
|
2852
|
-
potentialProjects.push(found);
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
return potentialProjects.filter((project) => isProjectExposed(config, project.name, project.sourcePath || project.path));
|
|
2862
|
-
}
|
|
2863
|
-
function getCodeIndexPath(project) {
|
|
2864
|
-
const scanRoot = project.path || project.dataPath;
|
|
2865
|
-
return path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
|
|
2866
|
-
}
|
|
2867
|
-
function detectActiveProject(knownProjects) {
|
|
2868
|
-
let scanList = knownProjects;
|
|
2869
|
-
if (!scanList) {
|
|
2870
|
-
const config = loadMCPConfig();
|
|
2871
|
-
const knownProjectsMap = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
2872
|
-
const all = projectService.scan({ knownProjects: knownProjectsMap });
|
|
2873
|
-
scanList = all.filter((project) => isProjectExposed(config, project.name, project.sourcePath || project.path));
|
|
2874
|
-
}
|
|
2875
|
-
return findClosestProject(scanList);
|
|
2876
|
-
}
|
|
2877
|
-
function getProjectContext(projectName) {
|
|
2878
|
-
const config = loadMCPConfig();
|
|
2879
|
-
const projects = projectService.scan();
|
|
2880
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2881
|
-
if (!project) {
|
|
2882
|
-
return null;
|
|
2883
|
-
}
|
|
2884
|
-
const permissions = getProjectPermissions(config, projectName, project.sourcePath || project.path);
|
|
2885
|
-
if (!permissions.knowledge) {
|
|
2886
|
-
return null;
|
|
2887
|
-
}
|
|
2888
|
-
if (!project.knowledgePath) {
|
|
2889
|
-
return null;
|
|
2890
|
-
}
|
|
2891
|
-
const contextPath = path17.join(project.knowledgePath, "project-context.md");
|
|
2892
|
-
if (!fs15.existsSync(contextPath)) {
|
|
2893
|
-
return null;
|
|
2894
|
-
}
|
|
2895
|
-
return fs15.readFileSync(contextPath, "utf-8");
|
|
2896
|
-
}
|
|
2897
|
-
function getProjectTasks(projectName) {
|
|
2898
|
-
const config = loadMCPConfig();
|
|
2899
|
-
const projects = projectService.scan();
|
|
2900
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2901
|
-
if (!project) {
|
|
2902
|
-
return [];
|
|
2903
|
-
}
|
|
2904
|
-
const permissions = getProjectPermissions(config, projectName, project.sourcePath || project.path);
|
|
2905
|
-
if (!permissions.tasks) {
|
|
2906
|
-
return [];
|
|
2907
|
-
}
|
|
2908
|
-
if (!project.tasksPath || !fs15.existsSync(project.tasksPath)) {
|
|
2909
|
-
return [];
|
|
2910
|
-
}
|
|
2911
|
-
const tasks = [];
|
|
2912
|
-
try {
|
|
2913
|
-
const taskDirs = fs15.readdirSync(project.tasksPath, { withFileTypes: true });
|
|
2914
|
-
for (const dir of taskDirs) {
|
|
2915
|
-
if (!dir.isDirectory()) continue;
|
|
2916
|
-
const metaPath = path17.join(project.tasksPath, dir.name, "meta.json");
|
|
2917
|
-
if (fs15.existsSync(metaPath)) {
|
|
2918
|
-
try {
|
|
2919
|
-
const meta = JSON.parse(fs15.readFileSync(metaPath, "utf-8"));
|
|
2920
|
-
tasks.push(meta);
|
|
2921
|
-
} catch (err) {
|
|
2922
|
-
logger.error(`[getProjectTasks] Failed to parse meta.json in ${dir.name}`, err);
|
|
2923
|
-
}
|
|
3143
|
+
"js": "javascript",
|
|
3144
|
+
"jsx": "javascript",
|
|
3145
|
+
"mjs": "javascript",
|
|
3146
|
+
"cjs": "javascript",
|
|
3147
|
+
"py": "python",
|
|
3148
|
+
"go": "go",
|
|
3149
|
+
"rs": "rust",
|
|
3150
|
+
"java": "java",
|
|
3151
|
+
"kt": "kotlin",
|
|
3152
|
+
"rb": "ruby",
|
|
3153
|
+
"php": "php",
|
|
3154
|
+
"swift": "swift",
|
|
3155
|
+
"c": "c",
|
|
3156
|
+
"cpp": "cpp",
|
|
3157
|
+
"h": "c",
|
|
3158
|
+
"hpp": "cpp",
|
|
3159
|
+
"cs": "csharp"
|
|
3160
|
+
};
|
|
3161
|
+
return langMap[ext] ?? "unknown";
|
|
3162
|
+
}
|
|
3163
|
+
function searchSymbols(symbolResults, query, options = {}) {
|
|
3164
|
+
const { type = "any", fuzzy = true, limit = 10, minScore = 0.3 } = options;
|
|
3165
|
+
const matches = [];
|
|
3166
|
+
for (const result of symbolResults) {
|
|
3167
|
+
for (const symbol of result.symbols) {
|
|
3168
|
+
if (type !== "any" && symbol.type !== type) continue;
|
|
3169
|
+
const score = fuzzy ? fuzzyMatchScore(query, symbol.name) : symbol.name.toLowerCase().includes(query.toLowerCase()) ? 1 : 0;
|
|
3170
|
+
if (score >= minScore) {
|
|
3171
|
+
matches.push({
|
|
3172
|
+
...symbol,
|
|
3173
|
+
file: result.filePath,
|
|
3174
|
+
score
|
|
3175
|
+
});
|
|
2924
3176
|
}
|
|
2925
3177
|
}
|
|
2926
|
-
} catch (err) {
|
|
2927
|
-
logger.error(`[getProjectTasks] Failed to read tasks directory ${project.tasksPath}`, err);
|
|
2928
3178
|
}
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3179
|
+
matches.sort((a, b) => {
|
|
3180
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
3181
|
+
return a.name.length - b.name.length;
|
|
3182
|
+
});
|
|
3183
|
+
return matches.slice(0, limit);
|
|
2933
3184
|
}
|
|
3185
|
+
var init_symbol_extractor = __esm({
|
|
3186
|
+
"src/mcp/services/symbol-extractor.ts"() {
|
|
3187
|
+
"use strict";
|
|
3188
|
+
}
|
|
3189
|
+
});
|
|
3190
|
+
|
|
3191
|
+
// src/mcp/resources/search.ts
|
|
3192
|
+
import * as fs19 from "fs";
|
|
3193
|
+
import * as path20 from "path";
|
|
2934
3194
|
async function searchCode(query, projectFilter, limit = 10, options) {
|
|
2935
3195
|
const config = loadMCPConfig();
|
|
2936
3196
|
const projects = getExposedProjects();
|
|
@@ -2949,7 +3209,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
2949
3209
|
}
|
|
2950
3210
|
try {
|
|
2951
3211
|
const codeIndexPath = getCodeIndexPath(project);
|
|
2952
|
-
if (!
|
|
3212
|
+
if (!fs19.existsSync(codeIndexPath)) {
|
|
2953
3213
|
logger.debug(`[searchCode] Code index not found for project '${project.name}'`);
|
|
2954
3214
|
continue;
|
|
2955
3215
|
}
|
|
@@ -2959,7 +3219,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
2959
3219
|
const codeChunk = r;
|
|
2960
3220
|
results.push({
|
|
2961
3221
|
project: project.name,
|
|
2962
|
-
file:
|
|
3222
|
+
file: path20.relative(project.sourcePath || project.path || "", codeChunk.filePath),
|
|
2963
3223
|
snippet: codeChunk.content,
|
|
2964
3224
|
lineStart: codeChunk.lineStart ?? 1,
|
|
2965
3225
|
lineEnd: codeChunk.lineEnd ?? 1,
|
|
@@ -3040,15 +3300,14 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3040
3300
|
if (useRAG) {
|
|
3041
3301
|
logger.info(`[RAG] Using semantic search for project '${project.name}'`);
|
|
3042
3302
|
try {
|
|
3043
|
-
const indexPath =
|
|
3303
|
+
const indexPath = path20.join(project.knowledgePath, "embeddings.json");
|
|
3044
3304
|
const rag = new RAGService(indexPath, projConfig?.semanticSearch?.model);
|
|
3045
3305
|
const ragResults = await rag.search(query, 5);
|
|
3046
3306
|
for (const r of ragResults) {
|
|
3047
3307
|
results.push({
|
|
3048
3308
|
project: project.name,
|
|
3049
|
-
file:
|
|
3309
|
+
file: path20.relative(project.knowledgePath, r.filePath),
|
|
3050
3310
|
matches: [r.content],
|
|
3051
|
-
// The chunk content is the match
|
|
3052
3311
|
score: r.score,
|
|
3053
3312
|
indexingInProgress: indexingInProgress2 || void 0,
|
|
3054
3313
|
advisoryMessage: advisoryMessage2
|
|
@@ -3060,11 +3319,11 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3060
3319
|
}
|
|
3061
3320
|
}
|
|
3062
3321
|
try {
|
|
3063
|
-
const files =
|
|
3322
|
+
const files = fs19.readdirSync(project.knowledgePath);
|
|
3064
3323
|
for (const file of files) {
|
|
3065
3324
|
if (!file.endsWith(".md")) continue;
|
|
3066
|
-
const filePath =
|
|
3067
|
-
const content =
|
|
3325
|
+
const filePath = path20.join(project.knowledgePath, file);
|
|
3326
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
3068
3327
|
const lines = content.split("\n");
|
|
3069
3328
|
const matches = [];
|
|
3070
3329
|
for (const line of lines) {
|
|
@@ -3072,373 +3331,69 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3072
3331
|
matches.push(line.trim());
|
|
3073
3332
|
}
|
|
3074
3333
|
}
|
|
3075
|
-
if (matches.length > 0) {
|
|
3076
|
-
results.push({
|
|
3077
|
-
project: project.name,
|
|
3078
|
-
file,
|
|
3079
|
-
matches: matches.slice(0, 5),
|
|
3080
|
-
// Limit to 5 matches per file
|
|
3081
|
-
indexingInProgress: indexingInProgress2 || void 0,
|
|
3082
|
-
advisoryMessage: advisoryMessage2
|
|
3083
|
-
});
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
} catch (err) {
|
|
3087
|
-
logger.error(`[searchKnowledge] Failed to read knowledge directory ${project.knowledgePath}`, err);
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
let filteredResults = results;
|
|
3091
|
-
if (options?.min_score !== void 0 && options.min_score > 0) {
|
|
3092
|
-
filteredResults = results.filter((r) => (r.score ?? 1) >= options.min_score);
|
|
3093
|
-
}
|
|
3094
|
-
filteredResults.sort((a, b) => (b.score ?? 1) - (a.score ?? 1));
|
|
3095
|
-
let truncated = false;
|
|
3096
|
-
let tokenCount = 0;
|
|
3097
|
-
let budgetedResults = filteredResults;
|
|
3098
|
-
if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
|
|
3099
|
-
budgetedResults = [];
|
|
3100
|
-
for (const result of filteredResults) {
|
|
3101
|
-
const resultTokens = estimateTokens(result.matches.join("\n"));
|
|
3102
|
-
if (tokenCount + resultTokens > options.max_tokens) {
|
|
3103
|
-
truncated = true;
|
|
3104
|
-
break;
|
|
3105
|
-
}
|
|
3106
|
-
budgetedResults.push(result);
|
|
3107
|
-
tokenCount += resultTokens;
|
|
3108
|
-
}
|
|
3109
|
-
} else {
|
|
3110
|
-
tokenCount = filteredResults.reduce((sum, r) => sum + estimateTokens(r.matches.join("\n")), 0);
|
|
3111
|
-
}
|
|
3112
|
-
let indexAgeSeconds;
|
|
3113
|
-
let lastIndexedAt;
|
|
3114
|
-
let indexingInProgress;
|
|
3115
|
-
let advisoryMessage;
|
|
3116
|
-
if (projectFilter) {
|
|
3117
|
-
const project = projects.find((p) => p.name === projectFilter);
|
|
3118
|
-
if (project) {
|
|
3119
|
-
indexingInProgress = indexingJobs.isRunning(project.name);
|
|
3120
|
-
advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
3121
|
-
const progress = indexingJobs.getProgress(project.name);
|
|
3122
|
-
if (progress.completedAt) {
|
|
3123
|
-
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3124
|
-
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
const cleanResults = budgetedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3129
|
-
return {
|
|
3130
|
-
results: cleanResults,
|
|
3131
|
-
token_count: tokenCount,
|
|
3132
|
-
truncated,
|
|
3133
|
-
index_age_seconds: indexAgeSeconds,
|
|
3134
|
-
last_indexed_at: lastIndexedAt,
|
|
3135
|
-
indexingInProgress,
|
|
3136
|
-
advisoryMessage
|
|
3137
|
-
};
|
|
3138
|
-
}
|
|
3139
|
-
function getScanContext(project, scanRoot) {
|
|
3140
|
-
const gitignorePath = path17.join(scanRoot, ".gitignore");
|
|
3141
|
-
const ig = fs15.existsSync(gitignorePath) ? ignore().add(fs15.readFileSync(gitignorePath, "utf-8")) : null;
|
|
3142
|
-
const toPosixRelativePath = (absolutePath) => {
|
|
3143
|
-
const rel = path17.relative(scanRoot, absolutePath);
|
|
3144
|
-
return rel.split(path17.sep).join("/");
|
|
3145
|
-
};
|
|
3146
|
-
const isUnderGitDir = (absolutePath) => {
|
|
3147
|
-
const rel = toPosixRelativePath(absolutePath);
|
|
3148
|
-
return rel === ".git" || rel.startsWith(".git/");
|
|
3149
|
-
};
|
|
3150
|
-
const isIgnoredByGitignore = (absolutePath, isDir) => {
|
|
3151
|
-
if (!ig) return false;
|
|
3152
|
-
const rel = toPosixRelativePath(absolutePath);
|
|
3153
|
-
return ig.ignores(isDir ? `${rel}/` : rel);
|
|
3154
|
-
};
|
|
3155
|
-
const shouldSkipEntryDir = (absolutePath) => {
|
|
3156
|
-
const dirName = path17.basename(absolutePath);
|
|
3157
|
-
if (dirName === ".git") return true;
|
|
3158
|
-
if (SKIP_DIRS.includes(dirName)) return true;
|
|
3159
|
-
if (isIgnoredByGitignore(absolutePath, true)) return true;
|
|
3160
|
-
return false;
|
|
3161
|
-
};
|
|
3162
|
-
const shouldSkipEntryFile = (absolutePath) => {
|
|
3163
|
-
if (isUnderGitDir(absolutePath)) return true;
|
|
3164
|
-
if (isIgnoredByGitignore(absolutePath, false)) return true;
|
|
3165
|
-
return false;
|
|
3166
|
-
};
|
|
3167
|
-
return { shouldSkipEntryDir, shouldSkipEntryFile };
|
|
3168
|
-
}
|
|
3169
|
-
async function indexKnowledge(projectName, force = false) {
|
|
3170
|
-
const config = loadMCPConfig();
|
|
3171
|
-
const projects = getExposedProjects();
|
|
3172
|
-
const project = projects.find((p2) => p2.name === projectName || p2.path && p2.path === projectName);
|
|
3173
|
-
if (!project) {
|
|
3174
|
-
return {
|
|
3175
|
-
state: "failed",
|
|
3176
|
-
status: "failed",
|
|
3177
|
-
success: false,
|
|
3178
|
-
message: `Project '${projectName}' not found`,
|
|
3179
|
-
filesIndexed: 0,
|
|
3180
|
-
filesSkipped: 0,
|
|
3181
|
-
progress: { itemsDone: 0 }
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path }) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
|
|
3185
|
-
const isEnabled = projConfig?.semanticSearch?.enabled || project.semanticSearchEnabled;
|
|
3186
|
-
if (!isEnabled) {
|
|
3187
|
-
return {
|
|
3188
|
-
state: "failed",
|
|
3189
|
-
status: "failed",
|
|
3190
|
-
success: false,
|
|
3191
|
-
message: "Semantic Search is not enabled for this project",
|
|
3192
|
-
filesIndexed: 0,
|
|
3193
|
-
filesSkipped: 0,
|
|
3194
|
-
progress: { itemsDone: 0 }
|
|
3195
|
-
};
|
|
3196
|
-
}
|
|
3197
|
-
const scanRoot = project.sourcePath || project.path || project.dataPath;
|
|
3198
|
-
if (!fs15.existsSync(scanRoot)) {
|
|
3199
|
-
return {
|
|
3200
|
-
state: "failed",
|
|
3201
|
-
status: "failed",
|
|
3202
|
-
success: false,
|
|
3203
|
-
message: "Project root not found",
|
|
3204
|
-
filesIndexed: 0,
|
|
3205
|
-
filesSkipped: 0,
|
|
3206
|
-
progress: { itemsDone: 0 }
|
|
3207
|
-
};
|
|
3208
|
-
}
|
|
3209
|
-
const runIndexing = async () => {
|
|
3210
|
-
const { shouldSkipEntryDir, shouldSkipEntryFile } = getScanContext(project, scanRoot);
|
|
3211
|
-
const indexPath = path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "embeddings.json");
|
|
3212
|
-
const codeIndexPath = path17.join(project.knowledgePath || path17.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
|
|
3213
|
-
const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
|
|
3214
|
-
const rag = new RAGService(indexPath, model);
|
|
3215
|
-
const codeRag = new RAGService(codeIndexPath, model);
|
|
3216
|
-
let indexed = 0;
|
|
3217
|
-
let codeIndexed = 0;
|
|
3218
|
-
let skipped = 0;
|
|
3219
|
-
let itemsTotal = 0;
|
|
3220
|
-
let itemsDone = 0;
|
|
3221
|
-
const preCount = (dir) => {
|
|
3222
|
-
const entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
3223
|
-
for (const entry of entries) {
|
|
3224
|
-
const fullPath = path17.join(dir, entry.name);
|
|
3225
|
-
if (entry.isDirectory()) {
|
|
3226
|
-
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3227
|
-
preCount(fullPath);
|
|
3228
|
-
} else if (entry.isFile()) {
|
|
3229
|
-
const ext = path17.extname(entry.name).toLowerCase();
|
|
3230
|
-
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3231
|
-
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3232
|
-
itemsTotal++;
|
|
3233
|
-
}
|
|
3234
|
-
}
|
|
3235
|
-
};
|
|
3236
|
-
preCount(scanRoot);
|
|
3237
|
-
indexingJobs.update(project.name, { itemsTotal });
|
|
3238
|
-
const cleanupIgnoredFiles = async () => {
|
|
3239
|
-
const indexedFiles = [...rag.getIndexedFiles(), ...codeRag.getIndexedFiles()];
|
|
3240
|
-
const unique = Array.from(new Set(indexedFiles));
|
|
3241
|
-
for (const filePath of unique) {
|
|
3242
|
-
if (!path17.isAbsolute(filePath)) continue;
|
|
3243
|
-
const relFilePath = filePath.split(path17.sep).join("/");
|
|
3244
|
-
const relScanRoot = scanRoot.split(path17.sep).join("/");
|
|
3245
|
-
const isInScanRoot = relFilePath === relScanRoot || relFilePath.startsWith(`${relScanRoot}/`);
|
|
3246
|
-
if (!isInScanRoot) continue;
|
|
3247
|
-
if (shouldSkipEntryFile(filePath)) {
|
|
3248
|
-
await rag.removeFile(filePath);
|
|
3249
|
-
await codeRag.removeFile(filePath);
|
|
3250
|
-
}
|
|
3251
|
-
}
|
|
3252
|
-
};
|
|
3253
|
-
await cleanupIgnoredFiles();
|
|
3254
|
-
const scanDir = async (dir) => {
|
|
3255
|
-
const entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
3256
|
-
for (const entry of entries) {
|
|
3257
|
-
const fullPath = path17.join(dir, entry.name);
|
|
3258
|
-
if (entry.isDirectory()) {
|
|
3259
|
-
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3260
|
-
await scanDir(fullPath);
|
|
3261
|
-
} else if (entry.isFile()) {
|
|
3262
|
-
const ext = path17.extname(entry.name).toLowerCase();
|
|
3263
|
-
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3264
|
-
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3265
|
-
try {
|
|
3266
|
-
indexingJobs.update(project.name, { currentItem: fullPath, itemsDone });
|
|
3267
|
-
const stat = fs15.statSync(fullPath);
|
|
3268
|
-
const mtime = force ? void 0 : stat.mtimeMs;
|
|
3269
|
-
const content = fs15.readFileSync(fullPath, "utf-8");
|
|
3270
|
-
const wasIndexed = await rag.indexFile(fullPath, content, mtime);
|
|
3271
|
-
if (wasIndexed) {
|
|
3272
|
-
indexed++;
|
|
3273
|
-
} else {
|
|
3274
|
-
skipped++;
|
|
3275
|
-
}
|
|
3276
|
-
if (CODE_EXTENSIONS.includes(ext)) {
|
|
3277
|
-
if (!mtime || codeRag.needsReindex(fullPath, mtime)) {
|
|
3278
|
-
const language = getLanguageFromExtension(ext);
|
|
3279
|
-
const chunks = codeRag.chunkContentWithLines(content);
|
|
3280
|
-
codeRag.clearFileChunks(fullPath);
|
|
3281
|
-
for (const chunk of chunks) {
|
|
3282
|
-
const context = extractContext(content, chunk.lineStart, language);
|
|
3283
|
-
await codeRag.indexCodeChunk(fullPath, chunk, context, language, mtime);
|
|
3284
|
-
}
|
|
3285
|
-
codeRag.updateFileMetadata(fullPath, chunks.length, mtime ?? Date.now(), language);
|
|
3286
|
-
codeIndexed++;
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
} catch (err) {
|
|
3290
|
-
logger.error(`[indexKnowledge] Failed to index ${fullPath}`, err);
|
|
3291
|
-
} finally {
|
|
3292
|
-
itemsDone++;
|
|
3293
|
-
indexingJobs.update(project.name, { itemsDone });
|
|
3294
|
-
if (itemsDone % 10 === 0) {
|
|
3295
|
-
await new Promise((resolve4) => setImmediate(resolve4));
|
|
3296
|
-
}
|
|
3297
|
-
}
|
|
3334
|
+
if (matches.length > 0) {
|
|
3335
|
+
results.push({
|
|
3336
|
+
project: project.name,
|
|
3337
|
+
file,
|
|
3338
|
+
matches: matches.slice(0, 5),
|
|
3339
|
+
// Limit to 5 matches per file
|
|
3340
|
+
indexingInProgress: indexingInProgress2 || void 0,
|
|
3341
|
+
advisoryMessage: advisoryMessage2
|
|
3342
|
+
});
|
|
3298
3343
|
}
|
|
3299
3344
|
}
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
|
-
rag.markFullIndex();
|
|
3303
|
-
codeRag.markFullIndex();
|
|
3304
|
-
const stats = rag.getStats();
|
|
3305
|
-
const codeStats = codeRag.getStats();
|
|
3306
|
-
const message = `Indexed ${indexed} files (${codeIndexed} code files), skipped ${skipped} unchanged. Knowledge: ${stats.totalChunks} chunks. Code: ${codeStats.totalChunks} chunks.`;
|
|
3307
|
-
logger.info(`[RAG] ${project.name}: ${message}`);
|
|
3308
|
-
indexingJobs.update(project.name, { currentItem: void 0 });
|
|
3309
|
-
};
|
|
3310
|
-
const startResult = indexingJobs.startOrStatus(project.name, runIndexing);
|
|
3311
|
-
const p = startResult.progress;
|
|
3312
|
-
return {
|
|
3313
|
-
state: startResult.state,
|
|
3314
|
-
status: startResult.status,
|
|
3315
|
-
success: startResult.status === "started" || startResult.status === "already_running",
|
|
3316
|
-
message: startResult.status === "started" ? `Indexing started in background for '${project.name}'.` : `Indexing already running for '${project.name}'.`,
|
|
3317
|
-
filesIndexed: 0,
|
|
3318
|
-
filesSkipped: 0,
|
|
3319
|
-
progress: {
|
|
3320
|
-
itemsDone: p.itemsDone,
|
|
3321
|
-
itemsTotal: p.itemsTotal,
|
|
3322
|
-
currentItem: p.currentItem,
|
|
3323
|
-
startedAt: p.startedAt,
|
|
3324
|
-
completedAt: p.completedAt,
|
|
3325
|
-
lastError: p.lastError
|
|
3345
|
+
} catch (err) {
|
|
3346
|
+
logger.error(`[searchKnowledge] Failed to read knowledge directory ${project.knowledgePath}`, err);
|
|
3326
3347
|
}
|
|
3327
|
-
};
|
|
3328
|
-
}
|
|
3329
|
-
function getContextPreamble() {
|
|
3330
|
-
const activeProject = detectActiveProject();
|
|
3331
|
-
if (!activeProject) {
|
|
3332
|
-
return `## System Context
|
|
3333
|
-
No active project detected. Run \`rrce-workflow mcp configure\` to expose projects.
|
|
3334
|
-
---
|
|
3335
|
-
`;
|
|
3336
|
-
}
|
|
3337
|
-
const rrceHome = process.env.RRCE_HOME || path17.join(os3.homedir(), ".rrce-workflow");
|
|
3338
|
-
const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
|
|
3339
|
-
const rrceData = activeProject.dataPath;
|
|
3340
|
-
return `## System Context
|
|
3341
|
-
| Key | Value |
|
|
3342
|
-
|-----|-------|
|
|
3343
|
-
| WORKSPACE_ROOT | \`${workspaceRoot}\` |
|
|
3344
|
-
| WORKSPACE_NAME | \`${activeProject.name}\` |
|
|
3345
|
-
| RRCE_DATA | \`${rrceData}\` |
|
|
3346
|
-
| RRCE_HOME | \`${rrceHome}\` |
|
|
3347
|
-
|
|
3348
|
-
---
|
|
3349
|
-
`;
|
|
3350
|
-
}
|
|
3351
|
-
function getTask(projectName, taskSlug) {
|
|
3352
|
-
const config = loadMCPConfig();
|
|
3353
|
-
const projects = projectService.scan();
|
|
3354
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3355
|
-
if (!project || !project.tasksPath) return null;
|
|
3356
|
-
const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
|
|
3357
|
-
if (!fs15.existsSync(metaPath)) return null;
|
|
3358
|
-
try {
|
|
3359
|
-
return JSON.parse(fs15.readFileSync(metaPath, "utf-8"));
|
|
3360
|
-
} catch (err) {
|
|
3361
|
-
logger.error(`[getTask] Failed to parse meta.json for task ${taskSlug}`, err);
|
|
3362
|
-
return null;
|
|
3363
3348
|
}
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
const projects = projectService.scan();
|
|
3368
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3369
|
-
if (!project || !project.tasksPath) {
|
|
3370
|
-
throw new Error(`Project '${projectName}' not found or not configured with a tasks path.`);
|
|
3349
|
+
let filteredResults = results;
|
|
3350
|
+
if (options?.min_score !== void 0 && options.min_score > 0) {
|
|
3351
|
+
filteredResults = results.filter((r) => (r.score ?? 1) >= options.min_score);
|
|
3371
3352
|
}
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3353
|
+
filteredResults.sort((a, b) => (b.score ?? 1) - (a.score ?? 1));
|
|
3354
|
+
let truncated = false;
|
|
3355
|
+
let tokenCount = 0;
|
|
3356
|
+
let budgetedResults = filteredResults;
|
|
3357
|
+
if (options?.max_tokens !== void 0 && options.max_tokens > 0) {
|
|
3358
|
+
budgetedResults = [];
|
|
3359
|
+
for (const result of filteredResults) {
|
|
3360
|
+
const resultTokens = estimateTokens(result.matches.join("\n"));
|
|
3361
|
+
if (tokenCount + resultTokens > options.max_tokens) {
|
|
3362
|
+
truncated = true;
|
|
3363
|
+
break;
|
|
3364
|
+
}
|
|
3365
|
+
budgetedResults.push(result);
|
|
3366
|
+
tokenCount += resultTokens;
|
|
3367
|
+
}
|
|
3368
|
+
} else {
|
|
3369
|
+
tokenCount = filteredResults.reduce((sum, r) => sum + estimateTokens(r.matches.join("\n")), 0);
|
|
3375
3370
|
}
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
try {
|
|
3391
|
-
const template = JSON.parse(fs15.readFileSync(templatePath, "utf-8"));
|
|
3392
|
-
meta = { ...template, ...meta };
|
|
3393
|
-
} catch (e) {
|
|
3394
|
-
logger.error("Failed to load meta template", e);
|
|
3371
|
+
let indexAgeSeconds;
|
|
3372
|
+
let lastIndexedAt;
|
|
3373
|
+
let indexingInProgress;
|
|
3374
|
+
let advisoryMessage;
|
|
3375
|
+
if (projectFilter) {
|
|
3376
|
+
const project = projects.find((p) => p.name === projectFilter);
|
|
3377
|
+
if (project) {
|
|
3378
|
+
indexingInProgress = indexingJobs.isRunning(project.name);
|
|
3379
|
+
advisoryMessage = indexingInProgress ? "Indexing in progress; results may be stale/incomplete." : void 0;
|
|
3380
|
+
const progress = indexingJobs.getProgress(project.name);
|
|
3381
|
+
if (progress.completedAt) {
|
|
3382
|
+
lastIndexedAt = new Date(progress.completedAt).toISOString();
|
|
3383
|
+
indexAgeSeconds = Math.floor((Date.now() - progress.completedAt) / 1e3);
|
|
3384
|
+
}
|
|
3395
3385
|
}
|
|
3396
3386
|
}
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
fs15.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
3407
|
-
return meta;
|
|
3408
|
-
}
|
|
3409
|
-
async function updateTask(projectName, taskSlug, taskData) {
|
|
3410
|
-
const meta = getTask(projectName, taskSlug);
|
|
3411
|
-
if (!meta) throw new Error(`Task '${taskSlug}' not found.`);
|
|
3412
|
-
const updatedMeta = {
|
|
3413
|
-
...meta,
|
|
3414
|
-
...taskData,
|
|
3415
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3416
|
-
// Ensure nested objects are merged if they exist in taskData
|
|
3417
|
-
agents: taskData.agents ? { ...meta.agents, ...taskData.agents } : meta.agents,
|
|
3418
|
-
workspace: meta.workspace
|
|
3419
|
-
// Protect workspace metadata
|
|
3387
|
+
const cleanResults = budgetedResults.map(({ indexingInProgress: _, advisoryMessage: __, ...rest }) => rest);
|
|
3388
|
+
return {
|
|
3389
|
+
results: cleanResults,
|
|
3390
|
+
token_count: tokenCount,
|
|
3391
|
+
truncated,
|
|
3392
|
+
index_age_seconds: indexAgeSeconds,
|
|
3393
|
+
last_indexed_at: lastIndexedAt,
|
|
3394
|
+
indexingInProgress,
|
|
3395
|
+
advisoryMessage
|
|
3420
3396
|
};
|
|
3421
|
-
const config = loadMCPConfig();
|
|
3422
|
-
const projects = projectService.scan();
|
|
3423
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3424
|
-
if (!project || !project.tasksPath) return null;
|
|
3425
|
-
const metaPath = path17.join(project.tasksPath, taskSlug, "meta.json");
|
|
3426
|
-
fs15.writeFileSync(metaPath, JSON.stringify(updatedMeta, null, 2));
|
|
3427
|
-
return updatedMeta;
|
|
3428
|
-
}
|
|
3429
|
-
function deleteTask(projectName, taskSlug) {
|
|
3430
|
-
const config = loadMCPConfig();
|
|
3431
|
-
const projects = projectService.scan();
|
|
3432
|
-
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
3433
|
-
if (!project || !project.tasksPath) return false;
|
|
3434
|
-
const taskDir = path17.join(project.tasksPath, taskSlug);
|
|
3435
|
-
if (!fs15.existsSync(taskDir)) return false;
|
|
3436
|
-
if (fs15.rmSync) {
|
|
3437
|
-
fs15.rmSync(taskDir, { recursive: true, force: true });
|
|
3438
|
-
} else {
|
|
3439
|
-
fs15.rmdirSync(taskDir, { recursive: true });
|
|
3440
|
-
}
|
|
3441
|
-
return true;
|
|
3442
3397
|
}
|
|
3443
3398
|
async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
3444
3399
|
const config = loadMCPConfig();
|
|
@@ -3455,10 +3410,10 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3455
3410
|
}
|
|
3456
3411
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3457
3412
|
let absoluteFilePath = filePath;
|
|
3458
|
-
if (!
|
|
3459
|
-
absoluteFilePath =
|
|
3413
|
+
if (!path20.isAbsolute(filePath)) {
|
|
3414
|
+
absoluteFilePath = path20.resolve(projectRoot, filePath);
|
|
3460
3415
|
}
|
|
3461
|
-
if (!
|
|
3416
|
+
if (!fs19.existsSync(absoluteFilePath)) {
|
|
3462
3417
|
return {
|
|
3463
3418
|
success: false,
|
|
3464
3419
|
file: filePath,
|
|
@@ -3475,13 +3430,13 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3475
3430
|
depth: options.depth ?? 1
|
|
3476
3431
|
});
|
|
3477
3432
|
const relationships = related.map((r) => ({
|
|
3478
|
-
file:
|
|
3433
|
+
file: path20.relative(projectRoot, r.file),
|
|
3479
3434
|
relationship: r.relationship,
|
|
3480
3435
|
importPath: r.importPath
|
|
3481
3436
|
}));
|
|
3482
3437
|
return {
|
|
3483
3438
|
success: true,
|
|
3484
|
-
file:
|
|
3439
|
+
file: path20.relative(projectRoot, absoluteFilePath),
|
|
3485
3440
|
project: projectName,
|
|
3486
3441
|
relationships
|
|
3487
3442
|
};
|
|
@@ -3509,7 +3464,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3509
3464
|
};
|
|
3510
3465
|
}
|
|
3511
3466
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3512
|
-
if (!
|
|
3467
|
+
if (!fs19.existsSync(projectRoot)) {
|
|
3513
3468
|
return {
|
|
3514
3469
|
success: false,
|
|
3515
3470
|
project: projectName,
|
|
@@ -3520,14 +3475,14 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3520
3475
|
try {
|
|
3521
3476
|
const codeFiles = [];
|
|
3522
3477
|
const scanDir = (dir) => {
|
|
3523
|
-
const entries =
|
|
3478
|
+
const entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
3524
3479
|
for (const entry of entries) {
|
|
3525
|
-
const fullPath =
|
|
3480
|
+
const fullPath = path20.join(dir, entry.name);
|
|
3526
3481
|
if (entry.isDirectory()) {
|
|
3527
3482
|
if (SKIP_DIRS.includes(entry.name)) continue;
|
|
3528
3483
|
scanDir(fullPath);
|
|
3529
3484
|
} else if (entry.isFile()) {
|
|
3530
|
-
const ext =
|
|
3485
|
+
const ext = path20.extname(entry.name).toLowerCase();
|
|
3531
3486
|
if (CODE_EXTENSIONS.includes(ext)) {
|
|
3532
3487
|
codeFiles.push(fullPath);
|
|
3533
3488
|
}
|
|
@@ -3538,7 +3493,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3538
3493
|
const symbolResults = [];
|
|
3539
3494
|
for (const file of codeFiles.slice(0, 500)) {
|
|
3540
3495
|
try {
|
|
3541
|
-
const content =
|
|
3496
|
+
const content = fs19.readFileSync(file, "utf-8");
|
|
3542
3497
|
const result = extractSymbols(content, file);
|
|
3543
3498
|
symbolResults.push(result);
|
|
3544
3499
|
} catch (e) {
|
|
@@ -3553,7 +3508,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3553
3508
|
const results = matches.map((m) => ({
|
|
3554
3509
|
name: m.name,
|
|
3555
3510
|
type: m.type,
|
|
3556
|
-
file:
|
|
3511
|
+
file: path20.relative(projectRoot, m.file),
|
|
3557
3512
|
line: m.line,
|
|
3558
3513
|
signature: m.signature,
|
|
3559
3514
|
exported: m.exported,
|
|
@@ -3586,24 +3541,24 @@ async function getFileSummary(filePath, projectName) {
|
|
|
3586
3541
|
}
|
|
3587
3542
|
const projectRoot = project.sourcePath || project.path || "";
|
|
3588
3543
|
let absolutePath = filePath;
|
|
3589
|
-
if (!
|
|
3590
|
-
absolutePath =
|
|
3544
|
+
if (!path20.isAbsolute(filePath)) {
|
|
3545
|
+
absolutePath = path20.resolve(projectRoot, filePath);
|
|
3591
3546
|
}
|
|
3592
|
-
if (!
|
|
3547
|
+
if (!fs19.existsSync(absolutePath)) {
|
|
3593
3548
|
return {
|
|
3594
3549
|
success: false,
|
|
3595
3550
|
message: `File not found: ${filePath}`
|
|
3596
3551
|
};
|
|
3597
3552
|
}
|
|
3598
3553
|
try {
|
|
3599
|
-
const stat =
|
|
3600
|
-
const content =
|
|
3554
|
+
const stat = fs19.statSync(absolutePath);
|
|
3555
|
+
const content = fs19.readFileSync(absolutePath, "utf-8");
|
|
3601
3556
|
const lines = content.split("\n");
|
|
3602
3557
|
const symbolResult = extractSymbols(content, absolutePath);
|
|
3603
3558
|
return {
|
|
3604
3559
|
success: true,
|
|
3605
3560
|
summary: {
|
|
3606
|
-
path:
|
|
3561
|
+
path: path20.relative(projectRoot, absolutePath),
|
|
3607
3562
|
language: symbolResult.language,
|
|
3608
3563
|
lines: lines.length,
|
|
3609
3564
|
size_bytes: stat.size,
|
|
@@ -3617,13 +3572,239 @@ async function getFileSummary(filePath, projectName) {
|
|
|
3617
3572
|
}))
|
|
3618
3573
|
}
|
|
3619
3574
|
};
|
|
3620
|
-
} catch (e) {
|
|
3621
|
-
logger.error(`[getFileSummary] Error reading ${filePath}`, e);
|
|
3622
|
-
return {
|
|
3623
|
-
success: false,
|
|
3624
|
-
message: `Error reading file: ${e instanceof Error ? e.message : String(e)}`
|
|
3575
|
+
} catch (e) {
|
|
3576
|
+
logger.error(`[getFileSummary] Error reading ${filePath}`, e);
|
|
3577
|
+
return {
|
|
3578
|
+
success: false,
|
|
3579
|
+
message: `Error reading file: ${e instanceof Error ? e.message : String(e)}`
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
var init_search = __esm({
|
|
3584
|
+
"src/mcp/resources/search.ts"() {
|
|
3585
|
+
"use strict";
|
|
3586
|
+
init_logger();
|
|
3587
|
+
init_config();
|
|
3588
|
+
init_config_utils();
|
|
3589
|
+
init_detection_service();
|
|
3590
|
+
init_rag();
|
|
3591
|
+
init_indexing_jobs();
|
|
3592
|
+
init_dependency_graph();
|
|
3593
|
+
init_symbol_extractor();
|
|
3594
|
+
init_projects();
|
|
3595
|
+
init_utils2();
|
|
3596
|
+
init_constants();
|
|
3597
|
+
}
|
|
3598
|
+
});
|
|
3599
|
+
|
|
3600
|
+
// src/mcp/resources/indexing.ts
|
|
3601
|
+
import * as fs20 from "fs";
|
|
3602
|
+
import * as path21 from "path";
|
|
3603
|
+
async function indexKnowledge(projectName, force = false) {
|
|
3604
|
+
const config = loadMCPConfig();
|
|
3605
|
+
const projects = getExposedProjects();
|
|
3606
|
+
const project = projects.find((p2) => p2.name === projectName || p2.path && p2.path === projectName);
|
|
3607
|
+
if (!project) {
|
|
3608
|
+
return {
|
|
3609
|
+
state: "failed",
|
|
3610
|
+
status: "failed",
|
|
3611
|
+
success: false,
|
|
3612
|
+
message: `Project '${projectName}' not found`,
|
|
3613
|
+
filesIndexed: 0,
|
|
3614
|
+
filesSkipped: 0,
|
|
3615
|
+
progress: { itemsDone: 0 }
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
const projConfig = findProjectConfig(config, { name: project.name, path: project.sourcePath || project.path }) || (project.source === "global" ? { semanticSearch: { enabled: true, model: "Xenova/all-MiniLM-L6-v2" } } : void 0);
|
|
3619
|
+
const isEnabled = projConfig?.semanticSearch?.enabled || project.semanticSearchEnabled;
|
|
3620
|
+
if (!isEnabled) {
|
|
3621
|
+
return {
|
|
3622
|
+
state: "failed",
|
|
3623
|
+
status: "failed",
|
|
3624
|
+
success: false,
|
|
3625
|
+
message: "Semantic Search is not enabled for this project",
|
|
3626
|
+
filesIndexed: 0,
|
|
3627
|
+
filesSkipped: 0,
|
|
3628
|
+
progress: { itemsDone: 0 }
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
const scanRoot = project.sourcePath || project.path || project.dataPath;
|
|
3632
|
+
if (!fs20.existsSync(scanRoot)) {
|
|
3633
|
+
return {
|
|
3634
|
+
state: "failed",
|
|
3635
|
+
status: "failed",
|
|
3636
|
+
success: false,
|
|
3637
|
+
message: "Project root not found",
|
|
3638
|
+
filesIndexed: 0,
|
|
3639
|
+
filesSkipped: 0,
|
|
3640
|
+
progress: { itemsDone: 0 }
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
const runIndexing = async () => {
|
|
3644
|
+
const { shouldSkipEntryDir, shouldSkipEntryFile } = getScanContext(project, scanRoot);
|
|
3645
|
+
const indexPath = path21.join(project.knowledgePath || path21.join(scanRoot, ".rrce-workflow", "knowledge"), "embeddings.json");
|
|
3646
|
+
const codeIndexPath = path21.join(project.knowledgePath || path21.join(scanRoot, ".rrce-workflow", "knowledge"), "code-embeddings.json");
|
|
3647
|
+
const model = projConfig?.semanticSearch?.model || "Xenova/all-MiniLM-L6-v2";
|
|
3648
|
+
const rag = new RAGService(indexPath, model);
|
|
3649
|
+
const codeRag = new RAGService(codeIndexPath, model);
|
|
3650
|
+
let indexed = 0;
|
|
3651
|
+
let codeIndexed = 0;
|
|
3652
|
+
let skipped = 0;
|
|
3653
|
+
let itemsTotal = 0;
|
|
3654
|
+
let itemsDone = 0;
|
|
3655
|
+
const preCount = (dir) => {
|
|
3656
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
3657
|
+
for (const entry of entries) {
|
|
3658
|
+
const fullPath = path21.join(dir, entry.name);
|
|
3659
|
+
if (entry.isDirectory()) {
|
|
3660
|
+
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3661
|
+
preCount(fullPath);
|
|
3662
|
+
} else if (entry.isFile()) {
|
|
3663
|
+
const ext = path21.extname(entry.name).toLowerCase();
|
|
3664
|
+
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3665
|
+
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3666
|
+
itemsTotal++;
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
};
|
|
3670
|
+
preCount(scanRoot);
|
|
3671
|
+
indexingJobs.update(project.name, { itemsTotal });
|
|
3672
|
+
const cleanupIgnoredFiles = async () => {
|
|
3673
|
+
const indexedFiles = [...rag.getIndexedFiles(), ...codeRag.getIndexedFiles()];
|
|
3674
|
+
const unique = Array.from(new Set(indexedFiles));
|
|
3675
|
+
for (const filePath of unique) {
|
|
3676
|
+
if (!path21.isAbsolute(filePath)) continue;
|
|
3677
|
+
const relFilePath = filePath.split(path21.sep).join("/");
|
|
3678
|
+
const relScanRoot = scanRoot.split(path21.sep).join("/");
|
|
3679
|
+
const isInScanRoot = relFilePath === relScanRoot || relFilePath.startsWith(`${relScanRoot}/`);
|
|
3680
|
+
if (!isInScanRoot) continue;
|
|
3681
|
+
if (shouldSkipEntryFile(filePath)) {
|
|
3682
|
+
await rag.removeFile(filePath);
|
|
3683
|
+
await codeRag.removeFile(filePath);
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
};
|
|
3687
|
+
await cleanupIgnoredFiles();
|
|
3688
|
+
const scanDir = async (dir) => {
|
|
3689
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
3690
|
+
for (const entry of entries) {
|
|
3691
|
+
const fullPath = path21.join(dir, entry.name);
|
|
3692
|
+
if (entry.isDirectory()) {
|
|
3693
|
+
if (shouldSkipEntryDir(fullPath)) continue;
|
|
3694
|
+
await scanDir(fullPath);
|
|
3695
|
+
} else if (entry.isFile()) {
|
|
3696
|
+
const ext = path21.extname(entry.name).toLowerCase();
|
|
3697
|
+
if (!INDEXABLE_EXTENSIONS.includes(ext)) continue;
|
|
3698
|
+
if (shouldSkipEntryFile(fullPath)) continue;
|
|
3699
|
+
try {
|
|
3700
|
+
indexingJobs.update(project.name, { currentItem: fullPath, itemsDone });
|
|
3701
|
+
const stat = fs20.statSync(fullPath);
|
|
3702
|
+
const mtime = force ? void 0 : stat.mtimeMs;
|
|
3703
|
+
const content = fs20.readFileSync(fullPath, "utf-8");
|
|
3704
|
+
const wasIndexed = await rag.indexFile(fullPath, content, mtime);
|
|
3705
|
+
if (wasIndexed) {
|
|
3706
|
+
indexed++;
|
|
3707
|
+
} else {
|
|
3708
|
+
skipped++;
|
|
3709
|
+
}
|
|
3710
|
+
if (CODE_EXTENSIONS.includes(ext)) {
|
|
3711
|
+
if (!mtime || codeRag.needsReindex(fullPath, mtime)) {
|
|
3712
|
+
const language = getLanguageFromExtension(ext);
|
|
3713
|
+
const chunks = codeRag.chunkContentWithLines(content);
|
|
3714
|
+
codeRag.clearFileChunks(fullPath);
|
|
3715
|
+
for (const chunk of chunks) {
|
|
3716
|
+
const context = extractContext(content, chunk.lineStart, language);
|
|
3717
|
+
await codeRag.indexCodeChunk(fullPath, chunk, context, language, mtime);
|
|
3718
|
+
}
|
|
3719
|
+
codeRag.updateFileMetadata(fullPath, chunks.length, mtime ?? Date.now(), language);
|
|
3720
|
+
codeIndexed++;
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
} catch (err) {
|
|
3724
|
+
logger.error(`[indexKnowledge] Failed to index ${fullPath}`, err);
|
|
3725
|
+
} finally {
|
|
3726
|
+
itemsDone++;
|
|
3727
|
+
indexingJobs.update(project.name, { itemsDone });
|
|
3728
|
+
if (itemsDone % 10 === 0) {
|
|
3729
|
+
await new Promise((resolve4) => setImmediate(resolve4));
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3625
3734
|
};
|
|
3735
|
+
await scanDir(scanRoot);
|
|
3736
|
+
rag.markFullIndex();
|
|
3737
|
+
codeRag.markFullIndex();
|
|
3738
|
+
const stats = rag.getStats();
|
|
3739
|
+
const codeStats = codeRag.getStats();
|
|
3740
|
+
const message = `Indexed ${indexed} files (${codeIndexed} code files), skipped ${skipped} unchanged. Knowledge: ${stats.totalChunks} chunks. Code: ${codeStats.totalChunks} chunks.`;
|
|
3741
|
+
logger.info(`[RAG] ${project.name}: ${message}`);
|
|
3742
|
+
indexingJobs.update(project.name, { currentItem: void 0 });
|
|
3743
|
+
};
|
|
3744
|
+
const startResult = indexingJobs.startOrStatus(project.name, runIndexing);
|
|
3745
|
+
const p = startResult.progress;
|
|
3746
|
+
return {
|
|
3747
|
+
state: startResult.state,
|
|
3748
|
+
status: startResult.status,
|
|
3749
|
+
success: startResult.status === "started" || startResult.status === "already_running",
|
|
3750
|
+
message: startResult.status === "started" ? `Indexing started in background for '${project.name}'.` : `Indexing already running for '${project.name}'.`,
|
|
3751
|
+
filesIndexed: 0,
|
|
3752
|
+
filesSkipped: 0,
|
|
3753
|
+
progress: {
|
|
3754
|
+
itemsDone: p.itemsDone,
|
|
3755
|
+
itemsTotal: p.itemsTotal,
|
|
3756
|
+
currentItem: p.currentItem,
|
|
3757
|
+
startedAt: p.startedAt,
|
|
3758
|
+
completedAt: p.completedAt,
|
|
3759
|
+
lastError: p.lastError
|
|
3760
|
+
}
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
var init_indexing = __esm({
|
|
3764
|
+
"src/mcp/resources/indexing.ts"() {
|
|
3765
|
+
"use strict";
|
|
3766
|
+
init_logger();
|
|
3767
|
+
init_config();
|
|
3768
|
+
init_config_utils();
|
|
3769
|
+
init_rag();
|
|
3770
|
+
init_indexing_jobs();
|
|
3771
|
+
init_context_extractor();
|
|
3772
|
+
init_projects();
|
|
3773
|
+
init_utils2();
|
|
3774
|
+
init_constants();
|
|
3775
|
+
}
|
|
3776
|
+
});
|
|
3777
|
+
|
|
3778
|
+
// src/mcp/resources/context.ts
|
|
3779
|
+
import * as path22 from "path";
|
|
3780
|
+
import * as os4 from "os";
|
|
3781
|
+
function getContextPreamble() {
|
|
3782
|
+
const activeProject = detectActiveProject();
|
|
3783
|
+
if (!activeProject) {
|
|
3784
|
+
return `## System Context
|
|
3785
|
+
No active project detected.
|
|
3786
|
+
|
|
3787
|
+
**To resolve paths manually:**
|
|
3788
|
+
- Call \`rrce_resolve_path(project: "your-project-name")\`
|
|
3789
|
+
- Or call \`rrce_list_projects()\` to see available projects
|
|
3790
|
+
|
|
3791
|
+
If the above tools fail, ask the user for clarification.
|
|
3792
|
+
---
|
|
3793
|
+
`;
|
|
3626
3794
|
}
|
|
3795
|
+
const rrceHome = process.env.RRCE_HOME || path22.join(os4.homedir(), ".rrce-workflow");
|
|
3796
|
+
const workspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
|
|
3797
|
+
const rrceData = activeProject.dataPath;
|
|
3798
|
+
return `## System Context
|
|
3799
|
+
| Key | Value |
|
|
3800
|
+
|-----|-------|
|
|
3801
|
+
| WORKSPACE_ROOT | \`${workspaceRoot}\` |
|
|
3802
|
+
| WORKSPACE_NAME | \`${activeProject.name}\` |
|
|
3803
|
+
| RRCE_DATA | \`${rrceData}\` |
|
|
3804
|
+
| RRCE_HOME | \`${rrceHome}\` |
|
|
3805
|
+
|
|
3806
|
+
---
|
|
3807
|
+
`;
|
|
3627
3808
|
}
|
|
3628
3809
|
async function getContextBundle(query, projectName, options = {}) {
|
|
3629
3810
|
const maxTokens = options.max_tokens ?? 4e3;
|
|
@@ -3768,6 +3949,18 @@ async function prefetchTaskContext(projectName, taskSlug, options = {}) {
|
|
|
3768
3949
|
truncated: bundle.truncated
|
|
3769
3950
|
};
|
|
3770
3951
|
}
|
|
3952
|
+
var init_context = __esm({
|
|
3953
|
+
"src/mcp/resources/context.ts"() {
|
|
3954
|
+
"use strict";
|
|
3955
|
+
init_config_utils();
|
|
3956
|
+
init_projects();
|
|
3957
|
+
init_search();
|
|
3958
|
+
init_tasks();
|
|
3959
|
+
init_utils2();
|
|
3960
|
+
}
|
|
3961
|
+
});
|
|
3962
|
+
|
|
3963
|
+
// src/mcp/resources/validation.ts
|
|
3771
3964
|
function searchTasks(projectName, options = {}) {
|
|
3772
3965
|
const allTasks = getProjectTasks(projectName);
|
|
3773
3966
|
const limit = options.limit ?? 20;
|
|
@@ -3886,6 +4079,16 @@ function validatePhase(projectName, taskSlug, phase) {
|
|
|
3886
4079
|
suggestions
|
|
3887
4080
|
};
|
|
3888
4081
|
}
|
|
4082
|
+
var init_validation = __esm({
|
|
4083
|
+
"src/mcp/resources/validation.ts"() {
|
|
4084
|
+
"use strict";
|
|
4085
|
+
init_tasks();
|
|
4086
|
+
}
|
|
4087
|
+
});
|
|
4088
|
+
|
|
4089
|
+
// src/mcp/resources/sessions.ts
|
|
4090
|
+
import * as fs21 from "fs";
|
|
4091
|
+
import * as path23 from "path";
|
|
3889
4092
|
function startSession(projectName, taskSlug, agent, phase) {
|
|
3890
4093
|
const config = loadMCPConfig();
|
|
3891
4094
|
const projects = projectService.scan();
|
|
@@ -3893,8 +4096,8 @@ function startSession(projectName, taskSlug, agent, phase) {
|
|
|
3893
4096
|
if (!project || !project.tasksPath) {
|
|
3894
4097
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3895
4098
|
}
|
|
3896
|
-
const taskDir =
|
|
3897
|
-
if (!
|
|
4099
|
+
const taskDir = path23.join(project.tasksPath, taskSlug);
|
|
4100
|
+
if (!fs21.existsSync(taskDir)) {
|
|
3898
4101
|
return { success: false, message: `Task '${taskSlug}' not found.` };
|
|
3899
4102
|
}
|
|
3900
4103
|
const session = {
|
|
@@ -3904,8 +4107,8 @@ function startSession(projectName, taskSlug, agent, phase) {
|
|
|
3904
4107
|
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3905
4108
|
heartbeat: (/* @__PURE__ */ new Date()).toISOString()
|
|
3906
4109
|
};
|
|
3907
|
-
const sessionPath =
|
|
3908
|
-
|
|
4110
|
+
const sessionPath = path23.join(taskDir, "session.json");
|
|
4111
|
+
fs21.writeFileSync(sessionPath, JSON.stringify(session, null, 2));
|
|
3909
4112
|
return { success: true, message: `Session started for ${agent} agent on task '${taskSlug}' (phase: ${phase})` };
|
|
3910
4113
|
}
|
|
3911
4114
|
function endSession(projectName, taskSlug) {
|
|
@@ -3915,11 +4118,11 @@ function endSession(projectName, taskSlug) {
|
|
|
3915
4118
|
if (!project || !project.tasksPath) {
|
|
3916
4119
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3917
4120
|
}
|
|
3918
|
-
const sessionPath =
|
|
3919
|
-
if (!
|
|
4121
|
+
const sessionPath = path23.join(project.tasksPath, taskSlug, "session.json");
|
|
4122
|
+
if (!fs21.existsSync(sessionPath)) {
|
|
3920
4123
|
return { success: true, message: `No active session for task '${taskSlug}'.` };
|
|
3921
4124
|
}
|
|
3922
|
-
|
|
4125
|
+
fs21.unlinkSync(sessionPath);
|
|
3923
4126
|
return { success: true, message: `Session ended for task '${taskSlug}'.` };
|
|
3924
4127
|
}
|
|
3925
4128
|
function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
@@ -3929,9 +4132,9 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
|
3929
4132
|
if (!project || !project.tasksPath) {
|
|
3930
4133
|
return { success: false, message: `Project '${projectName}' not found or not exposed.` };
|
|
3931
4134
|
}
|
|
3932
|
-
const taskDir =
|
|
3933
|
-
if (!
|
|
3934
|
-
|
|
4135
|
+
const taskDir = path23.join(project.tasksPath, taskSlug);
|
|
4136
|
+
if (!fs21.existsSync(taskDir)) {
|
|
4137
|
+
fs21.mkdirSync(taskDir, { recursive: true });
|
|
3935
4138
|
}
|
|
3936
4139
|
const todos = {
|
|
3937
4140
|
phase,
|
|
@@ -3939,91 +4142,41 @@ function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
|
3939
4142
|
items,
|
|
3940
4143
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3941
4144
|
};
|
|
3942
|
-
const todosPath =
|
|
3943
|
-
|
|
4145
|
+
const todosPath = path23.join(taskDir, "agent-todos.json");
|
|
4146
|
+
fs21.writeFileSync(todosPath, JSON.stringify(todos, null, 2));
|
|
3944
4147
|
return { success: true, message: `Updated ${items.length} todo items for task '${taskSlug}'.`, count: items.length };
|
|
3945
4148
|
}
|
|
3946
|
-
var
|
|
3947
|
-
|
|
3948
|
-
"src/mcp/resources.ts"() {
|
|
4149
|
+
var init_sessions = __esm({
|
|
4150
|
+
"src/mcp/resources/sessions.ts"() {
|
|
3949
4151
|
"use strict";
|
|
3950
|
-
init_logger();
|
|
3951
4152
|
init_config();
|
|
3952
|
-
init_config_utils();
|
|
3953
|
-
init_detection();
|
|
3954
4153
|
init_detection_service();
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
".php",
|
|
3982
|
-
".swift",
|
|
3983
|
-
".md",
|
|
3984
|
-
".mdx",
|
|
3985
|
-
".json",
|
|
3986
|
-
".yaml",
|
|
3987
|
-
".yml",
|
|
3988
|
-
".toml",
|
|
3989
|
-
".sh",
|
|
3990
|
-
".bash",
|
|
3991
|
-
".zsh",
|
|
3992
|
-
".sql",
|
|
3993
|
-
".html",
|
|
3994
|
-
".css",
|
|
3995
|
-
".scss",
|
|
3996
|
-
".sass",
|
|
3997
|
-
".less"
|
|
3998
|
-
];
|
|
3999
|
-
CODE_EXTENSIONS = [
|
|
4000
|
-
".ts",
|
|
4001
|
-
".tsx",
|
|
4002
|
-
".js",
|
|
4003
|
-
".jsx",
|
|
4004
|
-
".mjs",
|
|
4005
|
-
".cjs",
|
|
4006
|
-
".py",
|
|
4007
|
-
".pyw",
|
|
4008
|
-
".go",
|
|
4009
|
-
".rs",
|
|
4010
|
-
".java",
|
|
4011
|
-
".kt",
|
|
4012
|
-
".kts",
|
|
4013
|
-
".c",
|
|
4014
|
-
".cpp",
|
|
4015
|
-
".h",
|
|
4016
|
-
".hpp",
|
|
4017
|
-
".cs",
|
|
4018
|
-
".rb",
|
|
4019
|
-
".php",
|
|
4020
|
-
".swift",
|
|
4021
|
-
".sh",
|
|
4022
|
-
".bash",
|
|
4023
|
-
".zsh",
|
|
4024
|
-
".sql"
|
|
4025
|
-
];
|
|
4026
|
-
SKIP_DIRS = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "venv", ".venv", "target", "vendor"];
|
|
4154
|
+
}
|
|
4155
|
+
});
|
|
4156
|
+
|
|
4157
|
+
// src/mcp/resources/index.ts
|
|
4158
|
+
var init_resources = __esm({
|
|
4159
|
+
"src/mcp/resources/index.ts"() {
|
|
4160
|
+
"use strict";
|
|
4161
|
+
init_types2();
|
|
4162
|
+
init_constants();
|
|
4163
|
+
init_utils2();
|
|
4164
|
+
init_paths2();
|
|
4165
|
+
init_projects();
|
|
4166
|
+
init_tasks();
|
|
4167
|
+
init_search();
|
|
4168
|
+
init_indexing();
|
|
4169
|
+
init_context();
|
|
4170
|
+
init_validation();
|
|
4171
|
+
init_sessions();
|
|
4172
|
+
}
|
|
4173
|
+
});
|
|
4174
|
+
|
|
4175
|
+
// src/mcp/resources.ts
|
|
4176
|
+
var init_resources2 = __esm({
|
|
4177
|
+
"src/mcp/resources.ts"() {
|
|
4178
|
+
"use strict";
|
|
4179
|
+
init_resources();
|
|
4027
4180
|
}
|
|
4028
4181
|
});
|
|
4029
4182
|
|
|
@@ -4101,25 +4254,25 @@ function registerResourceHandlers(server) {
|
|
|
4101
4254
|
}
|
|
4102
4255
|
});
|
|
4103
4256
|
}
|
|
4104
|
-
var
|
|
4257
|
+
var init_resources3 = __esm({
|
|
4105
4258
|
"src/mcp/handlers/resources.ts"() {
|
|
4106
4259
|
"use strict";
|
|
4107
4260
|
init_logger();
|
|
4108
4261
|
init_config();
|
|
4109
|
-
|
|
4262
|
+
init_resources2();
|
|
4110
4263
|
}
|
|
4111
4264
|
});
|
|
4112
4265
|
|
|
4113
4266
|
// src/mcp/prompts.ts
|
|
4114
|
-
import * as
|
|
4115
|
-
import * as
|
|
4267
|
+
import * as path24 from "path";
|
|
4268
|
+
import * as fs22 from "fs";
|
|
4116
4269
|
function loadBaseProtocol2() {
|
|
4117
4270
|
if (baseProtocolCache !== null) {
|
|
4118
4271
|
return baseProtocolCache;
|
|
4119
4272
|
}
|
|
4120
|
-
const basePath =
|
|
4121
|
-
if (
|
|
4122
|
-
const content =
|
|
4273
|
+
const basePath = path24.join(getAgentCorePromptsDir(), "_base.md");
|
|
4274
|
+
if (fs22.existsSync(basePath)) {
|
|
4275
|
+
const content = fs22.readFileSync(basePath, "utf-8");
|
|
4123
4276
|
baseProtocolCache = content.replace(/^---[\s\S]*?---\n*/, "");
|
|
4124
4277
|
return baseProtocolCache;
|
|
4125
4278
|
}
|
|
@@ -4182,15 +4335,15 @@ function renderPromptWithContext(content, args) {
|
|
|
4182
4335
|
resolvedWorkspaceRoot = activeProject.sourcePath || activeProject.path || activeProject.dataPath;
|
|
4183
4336
|
resolvedWorkspaceName = activeProject.name;
|
|
4184
4337
|
if (activeProject.source === "global") {
|
|
4185
|
-
const workspacesDir =
|
|
4186
|
-
resolvedRrceHome =
|
|
4338
|
+
const workspacesDir = path24.dirname(activeProject.dataPath);
|
|
4339
|
+
resolvedRrceHome = path24.dirname(workspacesDir);
|
|
4187
4340
|
}
|
|
4188
4341
|
} else {
|
|
4189
4342
|
try {
|
|
4190
4343
|
const workspaceRoot = detectWorkspaceRoot();
|
|
4191
|
-
const workspaceName =
|
|
4192
|
-
const globalWorkspacePath =
|
|
4193
|
-
if (
|
|
4344
|
+
const workspaceName = path24.basename(workspaceRoot);
|
|
4345
|
+
const globalWorkspacePath = path24.join(DEFAULT_RRCE_HOME, "workspaces", workspaceName);
|
|
4346
|
+
if (fs22.existsSync(globalWorkspacePath)) {
|
|
4194
4347
|
resolvedRrceData = globalWorkspacePath;
|
|
4195
4348
|
resolvedWorkspaceRoot = workspaceRoot;
|
|
4196
4349
|
resolvedWorkspaceName = workspaceName;
|
|
@@ -4231,7 +4384,7 @@ var init_prompts2 = __esm({
|
|
|
4231
4384
|
"src/mcp/prompts.ts"() {
|
|
4232
4385
|
"use strict";
|
|
4233
4386
|
init_prompts();
|
|
4234
|
-
|
|
4387
|
+
init_resources2();
|
|
4235
4388
|
init_paths();
|
|
4236
4389
|
init_detection_service();
|
|
4237
4390
|
baseProtocolCache = null;
|
|
@@ -4792,7 +4945,7 @@ var init_tools = __esm({
|
|
|
4792
4945
|
"src/mcp/handlers/tools.ts"() {
|
|
4793
4946
|
"use strict";
|
|
4794
4947
|
init_logger();
|
|
4795
|
-
|
|
4948
|
+
init_resources2();
|
|
4796
4949
|
init_prompts2();
|
|
4797
4950
|
}
|
|
4798
4951
|
});
|
|
@@ -4860,7 +5013,7 @@ var init_prompts3 = __esm({
|
|
|
4860
5013
|
"src/mcp/handlers/prompts.ts"() {
|
|
4861
5014
|
"use strict";
|
|
4862
5015
|
init_logger();
|
|
4863
|
-
|
|
5016
|
+
init_resources2();
|
|
4864
5017
|
init_prompts2();
|
|
4865
5018
|
init_paths();
|
|
4866
5019
|
}
|
|
@@ -4928,8 +5081,8 @@ var init_server = __esm({
|
|
|
4928
5081
|
"use strict";
|
|
4929
5082
|
init_logger();
|
|
4930
5083
|
init_config();
|
|
4931
|
-
init_resources();
|
|
4932
5084
|
init_resources2();
|
|
5085
|
+
init_resources3();
|
|
4933
5086
|
init_tools();
|
|
4934
5087
|
init_prompts3();
|
|
4935
5088
|
serverState = { running: false };
|
|
@@ -5019,8 +5172,8 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
5019
5172
|
}
|
|
5020
5173
|
async function handleConfigureGlobalPath() {
|
|
5021
5174
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
5022
|
-
const
|
|
5023
|
-
const
|
|
5175
|
+
const fs33 = await import("fs");
|
|
5176
|
+
const path32 = await import("path");
|
|
5024
5177
|
note3(
|
|
5025
5178
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
5026
5179
|
and coordinate across projects.
|
|
@@ -5034,8 +5187,8 @@ locally in each project. MCP needs a central location.`,
|
|
|
5034
5187
|
return false;
|
|
5035
5188
|
}
|
|
5036
5189
|
try {
|
|
5037
|
-
if (!
|
|
5038
|
-
|
|
5190
|
+
if (!fs33.existsSync(resolvedPath)) {
|
|
5191
|
+
fs33.mkdirSync(resolvedPath, { recursive: true });
|
|
5039
5192
|
}
|
|
5040
5193
|
const config = loadMCPConfig();
|
|
5041
5194
|
saveMCPConfig(config);
|
|
@@ -5043,7 +5196,7 @@ locally in each project. MCP needs a central location.`,
|
|
|
5043
5196
|
`${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
|
|
5044
5197
|
|
|
5045
5198
|
MCP config will be stored at:
|
|
5046
|
-
${
|
|
5199
|
+
${path32.join(resolvedPath, "mcp.yaml")}`,
|
|
5047
5200
|
"Configuration Saved"
|
|
5048
5201
|
);
|
|
5049
5202
|
return true;
|
|
@@ -5140,8 +5293,8 @@ var init_Header = __esm({
|
|
|
5140
5293
|
});
|
|
5141
5294
|
|
|
5142
5295
|
// src/lib/drift-service.ts
|
|
5143
|
-
import * as
|
|
5144
|
-
import * as
|
|
5296
|
+
import * as fs23 from "fs";
|
|
5297
|
+
import * as path25 from "path";
|
|
5145
5298
|
import * as crypto2 from "crypto";
|
|
5146
5299
|
var DriftService;
|
|
5147
5300
|
var init_drift_service = __esm({
|
|
@@ -5150,26 +5303,26 @@ var init_drift_service = __esm({
|
|
|
5150
5303
|
DriftService = class {
|
|
5151
5304
|
static CHECKSUM_FILENAME = ".rrce-checksums.json";
|
|
5152
5305
|
static calculateHash(filePath) {
|
|
5153
|
-
const content =
|
|
5306
|
+
const content = fs23.readFileSync(filePath);
|
|
5154
5307
|
return crypto2.createHash("md5").update(content).digest("hex");
|
|
5155
5308
|
}
|
|
5156
5309
|
static getManifestPath(projectPath) {
|
|
5157
|
-
return
|
|
5310
|
+
return path25.join(projectPath, this.CHECKSUM_FILENAME);
|
|
5158
5311
|
}
|
|
5159
5312
|
static loadManifest(projectPath) {
|
|
5160
5313
|
const manifestPath = this.getManifestPath(projectPath);
|
|
5161
|
-
if (!
|
|
5314
|
+
if (!fs23.existsSync(manifestPath)) {
|
|
5162
5315
|
return {};
|
|
5163
5316
|
}
|
|
5164
5317
|
try {
|
|
5165
|
-
return JSON.parse(
|
|
5318
|
+
return JSON.parse(fs23.readFileSync(manifestPath, "utf8"));
|
|
5166
5319
|
} catch (e) {
|
|
5167
5320
|
return {};
|
|
5168
5321
|
}
|
|
5169
5322
|
}
|
|
5170
5323
|
static saveManifest(projectPath, manifest) {
|
|
5171
5324
|
const manifestPath = this.getManifestPath(projectPath);
|
|
5172
|
-
|
|
5325
|
+
fs23.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
5173
5326
|
}
|
|
5174
5327
|
/**
|
|
5175
5328
|
* Generates a manifest for the current state of files in the project
|
|
@@ -5177,9 +5330,9 @@ var init_drift_service = __esm({
|
|
|
5177
5330
|
static generateManifest(projectPath, files) {
|
|
5178
5331
|
const manifest = {};
|
|
5179
5332
|
for (const file of files) {
|
|
5180
|
-
const fullPath =
|
|
5181
|
-
if (
|
|
5182
|
-
const stats =
|
|
5333
|
+
const fullPath = path25.join(projectPath, file);
|
|
5334
|
+
if (fs23.existsSync(fullPath)) {
|
|
5335
|
+
const stats = fs23.statSync(fullPath);
|
|
5183
5336
|
manifest[file] = {
|
|
5184
5337
|
hash: this.calculateHash(fullPath),
|
|
5185
5338
|
mtime: stats.mtimeMs
|
|
@@ -5195,11 +5348,11 @@ var init_drift_service = __esm({
|
|
|
5195
5348
|
const manifest = this.loadManifest(projectPath);
|
|
5196
5349
|
const modifiedFiles = [];
|
|
5197
5350
|
for (const [relPath, entry] of Object.entries(manifest)) {
|
|
5198
|
-
const fullPath =
|
|
5199
|
-
if (!
|
|
5351
|
+
const fullPath = path25.join(projectPath, relPath);
|
|
5352
|
+
if (!fs23.existsSync(fullPath)) {
|
|
5200
5353
|
continue;
|
|
5201
5354
|
}
|
|
5202
|
-
const stats =
|
|
5355
|
+
const stats = fs23.statSync(fullPath);
|
|
5203
5356
|
if (stats.mtimeMs === entry.mtime) {
|
|
5204
5357
|
continue;
|
|
5205
5358
|
}
|
|
@@ -5246,15 +5399,15 @@ __export(ConfigContext_exports, {
|
|
|
5246
5399
|
useConfig: () => useConfig
|
|
5247
5400
|
});
|
|
5248
5401
|
import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
|
|
5249
|
-
import * as
|
|
5250
|
-
import * as
|
|
5402
|
+
import * as fs24 from "fs";
|
|
5403
|
+
import * as path26 from "path";
|
|
5251
5404
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
5252
5405
|
function getPackageVersion() {
|
|
5253
5406
|
try {
|
|
5254
5407
|
const agentCoreDir = getAgentCoreDir();
|
|
5255
|
-
const packageJsonPath =
|
|
5256
|
-
if (
|
|
5257
|
-
return JSON.parse(
|
|
5408
|
+
const packageJsonPath = path26.join(path26.dirname(agentCoreDir), "package.json");
|
|
5409
|
+
if (fs24.existsSync(packageJsonPath)) {
|
|
5410
|
+
return JSON.parse(fs24.readFileSync(packageJsonPath, "utf8")).version;
|
|
5258
5411
|
}
|
|
5259
5412
|
} catch (e) {
|
|
5260
5413
|
}
|
|
@@ -5321,16 +5474,16 @@ var init_ConfigContext = __esm({
|
|
|
5321
5474
|
});
|
|
5322
5475
|
|
|
5323
5476
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
5324
|
-
import * as
|
|
5325
|
-
import * as
|
|
5477
|
+
import * as fs25 from "fs";
|
|
5478
|
+
import * as path27 from "path";
|
|
5326
5479
|
function readSession(project, taskSlug) {
|
|
5327
5480
|
const rrceData = getProjectRRCEData(project);
|
|
5328
|
-
const sessionPath =
|
|
5329
|
-
if (!
|
|
5481
|
+
const sessionPath = path27.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5482
|
+
if (!fs25.existsSync(sessionPath)) {
|
|
5330
5483
|
return null;
|
|
5331
5484
|
}
|
|
5332
5485
|
try {
|
|
5333
|
-
const raw =
|
|
5486
|
+
const raw = fs25.readFileSync(sessionPath, "utf-8");
|
|
5334
5487
|
return JSON.parse(raw);
|
|
5335
5488
|
} catch {
|
|
5336
5489
|
return null;
|
|
@@ -5343,12 +5496,12 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
|
5343
5496
|
}
|
|
5344
5497
|
function readAgentTodos(project, taskSlug) {
|
|
5345
5498
|
const rrceData = getProjectRRCEData(project);
|
|
5346
|
-
const todosPath =
|
|
5347
|
-
if (!
|
|
5499
|
+
const todosPath = path27.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5500
|
+
if (!fs25.existsSync(todosPath)) {
|
|
5348
5501
|
return null;
|
|
5349
5502
|
}
|
|
5350
5503
|
try {
|
|
5351
|
-
const raw =
|
|
5504
|
+
const raw = fs25.readFileSync(todosPath, "utf-8");
|
|
5352
5505
|
return JSON.parse(raw);
|
|
5353
5506
|
} catch {
|
|
5354
5507
|
return null;
|
|
@@ -5361,8 +5514,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5361
5514
|
if (configPath.startsWith(rrceHome)) {
|
|
5362
5515
|
return "global";
|
|
5363
5516
|
}
|
|
5364
|
-
if (
|
|
5365
|
-
const content =
|
|
5517
|
+
if (fs25.existsSync(configPath)) {
|
|
5518
|
+
const content = fs25.readFileSync(configPath, "utf-8");
|
|
5366
5519
|
if (content.includes("mode: workspace")) return "workspace";
|
|
5367
5520
|
if (content.includes("mode: global")) return "global";
|
|
5368
5521
|
}
|
|
@@ -5372,7 +5525,7 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5372
5525
|
}
|
|
5373
5526
|
function getEffectiveGlobalBase() {
|
|
5374
5527
|
const dummy = resolveDataPath("global", "__rrce_dummy__", "");
|
|
5375
|
-
return
|
|
5528
|
+
return path27.dirname(path27.dirname(dummy));
|
|
5376
5529
|
}
|
|
5377
5530
|
function getProjectRRCEData(project) {
|
|
5378
5531
|
const workspaceRoot = project.sourcePath || project.path;
|
|
@@ -5381,19 +5534,19 @@ function getProjectRRCEData(project) {
|
|
|
5381
5534
|
}
|
|
5382
5535
|
function listProjectTasks(project) {
|
|
5383
5536
|
const rrceData = getProjectRRCEData(project);
|
|
5384
|
-
const tasksPath =
|
|
5385
|
-
if (!
|
|
5537
|
+
const tasksPath = path27.join(rrceData, "tasks");
|
|
5538
|
+
if (!fs25.existsSync(tasksPath)) {
|
|
5386
5539
|
return { projectName: project.name, tasksPath, tasks: [] };
|
|
5387
5540
|
}
|
|
5388
5541
|
const tasks = [];
|
|
5389
5542
|
try {
|
|
5390
|
-
const entries =
|
|
5543
|
+
const entries = fs25.readdirSync(tasksPath, { withFileTypes: true });
|
|
5391
5544
|
for (const entry of entries) {
|
|
5392
5545
|
if (!entry.isDirectory()) continue;
|
|
5393
|
-
const metaPath =
|
|
5394
|
-
if (!
|
|
5546
|
+
const metaPath = path27.join(tasksPath, entry.name, "meta.json");
|
|
5547
|
+
if (!fs25.existsSync(metaPath)) continue;
|
|
5395
5548
|
try {
|
|
5396
|
-
const raw =
|
|
5549
|
+
const raw = fs25.readFileSync(metaPath, "utf-8");
|
|
5397
5550
|
const meta = JSON.parse(raw);
|
|
5398
5551
|
if (!meta.task_slug) meta.task_slug = entry.name;
|
|
5399
5552
|
tasks.push(meta);
|
|
@@ -5412,18 +5565,18 @@ function listProjectTasks(project) {
|
|
|
5412
5565
|
}
|
|
5413
5566
|
function updateTaskStatus(project, taskSlug, status) {
|
|
5414
5567
|
const rrceData = getProjectRRCEData(project);
|
|
5415
|
-
const metaPath =
|
|
5416
|
-
if (!
|
|
5568
|
+
const metaPath = path27.join(rrceData, "tasks", taskSlug, "meta.json");
|
|
5569
|
+
if (!fs25.existsSync(metaPath)) {
|
|
5417
5570
|
return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
|
|
5418
5571
|
}
|
|
5419
5572
|
try {
|
|
5420
|
-
const meta = JSON.parse(
|
|
5573
|
+
const meta = JSON.parse(fs25.readFileSync(metaPath, "utf-8"));
|
|
5421
5574
|
const next = {
|
|
5422
5575
|
...meta,
|
|
5423
5576
|
status,
|
|
5424
5577
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5425
5578
|
};
|
|
5426
|
-
|
|
5579
|
+
fs25.writeFileSync(metaPath, JSON.stringify(next, null, 2));
|
|
5427
5580
|
return { ok: true, meta: next };
|
|
5428
5581
|
} catch (e) {
|
|
5429
5582
|
return { ok: false, error: String(e) };
|
|
@@ -5784,28 +5937,28 @@ var init_project_utils = __esm({
|
|
|
5784
5937
|
// src/mcp/ui/ProjectsView.tsx
|
|
5785
5938
|
import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
5786
5939
|
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
5787
|
-
import * as
|
|
5788
|
-
import * as
|
|
5940
|
+
import * as fs26 from "fs";
|
|
5941
|
+
import * as path28 from "path";
|
|
5789
5942
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
5790
5943
|
function getIndexStats(project) {
|
|
5791
5944
|
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
5792
5945
|
try {
|
|
5793
5946
|
const knowledgePath = project.knowledgePath;
|
|
5794
5947
|
if (knowledgePath) {
|
|
5795
|
-
const embPath =
|
|
5796
|
-
const codeEmbPath =
|
|
5797
|
-
if (
|
|
5798
|
-
const stat =
|
|
5948
|
+
const embPath = path28.join(knowledgePath, "embeddings.json");
|
|
5949
|
+
const codeEmbPath = path28.join(knowledgePath, "code-embeddings.json");
|
|
5950
|
+
if (fs26.existsSync(embPath)) {
|
|
5951
|
+
const stat = fs26.statSync(embPath);
|
|
5799
5952
|
stats.lastIndexed = stat.mtime.toISOString();
|
|
5800
5953
|
try {
|
|
5801
|
-
const data = JSON.parse(
|
|
5954
|
+
const data = JSON.parse(fs26.readFileSync(embPath, "utf-8"));
|
|
5802
5955
|
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5803
5956
|
} catch {
|
|
5804
5957
|
}
|
|
5805
5958
|
}
|
|
5806
|
-
if (
|
|
5959
|
+
if (fs26.existsSync(codeEmbPath)) {
|
|
5807
5960
|
try {
|
|
5808
|
-
const data = JSON.parse(
|
|
5961
|
+
const data = JSON.parse(fs26.readFileSync(codeEmbPath, "utf-8"));
|
|
5809
5962
|
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
5810
5963
|
} catch {
|
|
5811
5964
|
}
|
|
@@ -6506,7 +6659,7 @@ __export(App_exports, {
|
|
|
6506
6659
|
});
|
|
6507
6660
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
6508
6661
|
import { Box as Box11, useInput as useInput5, useApp } from "ink";
|
|
6509
|
-
import
|
|
6662
|
+
import fs27 from "fs";
|
|
6510
6663
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
6511
6664
|
var App;
|
|
6512
6665
|
var init_App = __esm({
|
|
@@ -6579,18 +6732,18 @@ var init_App = __esm({
|
|
|
6579
6732
|
useEffect6(() => {
|
|
6580
6733
|
const logPath = getLogFilePath();
|
|
6581
6734
|
let lastSize = 0;
|
|
6582
|
-
if (
|
|
6583
|
-
const stats =
|
|
6735
|
+
if (fs27.existsSync(logPath)) {
|
|
6736
|
+
const stats = fs27.statSync(logPath);
|
|
6584
6737
|
lastSize = stats.size;
|
|
6585
6738
|
}
|
|
6586
6739
|
const interval = setInterval(() => {
|
|
6587
|
-
if (
|
|
6588
|
-
const stats =
|
|
6740
|
+
if (fs27.existsSync(logPath)) {
|
|
6741
|
+
const stats = fs27.statSync(logPath);
|
|
6589
6742
|
if (stats.size > lastSize) {
|
|
6590
6743
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
6591
|
-
const fd =
|
|
6592
|
-
|
|
6593
|
-
|
|
6744
|
+
const fd = fs27.openSync(logPath, "r");
|
|
6745
|
+
fs27.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
6746
|
+
fs27.closeSync(fd);
|
|
6594
6747
|
const newContent = buffer.toString("utf-8");
|
|
6595
6748
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
6596
6749
|
setLogs((prev) => {
|
|
@@ -6815,15 +6968,15 @@ __export(update_flow_exports, {
|
|
|
6815
6968
|
});
|
|
6816
6969
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
6817
6970
|
import pc8 from "picocolors";
|
|
6818
|
-
import * as
|
|
6819
|
-
import * as
|
|
6971
|
+
import * as fs28 from "fs";
|
|
6972
|
+
import * as path29 from "path";
|
|
6820
6973
|
import { stringify as stringify2, parse } from "yaml";
|
|
6821
6974
|
function backupFile(filePath) {
|
|
6822
|
-
if (!
|
|
6975
|
+
if (!fs28.existsSync(filePath)) return null;
|
|
6823
6976
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
6824
6977
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
6825
6978
|
try {
|
|
6826
|
-
|
|
6979
|
+
fs28.copyFileSync(filePath, backupPath);
|
|
6827
6980
|
return backupPath;
|
|
6828
6981
|
} catch (e) {
|
|
6829
6982
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -6833,9 +6986,9 @@ function backupFile(filePath) {
|
|
|
6833
6986
|
function getPackageVersion2() {
|
|
6834
6987
|
try {
|
|
6835
6988
|
const agentCoreDir = getAgentCoreDir();
|
|
6836
|
-
const packageJsonPath =
|
|
6837
|
-
if (
|
|
6838
|
-
return JSON.parse(
|
|
6989
|
+
const packageJsonPath = path29.join(path29.dirname(agentCoreDir), "package.json");
|
|
6990
|
+
if (fs28.existsSync(packageJsonPath)) {
|
|
6991
|
+
return JSON.parse(fs28.readFileSync(packageJsonPath, "utf8")).version;
|
|
6839
6992
|
}
|
|
6840
6993
|
} catch (e) {
|
|
6841
6994
|
}
|
|
@@ -6850,9 +7003,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6850
7003
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
6851
7004
|
const configFilePath = getConfigPath(workspacePath);
|
|
6852
7005
|
let currentSyncedVersion;
|
|
6853
|
-
if (
|
|
7006
|
+
if (fs28.existsSync(configFilePath)) {
|
|
6854
7007
|
try {
|
|
6855
|
-
const content =
|
|
7008
|
+
const content = fs28.readFileSync(configFilePath, "utf-8");
|
|
6856
7009
|
const config = parse(content);
|
|
6857
7010
|
currentSyncedVersion = config.last_synced_version;
|
|
6858
7011
|
} catch (e) {
|
|
@@ -6860,8 +7013,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6860
7013
|
}
|
|
6861
7014
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
6862
7015
|
const ideTargets = [];
|
|
6863
|
-
if (
|
|
6864
|
-
const configContent =
|
|
7016
|
+
if (fs28.existsSync(configFilePath)) {
|
|
7017
|
+
const configContent = fs28.readFileSync(configFilePath, "utf-8");
|
|
6865
7018
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
6866
7019
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
6867
7020
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -6870,14 +7023,14 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6870
7023
|
const dirs = ["templates", "prompts", "docs"];
|
|
6871
7024
|
const updatedFiles = [];
|
|
6872
7025
|
for (const dir of dirs) {
|
|
6873
|
-
const srcDir =
|
|
6874
|
-
if (!
|
|
7026
|
+
const srcDir = path29.join(agentCoreDir, dir);
|
|
7027
|
+
if (!fs28.existsSync(srcDir)) continue;
|
|
6875
7028
|
const syncFiles = (src, rel) => {
|
|
6876
|
-
const entries =
|
|
7029
|
+
const entries = fs28.readdirSync(src, { withFileTypes: true });
|
|
6877
7030
|
for (const entry of entries) {
|
|
6878
|
-
const entrySrc =
|
|
6879
|
-
const entryRel =
|
|
6880
|
-
const entryDest =
|
|
7031
|
+
const entrySrc = path29.join(src, entry.name);
|
|
7032
|
+
const entryRel = path29.join(rel, entry.name);
|
|
7033
|
+
const entryDest = path29.join(dataPath, entryRel);
|
|
6881
7034
|
if (entry.isDirectory()) {
|
|
6882
7035
|
ensureDir(entryDest);
|
|
6883
7036
|
syncFiles(entrySrc, entryRel);
|
|
@@ -6885,8 +7038,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6885
7038
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
6886
7039
|
backupFile(entryDest);
|
|
6887
7040
|
}
|
|
6888
|
-
ensureDir(
|
|
6889
|
-
|
|
7041
|
+
ensureDir(path29.dirname(entryDest));
|
|
7042
|
+
fs28.copyFileSync(entrySrc, entryDest);
|
|
6890
7043
|
updatedFiles.push(entryRel);
|
|
6891
7044
|
}
|
|
6892
7045
|
}
|
|
@@ -6897,12 +7050,12 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6897
7050
|
DriftService.saveManifest(dataPath, manifest);
|
|
6898
7051
|
}
|
|
6899
7052
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
6900
|
-
ensureDir(
|
|
6901
|
-
ensureDir(
|
|
6902
|
-
copyDirRecursive(
|
|
6903
|
-
copyDirRecursive(
|
|
6904
|
-
if (
|
|
6905
|
-
const configContent =
|
|
7053
|
+
ensureDir(path29.join(rrceHome, "templates"));
|
|
7054
|
+
ensureDir(path29.join(rrceHome, "docs"));
|
|
7055
|
+
copyDirRecursive(path29.join(agentCoreDir, "templates"), path29.join(rrceHome, "templates"));
|
|
7056
|
+
copyDirRecursive(path29.join(agentCoreDir, "docs"), path29.join(rrceHome, "docs"));
|
|
7057
|
+
if (fs28.existsSync(configFilePath)) {
|
|
7058
|
+
const configContent = fs28.readFileSync(configFilePath, "utf-8");
|
|
6906
7059
|
if (configContent.includes("copilot: true")) {
|
|
6907
7060
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
6908
7061
|
ensureDir(copilotPath);
|
|
@@ -6924,21 +7077,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
6924
7077
|
try {
|
|
6925
7078
|
const yaml = parse(configContent);
|
|
6926
7079
|
yaml.last_synced_version = runningVersion;
|
|
6927
|
-
|
|
7080
|
+
fs28.writeFileSync(configFilePath, stringify2(yaml));
|
|
6928
7081
|
} catch (e) {
|
|
6929
7082
|
console.error("Failed to update config.yaml version:", e);
|
|
6930
7083
|
}
|
|
6931
7084
|
}
|
|
6932
|
-
const mcpPath =
|
|
6933
|
-
if (
|
|
7085
|
+
const mcpPath = path29.join(rrceHome, "mcp.yaml");
|
|
7086
|
+
if (fs28.existsSync(mcpPath)) {
|
|
6934
7087
|
try {
|
|
6935
|
-
const content =
|
|
7088
|
+
const content = fs28.readFileSync(mcpPath, "utf-8");
|
|
6936
7089
|
const yaml = parse(content);
|
|
6937
7090
|
if (yaml.projects) {
|
|
6938
7091
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
6939
7092
|
if (project) {
|
|
6940
7093
|
project.last_synced_version = runningVersion;
|
|
6941
|
-
|
|
7094
|
+
fs28.writeFileSync(mcpPath, stringify2(yaml));
|
|
6942
7095
|
}
|
|
6943
7096
|
}
|
|
6944
7097
|
} catch (e) {
|
|
@@ -6974,9 +7127,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
6974
7127
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
6975
7128
|
const configFilePath = getConfigPath(workspacePath);
|
|
6976
7129
|
let currentSyncedVersion;
|
|
6977
|
-
if (
|
|
7130
|
+
if (fs28.existsSync(configFilePath)) {
|
|
6978
7131
|
try {
|
|
6979
|
-
const content =
|
|
7132
|
+
const content = fs28.readFileSync(configFilePath, "utf-8");
|
|
6980
7133
|
const config = parse(content);
|
|
6981
7134
|
currentSyncedVersion = config.last_synced_version;
|
|
6982
7135
|
} catch (e) {
|
|
@@ -7000,8 +7153,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7000
7153
|
` \u2022 docs/ (documentation)`
|
|
7001
7154
|
];
|
|
7002
7155
|
const ideTargets = [];
|
|
7003
|
-
if (
|
|
7004
|
-
const configContent =
|
|
7156
|
+
if (fs28.existsSync(configFilePath)) {
|
|
7157
|
+
const configContent = fs28.readFileSync(configFilePath, "utf-8");
|
|
7005
7158
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7006
7159
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7007
7160
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7030,14 +7183,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7030
7183
|
const dirs = ["templates", "prompts", "docs"];
|
|
7031
7184
|
const updatedFiles = [];
|
|
7032
7185
|
for (const dir of dirs) {
|
|
7033
|
-
const srcDir =
|
|
7034
|
-
if (!
|
|
7186
|
+
const srcDir = path29.join(agentCoreDir, dir);
|
|
7187
|
+
if (!fs28.existsSync(srcDir)) continue;
|
|
7035
7188
|
const syncFiles = (src, rel) => {
|
|
7036
|
-
const entries =
|
|
7189
|
+
const entries = fs28.readdirSync(src, { withFileTypes: true });
|
|
7037
7190
|
for (const entry of entries) {
|
|
7038
|
-
const entrySrc =
|
|
7039
|
-
const entryRel =
|
|
7040
|
-
const entryDest =
|
|
7191
|
+
const entrySrc = path29.join(src, entry.name);
|
|
7192
|
+
const entryRel = path29.join(rel, entry.name);
|
|
7193
|
+
const entryDest = path29.join(dataPath, entryRel);
|
|
7041
7194
|
if (entry.isDirectory()) {
|
|
7042
7195
|
ensureDir(entryDest);
|
|
7043
7196
|
syncFiles(entrySrc, entryRel);
|
|
@@ -7045,8 +7198,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7045
7198
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
7046
7199
|
backupFile(entryDest);
|
|
7047
7200
|
}
|
|
7048
|
-
ensureDir(
|
|
7049
|
-
|
|
7201
|
+
ensureDir(path29.dirname(entryDest));
|
|
7202
|
+
fs28.copyFileSync(entrySrc, entryDest);
|
|
7050
7203
|
updatedFiles.push(entryRel);
|
|
7051
7204
|
}
|
|
7052
7205
|
}
|
|
@@ -7057,12 +7210,12 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7057
7210
|
DriftService.saveManifest(dataPath, manifest);
|
|
7058
7211
|
}
|
|
7059
7212
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
7060
|
-
ensureDir(
|
|
7061
|
-
ensureDir(
|
|
7062
|
-
copyDirRecursive(
|
|
7063
|
-
copyDirRecursive(
|
|
7064
|
-
if (
|
|
7065
|
-
const configContent =
|
|
7213
|
+
ensureDir(path29.join(rrceHome, "templates"));
|
|
7214
|
+
ensureDir(path29.join(rrceHome, "docs"));
|
|
7215
|
+
copyDirRecursive(path29.join(agentCoreDir, "templates"), path29.join(rrceHome, "templates"));
|
|
7216
|
+
copyDirRecursive(path29.join(agentCoreDir, "docs"), path29.join(rrceHome, "docs"));
|
|
7217
|
+
if (fs28.existsSync(configFilePath)) {
|
|
7218
|
+
const configContent = fs28.readFileSync(configFilePath, "utf-8");
|
|
7066
7219
|
if (configContent.includes("copilot: true")) {
|
|
7067
7220
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7068
7221
|
ensureDir(copilotPath);
|
|
@@ -7084,21 +7237,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7084
7237
|
try {
|
|
7085
7238
|
const yaml = parse(configContent);
|
|
7086
7239
|
yaml.last_synced_version = runningVersion;
|
|
7087
|
-
|
|
7240
|
+
fs28.writeFileSync(configFilePath, stringify2(yaml));
|
|
7088
7241
|
} catch (e) {
|
|
7089
7242
|
console.error("Failed to update config.yaml version:", e);
|
|
7090
7243
|
}
|
|
7091
7244
|
}
|
|
7092
|
-
const mcpPath =
|
|
7093
|
-
if (
|
|
7245
|
+
const mcpPath = path29.join(rrceHome, "mcp.yaml");
|
|
7246
|
+
if (fs28.existsSync(mcpPath)) {
|
|
7094
7247
|
try {
|
|
7095
|
-
const content =
|
|
7248
|
+
const content = fs28.readFileSync(mcpPath, "utf-8");
|
|
7096
7249
|
const yaml = parse(content);
|
|
7097
7250
|
if (yaml.projects) {
|
|
7098
7251
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7099
7252
|
if (project) {
|
|
7100
7253
|
project.last_synced_version = runningVersion;
|
|
7101
|
-
|
|
7254
|
+
fs28.writeFileSync(mcpPath, stringify2(yaml));
|
|
7102
7255
|
}
|
|
7103
7256
|
}
|
|
7104
7257
|
} catch (e) {
|
|
@@ -7133,8 +7286,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7133
7286
|
}
|
|
7134
7287
|
}
|
|
7135
7288
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
7136
|
-
const globalPath =
|
|
7137
|
-
const workspacePath =
|
|
7289
|
+
const globalPath = path29.join(customGlobalPath, "workspaces", workspaceName);
|
|
7290
|
+
const workspacePath = path29.join(workspaceRoot, ".rrce-workflow");
|
|
7138
7291
|
switch (mode) {
|
|
7139
7292
|
case "global":
|
|
7140
7293
|
return [globalPath];
|
|
@@ -7157,8 +7310,8 @@ var init_update_flow = __esm({
|
|
|
7157
7310
|
// src/commands/wizard/index.ts
|
|
7158
7311
|
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";
|
|
7159
7312
|
import pc13 from "picocolors";
|
|
7160
|
-
import * as
|
|
7161
|
-
import * as
|
|
7313
|
+
import * as fs32 from "fs";
|
|
7314
|
+
import * as path31 from "path";
|
|
7162
7315
|
import { parse as parse2 } from "yaml";
|
|
7163
7316
|
|
|
7164
7317
|
// src/lib/git.ts
|
|
@@ -7835,7 +7988,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
7835
7988
|
init_paths();
|
|
7836
7989
|
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";
|
|
7837
7990
|
import pc10 from "picocolors";
|
|
7838
|
-
import * as
|
|
7991
|
+
import * as fs29 from "fs";
|
|
7839
7992
|
init_detection();
|
|
7840
7993
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
7841
7994
|
const projects = scanForProjects({
|
|
@@ -7874,7 +8027,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
7874
8027
|
const s = spinner4();
|
|
7875
8028
|
s.start("Linking projects");
|
|
7876
8029
|
const configFilePath = getConfigPath(workspacePath);
|
|
7877
|
-
let configContent =
|
|
8030
|
+
let configContent = fs29.readFileSync(configFilePath, "utf-8");
|
|
7878
8031
|
if (configContent.includes("linked_projects:")) {
|
|
7879
8032
|
const lines = configContent.split("\n");
|
|
7880
8033
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -7901,7 +8054,7 @@ linked_projects:
|
|
|
7901
8054
|
`;
|
|
7902
8055
|
});
|
|
7903
8056
|
}
|
|
7904
|
-
|
|
8057
|
+
fs29.writeFileSync(configFilePath, configContent);
|
|
7905
8058
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
7906
8059
|
s.stop("Projects linked");
|
|
7907
8060
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -7937,15 +8090,15 @@ init_paths();
|
|
|
7937
8090
|
init_utils();
|
|
7938
8091
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
7939
8092
|
import pc11 from "picocolors";
|
|
7940
|
-
import * as
|
|
7941
|
-
import * as
|
|
8093
|
+
import * as fs30 from "fs";
|
|
8094
|
+
import * as path30 from "path";
|
|
7942
8095
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
7943
8096
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
7944
8097
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
7945
|
-
const globalPath =
|
|
8098
|
+
const globalPath = path30.join(customGlobalPath, "workspaces", workspaceName);
|
|
7946
8099
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
7947
8100
|
const existingDirs = subdirs.filter(
|
|
7948
|
-
(dir) =>
|
|
8101
|
+
(dir) => fs30.existsSync(path30.join(localPath, dir))
|
|
7949
8102
|
);
|
|
7950
8103
|
if (existingDirs.length === 0) {
|
|
7951
8104
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -7971,8 +8124,8 @@ Destination: ${pc11.cyan(globalPath)}`,
|
|
|
7971
8124
|
try {
|
|
7972
8125
|
ensureDir(globalPath);
|
|
7973
8126
|
for (const dir of existingDirs) {
|
|
7974
|
-
const srcDir =
|
|
7975
|
-
const destDir =
|
|
8127
|
+
const srcDir = path30.join(localPath, dir);
|
|
8128
|
+
const destDir = path30.join(globalPath, dir);
|
|
7976
8129
|
ensureDir(destDir);
|
|
7977
8130
|
copyDirRecursive(srcDir, destDir);
|
|
7978
8131
|
}
|
|
@@ -8000,7 +8153,7 @@ init_update_flow();
|
|
|
8000
8153
|
// src/commands/wizard/delete-flow.ts
|
|
8001
8154
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
8002
8155
|
import pc12 from "picocolors";
|
|
8003
|
-
import * as
|
|
8156
|
+
import * as fs31 from "fs";
|
|
8004
8157
|
init_detection();
|
|
8005
8158
|
init_config();
|
|
8006
8159
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -8044,8 +8197,8 @@ Are you sure?`,
|
|
|
8044
8197
|
for (const projectName of projectsToDelete) {
|
|
8045
8198
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
8046
8199
|
if (!project) continue;
|
|
8047
|
-
if (
|
|
8048
|
-
|
|
8200
|
+
if (fs31.existsSync(project.dataPath)) {
|
|
8201
|
+
fs31.rmSync(project.dataPath, { recursive: true, force: true });
|
|
8049
8202
|
}
|
|
8050
8203
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
8051
8204
|
configChanged = true;
|
|
@@ -8067,9 +8220,9 @@ init_config();
|
|
|
8067
8220
|
function getPackageVersion3() {
|
|
8068
8221
|
try {
|
|
8069
8222
|
const agentCoreDir = getAgentCoreDir();
|
|
8070
|
-
const packageJsonPath =
|
|
8071
|
-
if (
|
|
8072
|
-
return JSON.parse(
|
|
8223
|
+
const packageJsonPath = path31.join(path31.dirname(agentCoreDir), "package.json");
|
|
8224
|
+
if (fs32.existsSync(packageJsonPath)) {
|
|
8225
|
+
return JSON.parse(fs32.readFileSync(packageJsonPath, "utf8")).version;
|
|
8073
8226
|
}
|
|
8074
8227
|
} catch (e) {
|
|
8075
8228
|
}
|
|
@@ -8077,9 +8230,9 @@ function getPackageVersion3() {
|
|
|
8077
8230
|
}
|
|
8078
8231
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
8079
8232
|
const configFilePath = getConfigPath(workspacePath);
|
|
8080
|
-
if (
|
|
8233
|
+
if (fs32.existsSync(configFilePath)) {
|
|
8081
8234
|
try {
|
|
8082
|
-
const content =
|
|
8235
|
+
const content = fs32.readFileSync(configFilePath, "utf-8");
|
|
8083
8236
|
const config = parse2(content);
|
|
8084
8237
|
if (config.last_synced_version) {
|
|
8085
8238
|
return config.last_synced_version;
|
|
@@ -8088,10 +8241,10 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
8088
8241
|
}
|
|
8089
8242
|
}
|
|
8090
8243
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
8091
|
-
const mcpPath =
|
|
8092
|
-
if (
|
|
8244
|
+
const mcpPath = path31.join(rrceHome, "mcp.yaml");
|
|
8245
|
+
if (fs32.existsSync(mcpPath)) {
|
|
8093
8246
|
try {
|
|
8094
|
-
const content =
|
|
8247
|
+
const content = fs32.readFileSync(mcpPath, "utf-8");
|
|
8095
8248
|
const config = parse2(content);
|
|
8096
8249
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
8097
8250
|
if (project?.last_synced_version) {
|
|
@@ -8161,11 +8314,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8161
8314
|
workspacePath
|
|
8162
8315
|
});
|
|
8163
8316
|
const configFilePath = getConfigPath(workspacePath);
|
|
8164
|
-
let isAlreadyConfigured =
|
|
8317
|
+
let isAlreadyConfigured = fs32.existsSync(configFilePath);
|
|
8165
8318
|
let currentStorageMode = null;
|
|
8166
8319
|
if (isAlreadyConfigured) {
|
|
8167
8320
|
try {
|
|
8168
|
-
const configContent =
|
|
8321
|
+
const configContent = fs32.readFileSync(configFilePath, "utf-8");
|
|
8169
8322
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
8170
8323
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
8171
8324
|
} catch {
|
|
@@ -8182,7 +8335,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8182
8335
|
}
|
|
8183
8336
|
}
|
|
8184
8337
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
8185
|
-
const hasLocalData =
|
|
8338
|
+
const hasLocalData = fs32.existsSync(localDataPath);
|
|
8186
8339
|
if (isAlreadyConfigured) {
|
|
8187
8340
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
8188
8341
|
if (!continueToMenu) {
|