rrce-workflow 0.3.30 → 0.3.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -45
- package/dist/index.js +233 -117
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,8 +11,10 @@ RRCE-Workflow transforms your AI coding assistant (GitHub Copilot, OpenCode, Cla
|
|
|
11
11
|
- **Global Knowledge Base**: Centralized context management across all your projects (`~/.rrce-workflow/`).
|
|
12
12
|
- **MCP Hub**: A Model Context Protocol server exposing tools, resources, and prompts to any MCP-compatible client.
|
|
13
13
|
- **Semantic Search (RAG)**: Local, privacy-first vector indexing powered by `@xenova/transformers` for deep codebase understanding.
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
14
|
+
- **4-Phase Workflow**: Init → Design (research+planning merged) → Develop → Document for streamlined development.
|
|
15
|
+
- **Slash Commands**: In-context execution (`/rrce_*`) with ~60% token efficiency over subagent delegation.
|
|
16
|
+
- **Task Management**: Built-in CRUD operations for tracking high-level tasks via MCP tools, including knowledge extraction and cleanup.
|
|
17
|
+
- **Agent Session Tracking**: Real-time task progress visualization in MCP TUI with OpenCode Todo sidebar sync.
|
|
16
18
|
|
|
17
19
|
---
|
|
18
20
|
|
|
@@ -71,14 +73,19 @@ RRCE-Workflow uses the [Model Context Protocol](https://modelcontextprotocol.io/
|
|
|
71
73
|
| `search_knowledge` | Semantic search across project knowledge bases |
|
|
72
74
|
| `search_code` | Semantic search across code files (snippets + line numbers + context) |
|
|
73
75
|
| `find_related_files` | Find imports/imported-by relationships for a file |
|
|
74
|
-
| `index_knowledge` | Start (or query) the semantic indexing job for a project |
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `create_task` | Create a task |
|
|
80
|
-
| `update_task` | Update a task (`meta.json`) |
|
|
76
|
+
| `index_knowledge` | Start (or query) the semantic indexing job for a project. Supports `force` (re-hash) and `clean` (wipe/rebuild) parameters |
|
|
77
|
+
| `list_tasks` | List all tasks for a project (supports filtering by status, keyword, date) |
|
|
78
|
+
| `get_task` | Get details of a task (including phase status, checklist, metadata) |
|
|
79
|
+
| `create_task` | Create a new task in the project |
|
|
80
|
+
| `update_task` | Update task metadata (`meta.json`) |
|
|
81
81
|
| `delete_task` | Delete a task |
|
|
82
|
+
| `search_tasks` | Search tasks by keyword, status, agent phase, or date |
|
|
83
|
+
| `validate_phase` | Check if a task phase has all prerequisites complete |
|
|
84
|
+
| `cleanup_task` | Extract valuable knowledge from tasks and delete artifacts. Supports single, bulk, or --all mode |
|
|
85
|
+
| `start_session` | Start an agent session for active task tracking (TUI visibility) |
|
|
86
|
+
| `end_session` | End an agent session before completion signal |
|
|
87
|
+
| `update_agent_todos` | Update agent todo list for granular work tracking (TUI display) |
|
|
88
|
+
| `get_agent_prompt` | Get the system prompt for a specific agent or slash command |
|
|
82
89
|
|
|
83
90
|
### Connecting Your IDE
|
|
84
91
|
|
|
@@ -103,14 +110,15 @@ RRCE-Workflow integrates with OpenCode both as an MCP server and by providing a
|
|
|
103
110
|
```
|
|
104
111
|
|
|
105
112
|
2. **Install Agents**: Run `npx rrce-workflow` and select **OpenCode** as a tool. This generates:
|
|
106
|
-
- **Primary Agent (`rrce`)**:
|
|
107
|
-
- **Subagents** (`@rrce_*`): Specialized agents for
|
|
113
|
+
- **Primary Agent (`rrce`)**: Phase Coordinator orchestrating the complete workflow (tab-switchable)
|
|
114
|
+
- **Subagents** (`@rrce_*`): Specialized agents for isolated execution (expert mode)
|
|
108
115
|
- **Auto-configuration**: Hides OpenCode's native plan agent to avoid confusion
|
|
109
116
|
|
|
110
117
|
3. **Usage**:
|
|
111
118
|
- Press `Tab` to cycle to the RRCE agent for structured workflows
|
|
119
|
+
- Use slash commands (`/rrce_init`, `/rrce_design`, `/rrce_develop`) for in-context execution (60% more efficient)
|
|
120
|
+
- Direct subagent access via `@rrce_init`, `@rrce_design`, etc. for isolated execution
|
|
112
121
|
- Build agent can automatically delegate to RRCE for complex tasks
|
|
113
|
-
- Direct subagent access via `@rrce_init`, `@rrce_research`, etc.
|
|
114
122
|
|
|
115
123
|
See [OpenCode Guide](docs/opencode-guide.md) for detailed usage instructions.
|
|
116
124
|
|
|
@@ -179,45 +187,65 @@ Stores everything in a `.rrce-workflow` folder inside your project root.
|
|
|
179
187
|
|
|
180
188
|
---
|
|
181
189
|
|
|
182
|
-
##
|
|
190
|
+
## 4-Phase Workflow
|
|
183
191
|
|
|
184
|
-
RRCE
|
|
192
|
+
RRCE uses a streamlined 4-phase pipeline for end-to-end development:
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
```
|
|
195
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
196
|
+
│ 1. Init → 2. Design → 3. Develop → 4. Document │
|
|
197
|
+
│ /rrce_init /rrce_design /rrce_develop /rrce_docs │
|
|
198
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Phase | Slash Command | Purpose | Prerequisite |
|
|
202
|
+
|-------|---------------|---------|--------------|
|
|
203
|
+
| **Init** | `/rrce_init` | Project setup, context extraction, semantic indexing | None |
|
|
204
|
+
| **Design** | `/rrce_design task-slug "request"` | Research + Planning (merged for efficiency) | Init complete |
|
|
205
|
+
| **Develop** | `/rrce_develop task-slug` | Code implementation based on approved plan | Design complete |
|
|
206
|
+
| **Document** | `/rrce_docs task-slug` | Generate/update documentation | Develop complete |
|
|
187
207
|
|
|
188
|
-
|
|
208
|
+
### Slash Commands (In-Context Execution)
|
|
189
209
|
|
|
190
|
-
- **
|
|
191
|
-
- **Purpose**: Automatically coordinate research → planning → execution → documentation
|
|
192
|
-
- **Delegation**: Build agent can delegate to RRCE for complex tasks
|
|
193
|
-
- **Benefits**: Results flow back to calling agents, preventing hallucination
|
|
210
|
+
The primary interaction model is **in-context slash commands** (`/rrce_*`), which achieve a **60% token reduction** compared to subagent delegation.
|
|
194
211
|
|
|
195
|
-
|
|
212
|
+
| Command | Arguments | Purpose |
|
|
213
|
+
|---------|-----------|---------|
|
|
214
|
+
| `/rrce_init` | `[project-name]` | Initialize project context and semantic index |
|
|
215
|
+
| `/rrce_design` | `task-slug "request"` | Research and plan in single session |
|
|
216
|
+
| `/rrce_develop` | `task-slug` | Execute code implementation |
|
|
217
|
+
| `/rrce_docs` | `doc-type [task-slug]` | Generate documentation |
|
|
218
|
+
| `/rrce_cleanup` | `task-slug` \| `--all` | Extract knowledge and delete tasks |
|
|
219
|
+
| `/rrce_sync` | `[scope]` | Sync knowledge base with codebase |
|
|
220
|
+
| `/rrce_doctor` | `[focus-area]` | Analyze codebase health |
|
|
196
221
|
|
|
197
|
-
|
|
222
|
+
### Subagents (Isolated Execution)
|
|
223
|
+
|
|
224
|
+
For fully autonomous, non-interactive execution, use subagents via `@mentions`:
|
|
198
225
|
|
|
199
226
|
| Agent | Invoke With | Purpose | Key Arguments |
|
|
200
227
|
|-------|-------------|---------|---------------|
|
|
201
|
-
| **Init** | `@rrce_init` | Analyze codebase, establish project context
|
|
202
|
-
| **
|
|
203
|
-
| **
|
|
204
|
-
| **
|
|
205
|
-
| **Docs** | `@rrce_documentation` | Generate project documentation (API, architecture, changelog) | `DOC_TYPE`, `TASK_SLUG` |
|
|
228
|
+
| **Init** | `@rrce_init` | Analyze codebase, establish project context | `PROJECT_NAME` (optional) |
|
|
229
|
+
| **Design** | `@rrce_design` | Research + planning for isolated execution | `TASK_SLUG`, `REQUEST` |
|
|
230
|
+
| **Develop** | `@rrce_develop` | Implement the plan - ONLY agent authorized to modify code | `TASK_SLUG` |
|
|
231
|
+
| **Docs** | `@rrce_docs` | Generate project documentation | `DOC_TYPE`, `TASK_SLUG` |
|
|
206
232
|
| **Sync** | `@rrce_sync` | Reconcile knowledge base with current codebase state | `SCOPE` (optional) |
|
|
207
|
-
| **Doctor** | `@rrce_doctor` | Analyze codebase health,
|
|
233
|
+
| **Doctor** | `@rrce_doctor` | Analyze codebase health, recommend improvements | `PROJECT_NAME`, `FOCUS_AREA` |
|
|
234
|
+
|
|
235
|
+
### OpenCode Integration
|
|
208
236
|
|
|
209
|
-
|
|
237
|
+
OpenCode provides specialized UX optimizations:
|
|
210
238
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
| **Subagents** | Expert control, specific phase needed | `@rrce_executor TASK_SLUG=user-auth` → Direct execution |
|
|
215
|
-
| **Build Delegation** | Want build's help but need structure | Stay in build: "Implement caching" → Build delegates to RRCE |
|
|
239
|
+
- **Tool Name Stabilization**: Standard tools (`read`, `write`) use no `rrce_` prefix, aligning with native IDE capabilities
|
|
240
|
+
- **Checklist Sync**: Agents automatically push their task checklist to the OpenCode Todo sidebar
|
|
241
|
+
- **Hybrid Delegation**: Orchestrator uses a mix of `@mention` text and interactive confirmation suggestions
|
|
216
242
|
|
|
217
243
|
### Recommended Workflow
|
|
218
|
-
1.
|
|
219
|
-
2.
|
|
220
|
-
3.
|
|
244
|
+
1. **`/rrce_init`**: "Analyze this codebase." → Creates `project-context.md` and semantic index
|
|
245
|
+
2. **`/rrce_design my-feature "Add user authentication"`**: Research + planning in one session
|
|
246
|
+
3. **`/rrce_develop my-feature`**: Execute the implementation
|
|
247
|
+
4. **`/rrce_docs my-feature`**: Generate/update documentation
|
|
248
|
+
5. **`/rrce_cleanup my-feature`**: (Optional) Extract insights and delete task artifacts
|
|
221
249
|
|
|
222
250
|
---
|
|
223
251
|
|
|
@@ -226,23 +254,59 @@ For expert control, invoke subagents directly via your AI assistant or MCP tools
|
|
|
226
254
|
RRCE-Workflow includes a local, embedding-based search engine powered by `@xenova/transformers`.
|
|
227
255
|
|
|
228
256
|
- **Privacy First**: All embeddings are calculated locally. No code leaves your machine.
|
|
229
|
-
- **Full Codebase Indexing**: The `index_knowledge` tool scans your entire source tree (respecting
|
|
257
|
+
- **Full Codebase Indexing**: The `index_knowledge` tool scans your entire source tree (respecting `.gitignore` rules).
|
|
258
|
+
- **Background Jobs**: Non-blocking indexing with progress tracking via the MCP Dashboard.
|
|
259
|
+
- **Automatic Cleanup**: DriftService detects and removes embeddings for deleted files during reindexing.
|
|
260
|
+
- **Dual Index**: Separate indices for knowledge (`embeddings.json`) and code (`code-embeddings.json`).
|
|
230
261
|
- **Smart Fallback**: If RAG fails or isn't enabled, `search_knowledge` performs line-by-line text matching.
|
|
231
262
|
- **Model**: Uses `Xenova/all-MiniLM-L6-v2` by default (configurable per-project).
|
|
232
263
|
|
|
264
|
+
### Reindexing Guidance
|
|
265
|
+
|
|
266
|
+
| Scenario | Tool Argument | Rationale |
|
|
267
|
+
|----------|---------------|-----------|
|
|
268
|
+
| Routine updates | `{ "project": "name" }` | Incremental (fastest). Only updates changed files |
|
|
269
|
+
| Major refactors | `{ "project": "name", "force": true }` | Forces re-calculation of hashes for all files without wiping |
|
|
270
|
+
| Corrupt index / Stale vectors | `{ "project": "name", "clean": true }` | Wipes index files and rebuilds from scratch. Resolves vector drift |
|
|
271
|
+
|
|
233
272
|
RAG is enabled by default in Express Setup. You can toggle it per-project in the MCP Dashboard or via `config.yaml`.
|
|
234
273
|
|
|
235
274
|
---
|
|
236
275
|
|
|
237
|
-
##
|
|
276
|
+
## MCP Dashboard (TUI)
|
|
277
|
+
|
|
278
|
+
The **MCP Dashboard** provides a cockpit-style interface for managing your RRCE workflow:
|
|
238
279
|
|
|
239
|
-
|
|
280
|
+
### Tabs Overview
|
|
281
|
+
1. **Overview (System Cockpit)**: Dashboard snapshot of server health, recent activity, and active task tracking
|
|
282
|
+
2. **Logs**: Real-time tailing of the MCP hub server logs
|
|
283
|
+
3. **Tasks**: Priority view for task management with current project auto-pinned and expanded
|
|
284
|
+
4. **Projects**: Configuration hub for project exposure with real-time indexing progress (indented row)
|
|
285
|
+
|
|
286
|
+
### Key Features
|
|
287
|
+
- **Unified Cockpit Aesthetic**: White borders, high-density information display
|
|
288
|
+
- **Active Task Tracking**: Real-time progress visualization with phase indicators
|
|
289
|
+
- **Session Management**: Agent todo list display showing granular work items
|
|
290
|
+
- **Project Prioritization**: Current workspace automatically pinned and expanded in Tasks tab
|
|
291
|
+
- **Background Indexing**: Non-blocking indexing with progress reporting in Projects tab
|
|
292
|
+
|
|
293
|
+
## 🛠 AI Agent Effectiveness & Code Health
|
|
240
294
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
- **
|
|
245
|
-
- **
|
|
295
|
+
The codebase has been optimized for **highly navigatable AI coding agents**:
|
|
296
|
+
|
|
297
|
+
### Codebase Optimizations
|
|
298
|
+
- **Modular Architecture**: Domain-specific tool handlers split into separate files (`tools/project.ts`, `tools/task.ts`, etc.)
|
|
299
|
+
- **Component Refactoring**: TUI views split into modular sub-components (<200 LOC per file)
|
|
300
|
+
- **Type Safety**: Strict typing with `TaskMeta`, `AgentInfo`, and `DetectedProject` interfaces (no `any` types)
|
|
301
|
+
- **Efficiency**: ~65% token reduction via prompt condensation, session reuse, and hybrid research
|
|
302
|
+
- **Testing**: 207 tests passing across 18 test files using Vitest
|
|
303
|
+
|
|
304
|
+
### AI Agent Guide (AGENTS.md)
|
|
305
|
+
All AI coding agents MUST consult `AGENTS.md` for technical ground truth, including:
|
|
306
|
+
- Build/test commands (dev mode: `npm run dev`, test: `npm test`)
|
|
307
|
+
- Naming conventions (kebab-case files, camelCase functions, PascalCase classes)
|
|
308
|
+
- Implementation patterns (`WorkflowError` error handling, SSOT principles)
|
|
309
|
+
- MCP & RAG patterns (background jobs, semantic search)
|
|
246
310
|
|
|
247
311
|
---
|
|
248
312
|
|
package/dist/index.js
CHANGED
|
@@ -1365,15 +1365,90 @@ var init_config_utils = __esm({
|
|
|
1365
1365
|
}
|
|
1366
1366
|
});
|
|
1367
1367
|
|
|
1368
|
+
// src/mcp/config-service.ts
|
|
1369
|
+
import { EventEmitter } from "events";
|
|
1370
|
+
function onConfigChange(listener) {
|
|
1371
|
+
configEmitter.on("change", listener);
|
|
1372
|
+
}
|
|
1373
|
+
function offConfigChange(listener) {
|
|
1374
|
+
configEmitter.off("change", listener);
|
|
1375
|
+
}
|
|
1376
|
+
var configEmitter, MCPConfigService, configService;
|
|
1377
|
+
var init_config_service = __esm({
|
|
1378
|
+
"src/mcp/config-service.ts"() {
|
|
1379
|
+
"use strict";
|
|
1380
|
+
init_config();
|
|
1381
|
+
configEmitter = new EventEmitter();
|
|
1382
|
+
MCPConfigService = class {
|
|
1383
|
+
cache = null;
|
|
1384
|
+
cacheTime = 0;
|
|
1385
|
+
TTL = 5e3;
|
|
1386
|
+
// 5 seconds cache TTL
|
|
1387
|
+
/**
|
|
1388
|
+
* Load config with caching
|
|
1389
|
+
* Returns cached version if still valid, otherwise reads from disk
|
|
1390
|
+
*/
|
|
1391
|
+
load() {
|
|
1392
|
+
const now = Date.now();
|
|
1393
|
+
if (this.cache && now - this.cacheTime < this.TTL) {
|
|
1394
|
+
return this.cache;
|
|
1395
|
+
}
|
|
1396
|
+
this.cache = loadMCPConfig();
|
|
1397
|
+
this.cacheTime = now;
|
|
1398
|
+
return this.cache;
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Save config, update cache, and emit change event
|
|
1402
|
+
*/
|
|
1403
|
+
save(config) {
|
|
1404
|
+
const oldConfig = this.cache;
|
|
1405
|
+
saveMCPConfig(config);
|
|
1406
|
+
this.cache = config;
|
|
1407
|
+
this.cacheTime = Date.now();
|
|
1408
|
+
configEmitter.emit("change", oldConfig || loadMCPConfig(), config);
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Invalidate cache, forcing next load to read from disk
|
|
1412
|
+
*/
|
|
1413
|
+
invalidate() {
|
|
1414
|
+
this.cache = null;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Check if cache is currently valid
|
|
1418
|
+
*/
|
|
1419
|
+
isCacheValid() {
|
|
1420
|
+
return this.cache !== null && Date.now() - this.cacheTime < this.TTL;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Get current cache age in milliseconds
|
|
1424
|
+
*/
|
|
1425
|
+
getCacheAge() {
|
|
1426
|
+
return this.cache ? Date.now() - this.cacheTime : Infinity;
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Get the config file path
|
|
1430
|
+
*/
|
|
1431
|
+
getConfigPath() {
|
|
1432
|
+
return getMCPConfigPath();
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
configService = new MCPConfigService();
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1368
1439
|
// src/mcp/config.ts
|
|
1369
1440
|
var config_exports = {};
|
|
1370
1441
|
__export(config_exports, {
|
|
1371
1442
|
cleanStaleProjects: () => cleanStaleProjects,
|
|
1443
|
+
configEmitter: () => configEmitter,
|
|
1444
|
+
configService: () => configService,
|
|
1372
1445
|
ensureMCPGlobalPath: () => ensureMCPGlobalPath,
|
|
1373
1446
|
getMCPConfigPath: () => getMCPConfigPath,
|
|
1374
1447
|
getProjectPermissions: () => getProjectPermissions,
|
|
1375
1448
|
isProjectExposed: () => isProjectExposed,
|
|
1376
1449
|
loadMCPConfig: () => loadMCPConfig,
|
|
1450
|
+
offConfigChange: () => offConfigChange,
|
|
1451
|
+
onConfigChange: () => onConfigChange,
|
|
1377
1452
|
removeProjectConfig: () => removeProjectConfig,
|
|
1378
1453
|
saveMCPConfig: () => saveMCPConfig,
|
|
1379
1454
|
setProjectConfig: () => setProjectConfig
|
|
@@ -1560,6 +1635,8 @@ var init_config = __esm({
|
|
|
1560
1635
|
init_paths();
|
|
1561
1636
|
init_types();
|
|
1562
1637
|
init_config_utils();
|
|
1638
|
+
init_config_service();
|
|
1639
|
+
init_config_service();
|
|
1563
1640
|
}
|
|
1564
1641
|
});
|
|
1565
1642
|
|
|
@@ -1815,7 +1892,7 @@ var init_utils2 = __esm({
|
|
|
1815
1892
|
import * as fs15 from "fs";
|
|
1816
1893
|
import * as path17 from "path";
|
|
1817
1894
|
function resolveProjectPaths(project, pathInput) {
|
|
1818
|
-
const config =
|
|
1895
|
+
const config = configService.load();
|
|
1819
1896
|
let workspaceRoot = pathInput;
|
|
1820
1897
|
let workspaceName = project;
|
|
1821
1898
|
if (!workspaceRoot && project) {
|
|
@@ -1886,7 +1963,7 @@ var init_paths2 = __esm({
|
|
|
1886
1963
|
import * as fs16 from "fs";
|
|
1887
1964
|
import * as path18 from "path";
|
|
1888
1965
|
function getExposedProjects() {
|
|
1889
|
-
const config =
|
|
1966
|
+
const config = configService.load();
|
|
1890
1967
|
const knownProjects = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
1891
1968
|
const allProjects = projectService.scan({ knownProjects });
|
|
1892
1969
|
const activeProject = detectActiveProject(allProjects);
|
|
@@ -1933,7 +2010,7 @@ function getExposedProjects() {
|
|
|
1933
2010
|
function detectActiveProject(knownProjects) {
|
|
1934
2011
|
let scanList = knownProjects;
|
|
1935
2012
|
if (!scanList) {
|
|
1936
|
-
const config =
|
|
2013
|
+
const config = configService.load();
|
|
1937
2014
|
const knownProjectsMap = config.projects.filter((p) => !!p.path).map((p) => ({ name: p.name, path: p.path }));
|
|
1938
2015
|
const all = projectService.scan({ knownProjects: knownProjectsMap });
|
|
1939
2016
|
scanList = all.filter((project) => isProjectExposed(config, project.name, project.sourcePath || project.path));
|
|
@@ -1941,7 +2018,7 @@ function detectActiveProject(knownProjects) {
|
|
|
1941
2018
|
return findClosestProject(scanList);
|
|
1942
2019
|
}
|
|
1943
2020
|
function getProjectContext(projectName) {
|
|
1944
|
-
const config =
|
|
2021
|
+
const config = configService.load();
|
|
1945
2022
|
const projects = projectService.scan();
|
|
1946
2023
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1947
2024
|
if (!project) {
|
|
@@ -1980,7 +2057,7 @@ import * as path19 from "path";
|
|
|
1980
2057
|
import * as os3 from "os";
|
|
1981
2058
|
import * as crypto from "crypto";
|
|
1982
2059
|
function getProjectTasks(projectName) {
|
|
1983
|
-
const config =
|
|
2060
|
+
const config = configService.load();
|
|
1984
2061
|
const projects = projectService.scan();
|
|
1985
2062
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
1986
2063
|
if (!project) {
|
|
@@ -2014,7 +2091,7 @@ function getProjectTasks(projectName) {
|
|
|
2014
2091
|
return tasks;
|
|
2015
2092
|
}
|
|
2016
2093
|
function getTask(projectName, taskSlug) {
|
|
2017
|
-
const config =
|
|
2094
|
+
const config = configService.load();
|
|
2018
2095
|
const projects = projectService.scan();
|
|
2019
2096
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2020
2097
|
if (!project || !project.tasksPath) return null;
|
|
@@ -2028,7 +2105,7 @@ function getTask(projectName, taskSlug) {
|
|
|
2028
2105
|
}
|
|
2029
2106
|
}
|
|
2030
2107
|
async function createTask(projectName, taskSlug, taskData) {
|
|
2031
|
-
const config =
|
|
2108
|
+
const config = configService.load();
|
|
2032
2109
|
const projects = projectService.scan();
|
|
2033
2110
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2034
2111
|
if (!project || !project.tasksPath) {
|
|
@@ -2083,7 +2160,7 @@ async function updateTask(projectName, taskSlug, taskData) {
|
|
|
2083
2160
|
workspace: meta.workspace
|
|
2084
2161
|
// Protect workspace metadata
|
|
2085
2162
|
};
|
|
2086
|
-
const config =
|
|
2163
|
+
const config = configService.load();
|
|
2087
2164
|
const projects = projectService.scan();
|
|
2088
2165
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2089
2166
|
if (!project || !project.tasksPath) return null;
|
|
@@ -2092,7 +2169,7 @@ async function updateTask(projectName, taskSlug, taskData) {
|
|
|
2092
2169
|
return updatedMeta;
|
|
2093
2170
|
}
|
|
2094
2171
|
function deleteTask(projectName, taskSlug) {
|
|
2095
|
-
const config =
|
|
2172
|
+
const config = configService.load();
|
|
2096
2173
|
const projects = projectService.scan();
|
|
2097
2174
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
2098
2175
|
if (!project || !project.tasksPath) return false;
|
|
@@ -3279,7 +3356,7 @@ var init_search_utils = __esm({
|
|
|
3279
3356
|
import * as fs20 from "fs";
|
|
3280
3357
|
import * as path22 from "path";
|
|
3281
3358
|
async function searchCode(query, projectFilter, limit = 10, options) {
|
|
3282
|
-
const config =
|
|
3359
|
+
const config = configService.load();
|
|
3283
3360
|
const projects = getExposedProjects();
|
|
3284
3361
|
const results = [];
|
|
3285
3362
|
for (const project of projects) {
|
|
@@ -3352,7 +3429,7 @@ async function searchCode(query, projectFilter, limit = 10, options) {
|
|
|
3352
3429
|
};
|
|
3353
3430
|
}
|
|
3354
3431
|
async function searchKnowledge(query, projectFilter, options) {
|
|
3355
|
-
const config =
|
|
3432
|
+
const config = configService.load();
|
|
3356
3433
|
const projects = getExposedProjects();
|
|
3357
3434
|
const results = [];
|
|
3358
3435
|
const queryLower = query.toLowerCase();
|
|
@@ -3439,7 +3516,7 @@ async function searchKnowledge(query, projectFilter, options) {
|
|
|
3439
3516
|
};
|
|
3440
3517
|
}
|
|
3441
3518
|
async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
3442
|
-
const config =
|
|
3519
|
+
const config = configService.load();
|
|
3443
3520
|
const projects = getExposedProjects();
|
|
3444
3521
|
const project = projects.find((p) => p.name === projectName);
|
|
3445
3522
|
if (!project) {
|
|
@@ -3495,7 +3572,7 @@ async function findRelatedFiles2(filePath, projectName, options = {}) {
|
|
|
3495
3572
|
}
|
|
3496
3573
|
}
|
|
3497
3574
|
async function searchSymbols2(name, projectName, options = {}) {
|
|
3498
|
-
const config =
|
|
3575
|
+
const config = configService.load();
|
|
3499
3576
|
const projects = getExposedProjects();
|
|
3500
3577
|
const project = projects.find((p) => p.name === projectName);
|
|
3501
3578
|
if (!project) {
|
|
@@ -3573,7 +3650,7 @@ async function searchSymbols2(name, projectName, options = {}) {
|
|
|
3573
3650
|
}
|
|
3574
3651
|
}
|
|
3575
3652
|
async function getFileSummary(filePath, projectName) {
|
|
3576
|
-
const config =
|
|
3653
|
+
const config = configService.load();
|
|
3577
3654
|
const projects = getExposedProjects();
|
|
3578
3655
|
const project = projects.find((p) => p.name === projectName);
|
|
3579
3656
|
if (!project) {
|
|
@@ -3761,7 +3838,7 @@ var init_drift_service = __esm({
|
|
|
3761
3838
|
import * as fs22 from "fs";
|
|
3762
3839
|
import * as path24 from "path";
|
|
3763
3840
|
async function indexKnowledge(projectName, force = false, clean = false) {
|
|
3764
|
-
const config =
|
|
3841
|
+
const config = configService.load();
|
|
3765
3842
|
const projects = getExposedProjects();
|
|
3766
3843
|
const project = projects.find((p2) => p2.name === projectName || p2.path && p2.path === projectName);
|
|
3767
3844
|
if (!project) {
|
|
@@ -4262,7 +4339,7 @@ var init_validation = __esm({
|
|
|
4262
4339
|
import * as fs23 from "fs";
|
|
4263
4340
|
import * as path26 from "path";
|
|
4264
4341
|
function startSession(projectName, taskSlug, agent, phase) {
|
|
4265
|
-
const config =
|
|
4342
|
+
const config = configService.load();
|
|
4266
4343
|
const projects = projectService.scan();
|
|
4267
4344
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
4268
4345
|
if (!project || !project.tasksPath) {
|
|
@@ -4284,7 +4361,7 @@ function startSession(projectName, taskSlug, agent, phase) {
|
|
|
4284
4361
|
return { success: true, message: `Session started for ${agent} agent on task '${taskSlug}' (phase: ${phase})` };
|
|
4285
4362
|
}
|
|
4286
4363
|
function endSession(projectName, taskSlug) {
|
|
4287
|
-
const config =
|
|
4364
|
+
const config = configService.load();
|
|
4288
4365
|
const projects = projectService.scan();
|
|
4289
4366
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
4290
4367
|
if (!project || !project.tasksPath) {
|
|
@@ -4298,7 +4375,7 @@ function endSession(projectName, taskSlug) {
|
|
|
4298
4375
|
return { success: true, message: `Session ended for task '${taskSlug}'.` };
|
|
4299
4376
|
}
|
|
4300
4377
|
function updateAgentTodos(projectName, taskSlug, phase, agent, items) {
|
|
4301
|
-
const config =
|
|
4378
|
+
const config = configService.load();
|
|
4302
4379
|
const projects = projectService.scan();
|
|
4303
4380
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
4304
4381
|
if (!project || !project.tasksPath) {
|
|
@@ -4370,7 +4447,7 @@ function registerResourceHandlers(server) {
|
|
|
4370
4447
|
mimeType: "application/json"
|
|
4371
4448
|
});
|
|
4372
4449
|
for (const project of projects) {
|
|
4373
|
-
const config =
|
|
4450
|
+
const config = configService.load();
|
|
4374
4451
|
const permissions = getProjectPermissions(config, project.name, project.dataPath);
|
|
4375
4452
|
if (permissions.knowledge) {
|
|
4376
4453
|
resources.push({
|
|
@@ -4438,7 +4515,7 @@ var init_resources3 = __esm({
|
|
|
4438
4515
|
// src/mcp/handlers/tools/project.ts
|
|
4439
4516
|
async function handleProjectTool(name, args) {
|
|
4440
4517
|
if (!args) {
|
|
4441
|
-
if (name === "list_projects" || name === "help_setup") {
|
|
4518
|
+
if (name === "list_projects" || name === "help_setup" || name === "reload_config") {
|
|
4442
4519
|
} else {
|
|
4443
4520
|
return null;
|
|
4444
4521
|
}
|
|
@@ -4487,6 +4564,19 @@ To fix this:
|
|
|
4487
4564
|
`;
|
|
4488
4565
|
return { content: [{ type: "text", text: msg }] };
|
|
4489
4566
|
}
|
|
4567
|
+
case "reload_config": {
|
|
4568
|
+
configService.invalidate();
|
|
4569
|
+
const newConfig = configService.load();
|
|
4570
|
+
const exposedProjects = getExposedProjects().map((p) => p.name);
|
|
4571
|
+
const msg = `Config reloaded successfully.
|
|
4572
|
+
- Cache invalidated
|
|
4573
|
+
- Config file: ${configService.getConfigPath()}
|
|
4574
|
+
- Exposed projects: ${exposedProjects.join(", ") || "none"}
|
|
4575
|
+
- Total projects in config: ${newConfig.projects.length}
|
|
4576
|
+
`;
|
|
4577
|
+
logger.info("Config reloaded via reload_config tool", { exposedProjects });
|
|
4578
|
+
return { content: [{ type: "text", text: msg }] };
|
|
4579
|
+
}
|
|
4490
4580
|
default:
|
|
4491
4581
|
return null;
|
|
4492
4582
|
}
|
|
@@ -4497,6 +4587,7 @@ var init_project = __esm({
|
|
|
4497
4587
|
"use strict";
|
|
4498
4588
|
init_resources2();
|
|
4499
4589
|
init_logger();
|
|
4590
|
+
init_config_service();
|
|
4500
4591
|
projectTools = [
|
|
4501
4592
|
{
|
|
4502
4593
|
name: "resolve_path",
|
|
@@ -4535,6 +4626,11 @@ var init_project = __esm({
|
|
|
4535
4626
|
},
|
|
4536
4627
|
required: ["project"]
|
|
4537
4628
|
}
|
|
4629
|
+
},
|
|
4630
|
+
{
|
|
4631
|
+
name: "reload_config",
|
|
4632
|
+
description: "Reload MCP configuration from disk. Use this after manually editing mcp.yaml or when changes are not reflecting automatically.",
|
|
4633
|
+
inputSchema: { type: "object", properties: {} }
|
|
4538
4634
|
}
|
|
4539
4635
|
];
|
|
4540
4636
|
}
|
|
@@ -5218,7 +5314,7 @@ async function cleanupSingleTask(projectName, taskSlug, projectDataPath) {
|
|
|
5218
5314
|
}
|
|
5219
5315
|
}
|
|
5220
5316
|
function getProjectDataPath(projectName) {
|
|
5221
|
-
const config =
|
|
5317
|
+
const config = configService.load();
|
|
5222
5318
|
const projects = projectService.scan();
|
|
5223
5319
|
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
5224
5320
|
if (!project || !project.dataPath) {
|
|
@@ -5456,6 +5552,7 @@ var init_prompts3 = __esm({
|
|
|
5456
5552
|
// src/mcp/server.ts
|
|
5457
5553
|
import { Server as Server4 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5458
5554
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5555
|
+
import * as fs26 from "fs";
|
|
5459
5556
|
async function startMCPServer(options = {}) {
|
|
5460
5557
|
try {
|
|
5461
5558
|
logger.info("Starting MCP Server...");
|
|
@@ -5465,9 +5562,8 @@ async function startMCPServer(options = {}) {
|
|
|
5465
5562
|
});
|
|
5466
5563
|
process.on("unhandledRejection", (reason) => {
|
|
5467
5564
|
logger.error("Unhandled Rejection", reason);
|
|
5468
|
-
console.error("Unhandled Rejection:", reason);
|
|
5469
5565
|
});
|
|
5470
|
-
const config =
|
|
5566
|
+
const config = configService.load();
|
|
5471
5567
|
mcpServer = new Server4(
|
|
5472
5568
|
{ name: "rrce-mcp-hub", version: "1.0.0" },
|
|
5473
5569
|
{ capabilities: { resources: {}, tools: {}, prompts: {} } }
|
|
@@ -5478,6 +5574,21 @@ async function startMCPServer(options = {}) {
|
|
|
5478
5574
|
registerResourceHandlers(mcpServer);
|
|
5479
5575
|
registerToolHandlers(mcpServer);
|
|
5480
5576
|
registerPromptHandlers(mcpServer);
|
|
5577
|
+
const configPath = configService.getConfigPath();
|
|
5578
|
+
if (!options.interactive) {
|
|
5579
|
+
try {
|
|
5580
|
+
if (fs26.existsSync(configPath)) {
|
|
5581
|
+
configWatcher = fs26.watch(configPath, () => {
|
|
5582
|
+
configService.invalidate();
|
|
5583
|
+
const exposed2 = getExposedProjects().map((p) => p.name).join(", ");
|
|
5584
|
+
logger.info("Config file changed, refreshed exposed projects", { exposedProjects: exposed2 });
|
|
5585
|
+
});
|
|
5586
|
+
logger.info("Watching config file for changes", { configPath });
|
|
5587
|
+
}
|
|
5588
|
+
} catch (err) {
|
|
5589
|
+
logger.warn("Failed to watch config file", { error: String(err) });
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5481
5592
|
if (!options.interactive) {
|
|
5482
5593
|
const transport = new StdioServerTransport();
|
|
5483
5594
|
await mcpServer.connect(transport);
|
|
@@ -5498,6 +5609,10 @@ async function startMCPServer(options = {}) {
|
|
|
5498
5609
|
}
|
|
5499
5610
|
}
|
|
5500
5611
|
function stopMCPServer() {
|
|
5612
|
+
if (configWatcher) {
|
|
5613
|
+
configWatcher.close();
|
|
5614
|
+
configWatcher = null;
|
|
5615
|
+
}
|
|
5501
5616
|
if (mcpServer) {
|
|
5502
5617
|
logger.info("Stopping MCP Server...");
|
|
5503
5618
|
mcpServer.close();
|
|
@@ -5509,18 +5624,19 @@ function stopMCPServer() {
|
|
|
5509
5624
|
function getMCPServerStatus() {
|
|
5510
5625
|
return { ...serverState };
|
|
5511
5626
|
}
|
|
5512
|
-
var serverState, mcpServer;
|
|
5627
|
+
var serverState, mcpServer, configWatcher;
|
|
5513
5628
|
var init_server = __esm({
|
|
5514
5629
|
"src/mcp/server.ts"() {
|
|
5515
5630
|
"use strict";
|
|
5516
5631
|
init_logger();
|
|
5517
|
-
|
|
5632
|
+
init_config_service();
|
|
5518
5633
|
init_resources2();
|
|
5519
5634
|
init_resources3();
|
|
5520
5635
|
init_tools();
|
|
5521
5636
|
init_prompts3();
|
|
5522
5637
|
serverState = { running: false };
|
|
5523
5638
|
mcpServer = null;
|
|
5639
|
+
configWatcher = null;
|
|
5524
5640
|
}
|
|
5525
5641
|
});
|
|
5526
5642
|
|
|
@@ -5530,7 +5646,7 @@ import pc5 from "picocolors";
|
|
|
5530
5646
|
async function handleConfigure() {
|
|
5531
5647
|
const s = spinner();
|
|
5532
5648
|
s.start("Scanning for projects...");
|
|
5533
|
-
const config =
|
|
5649
|
+
const config = configService.load();
|
|
5534
5650
|
const knownPaths = config.projects.map((p) => p.path).filter((p) => !!p);
|
|
5535
5651
|
const projects = scanForProjects({ knownPaths });
|
|
5536
5652
|
logger.info("Configure: Loaded config", { projects: config.projects, defaultMode: config.defaults.includeNew });
|
|
@@ -5606,7 +5722,7 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
5606
5722
|
}
|
|
5607
5723
|
async function handleConfigureGlobalPath() {
|
|
5608
5724
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
5609
|
-
const
|
|
5725
|
+
const fs36 = await import("fs");
|
|
5610
5726
|
const path35 = await import("path");
|
|
5611
5727
|
note3(
|
|
5612
5728
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
@@ -5621,10 +5737,10 @@ locally in each project. MCP needs a central location.`,
|
|
|
5621
5737
|
return false;
|
|
5622
5738
|
}
|
|
5623
5739
|
try {
|
|
5624
|
-
if (!
|
|
5625
|
-
|
|
5740
|
+
if (!fs36.existsSync(resolvedPath)) {
|
|
5741
|
+
fs36.mkdirSync(resolvedPath, { recursive: true });
|
|
5626
5742
|
}
|
|
5627
|
-
const config =
|
|
5743
|
+
const config = configService.load();
|
|
5628
5744
|
saveMCPConfig(config);
|
|
5629
5745
|
note3(
|
|
5630
5746
|
`${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
|
|
@@ -5734,15 +5850,15 @@ __export(ConfigContext_exports, {
|
|
|
5734
5850
|
useConfig: () => useConfig
|
|
5735
5851
|
});
|
|
5736
5852
|
import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
|
|
5737
|
-
import * as
|
|
5853
|
+
import * as fs27 from "fs";
|
|
5738
5854
|
import * as path29 from "path";
|
|
5739
5855
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
5740
5856
|
function getPackageVersion() {
|
|
5741
5857
|
try {
|
|
5742
5858
|
const agentCoreDir = getAgentCoreDir();
|
|
5743
5859
|
const packageJsonPath = path29.join(path29.dirname(agentCoreDir), "package.json");
|
|
5744
|
-
if (
|
|
5745
|
-
return JSON.parse(
|
|
5860
|
+
if (fs27.existsSync(packageJsonPath)) {
|
|
5861
|
+
return JSON.parse(fs27.readFileSync(packageJsonPath, "utf8")).version;
|
|
5746
5862
|
}
|
|
5747
5863
|
} catch (e) {
|
|
5748
5864
|
}
|
|
@@ -5809,16 +5925,16 @@ var init_ConfigContext = __esm({
|
|
|
5809
5925
|
});
|
|
5810
5926
|
|
|
5811
5927
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
5812
|
-
import * as
|
|
5928
|
+
import * as fs28 from "fs";
|
|
5813
5929
|
import * as path30 from "path";
|
|
5814
5930
|
function readSession(project, taskSlug) {
|
|
5815
5931
|
const rrceData = getProjectRRCEData(project);
|
|
5816
5932
|
const sessionPath = path30.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5817
|
-
if (!
|
|
5933
|
+
if (!fs28.existsSync(sessionPath)) {
|
|
5818
5934
|
return null;
|
|
5819
5935
|
}
|
|
5820
5936
|
try {
|
|
5821
|
-
const raw =
|
|
5937
|
+
const raw = fs28.readFileSync(sessionPath, "utf-8");
|
|
5822
5938
|
return JSON.parse(raw);
|
|
5823
5939
|
} catch {
|
|
5824
5940
|
return null;
|
|
@@ -5832,11 +5948,11 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
|
5832
5948
|
function readAgentTodos(project, taskSlug) {
|
|
5833
5949
|
const rrceData = getProjectRRCEData(project);
|
|
5834
5950
|
const todosPath = path30.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5835
|
-
if (!
|
|
5951
|
+
if (!fs28.existsSync(todosPath)) {
|
|
5836
5952
|
return null;
|
|
5837
5953
|
}
|
|
5838
5954
|
try {
|
|
5839
|
-
const raw =
|
|
5955
|
+
const raw = fs28.readFileSync(todosPath, "utf-8");
|
|
5840
5956
|
return JSON.parse(raw);
|
|
5841
5957
|
} catch {
|
|
5842
5958
|
return null;
|
|
@@ -5849,8 +5965,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5849
5965
|
if (configPath.startsWith(rrceHome)) {
|
|
5850
5966
|
return "global";
|
|
5851
5967
|
}
|
|
5852
|
-
if (
|
|
5853
|
-
const content =
|
|
5968
|
+
if (fs28.existsSync(configPath)) {
|
|
5969
|
+
const content = fs28.readFileSync(configPath, "utf-8");
|
|
5854
5970
|
if (content.includes("mode: workspace")) return "workspace";
|
|
5855
5971
|
if (content.includes("mode: global")) return "global";
|
|
5856
5972
|
}
|
|
@@ -5870,18 +5986,18 @@ function getProjectRRCEData(project) {
|
|
|
5870
5986
|
function listProjectTasks(project) {
|
|
5871
5987
|
const rrceData = getProjectRRCEData(project);
|
|
5872
5988
|
const tasksPath = path30.join(rrceData, "tasks");
|
|
5873
|
-
if (!
|
|
5989
|
+
if (!fs28.existsSync(tasksPath)) {
|
|
5874
5990
|
return { projectName: project.name, tasksPath, tasks: [] };
|
|
5875
5991
|
}
|
|
5876
5992
|
const tasks = [];
|
|
5877
5993
|
try {
|
|
5878
|
-
const entries =
|
|
5994
|
+
const entries = fs28.readdirSync(tasksPath, { withFileTypes: true });
|
|
5879
5995
|
for (const entry of entries) {
|
|
5880
5996
|
if (!entry.isDirectory()) continue;
|
|
5881
5997
|
const metaPath = path30.join(tasksPath, entry.name, "meta.json");
|
|
5882
|
-
if (!
|
|
5998
|
+
if (!fs28.existsSync(metaPath)) continue;
|
|
5883
5999
|
try {
|
|
5884
|
-
const raw =
|
|
6000
|
+
const raw = fs28.readFileSync(metaPath, "utf-8");
|
|
5885
6001
|
const meta = JSON.parse(raw);
|
|
5886
6002
|
if (!meta.task_slug) meta.task_slug = entry.name;
|
|
5887
6003
|
tasks.push(meta);
|
|
@@ -5901,17 +6017,17 @@ function listProjectTasks(project) {
|
|
|
5901
6017
|
function updateTaskStatus(project, taskSlug, status) {
|
|
5902
6018
|
const rrceData = getProjectRRCEData(project);
|
|
5903
6019
|
const metaPath = path30.join(rrceData, "tasks", taskSlug, "meta.json");
|
|
5904
|
-
if (!
|
|
6020
|
+
if (!fs28.existsSync(metaPath)) {
|
|
5905
6021
|
return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
|
|
5906
6022
|
}
|
|
5907
6023
|
try {
|
|
5908
|
-
const meta = JSON.parse(
|
|
6024
|
+
const meta = JSON.parse(fs28.readFileSync(metaPath, "utf-8"));
|
|
5909
6025
|
const next = {
|
|
5910
6026
|
...meta,
|
|
5911
6027
|
status,
|
|
5912
6028
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5913
6029
|
};
|
|
5914
|
-
|
|
6030
|
+
fs28.writeFileSync(metaPath, JSON.stringify(next, null, 2));
|
|
5915
6031
|
return { ok: true, meta: next };
|
|
5916
6032
|
} catch (e) {
|
|
5917
6033
|
return { ok: false, error: String(e) };
|
|
@@ -6296,7 +6412,7 @@ var init_ProjectViews = __esm({
|
|
|
6296
6412
|
});
|
|
6297
6413
|
|
|
6298
6414
|
// src/mcp/ui/lib/projects.ts
|
|
6299
|
-
import * as
|
|
6415
|
+
import * as fs29 from "fs";
|
|
6300
6416
|
import * as path31 from "path";
|
|
6301
6417
|
function getIndexStats(project) {
|
|
6302
6418
|
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
@@ -6305,18 +6421,18 @@ function getIndexStats(project) {
|
|
|
6305
6421
|
if (knowledgePath) {
|
|
6306
6422
|
const embPath = path31.join(knowledgePath, "embeddings.json");
|
|
6307
6423
|
const codeEmbPath = path31.join(knowledgePath, "code-embeddings.json");
|
|
6308
|
-
if (
|
|
6309
|
-
const stat =
|
|
6424
|
+
if (fs29.existsSync(embPath)) {
|
|
6425
|
+
const stat = fs29.statSync(embPath);
|
|
6310
6426
|
stats.lastIndexed = stat.mtime.toISOString();
|
|
6311
6427
|
try {
|
|
6312
|
-
const data = JSON.parse(
|
|
6428
|
+
const data = JSON.parse(fs29.readFileSync(embPath, "utf-8"));
|
|
6313
6429
|
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6314
6430
|
} catch {
|
|
6315
6431
|
}
|
|
6316
6432
|
}
|
|
6317
|
-
if (
|
|
6433
|
+
if (fs29.existsSync(codeEmbPath)) {
|
|
6318
6434
|
try {
|
|
6319
|
-
const data = JSON.parse(
|
|
6435
|
+
const data = JSON.parse(fs29.readFileSync(codeEmbPath, "utf-8"));
|
|
6320
6436
|
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6321
6437
|
} catch {
|
|
6322
6438
|
}
|
|
@@ -6349,7 +6465,7 @@ var init_ProjectsView = __esm({
|
|
|
6349
6465
|
init_ui_helpers();
|
|
6350
6466
|
init_ProjectViews();
|
|
6351
6467
|
init_projects2();
|
|
6352
|
-
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
|
|
6468
|
+
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange: onConfigChange2, workspacePath }) => {
|
|
6353
6469
|
const { driftReports, checkAllDrift } = useConfig();
|
|
6354
6470
|
const [config, setConfig] = useState3(initialConfig);
|
|
6355
6471
|
const [indexingStats, setIndexingStats] = useState3({});
|
|
@@ -6386,7 +6502,7 @@ var init_ProjectsView = __esm({
|
|
|
6386
6502
|
};
|
|
6387
6503
|
saveMCPConfig(newConfig);
|
|
6388
6504
|
setConfig(newConfig);
|
|
6389
|
-
|
|
6505
|
+
onConfigChange2?.();
|
|
6390
6506
|
}
|
|
6391
6507
|
});
|
|
6392
6508
|
const projectItems = useMemo3(() => {
|
|
@@ -6452,7 +6568,7 @@ ${statsRow}` : label;
|
|
|
6452
6568
|
});
|
|
6453
6569
|
saveMCPConfig(newConfig);
|
|
6454
6570
|
setConfig(newConfig);
|
|
6455
|
-
|
|
6571
|
+
onConfigChange2?.();
|
|
6456
6572
|
};
|
|
6457
6573
|
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
|
|
6458
6574
|
/* @__PURE__ */ jsx6(ProjectsHeader, { autoExpose: config.defaults.includeNew }),
|
|
@@ -7061,7 +7177,7 @@ __export(App_exports, {
|
|
|
7061
7177
|
});
|
|
7062
7178
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
7063
7179
|
import { Box as Box14, useInput as useInput5, useApp } from "ink";
|
|
7064
|
-
import
|
|
7180
|
+
import fs30 from "fs";
|
|
7065
7181
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
7066
7182
|
var App;
|
|
7067
7183
|
var init_App = __esm({
|
|
@@ -7134,18 +7250,18 @@ var init_App = __esm({
|
|
|
7134
7250
|
useEffect6(() => {
|
|
7135
7251
|
const logPath = getLogFilePath();
|
|
7136
7252
|
let lastSize = 0;
|
|
7137
|
-
if (
|
|
7138
|
-
const stats =
|
|
7253
|
+
if (fs30.existsSync(logPath)) {
|
|
7254
|
+
const stats = fs30.statSync(logPath);
|
|
7139
7255
|
lastSize = stats.size;
|
|
7140
7256
|
}
|
|
7141
7257
|
const interval = setInterval(() => {
|
|
7142
|
-
if (
|
|
7143
|
-
const stats =
|
|
7258
|
+
if (fs30.existsSync(logPath)) {
|
|
7259
|
+
const stats = fs30.statSync(logPath);
|
|
7144
7260
|
if (stats.size > lastSize) {
|
|
7145
7261
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
7146
|
-
const fd =
|
|
7147
|
-
|
|
7148
|
-
|
|
7262
|
+
const fd = fs30.openSync(logPath, "r");
|
|
7263
|
+
fs30.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
7264
|
+
fs30.closeSync(fd);
|
|
7149
7265
|
const newContent = buffer.toString("utf-8");
|
|
7150
7266
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
7151
7267
|
setLogs((prev) => {
|
|
@@ -7223,7 +7339,7 @@ async function handleStartServer() {
|
|
|
7223
7339
|
const { render } = await import("ink");
|
|
7224
7340
|
const { App: App2 } = await Promise.resolve().then(() => (init_App(), App_exports));
|
|
7225
7341
|
const { ConfigProvider: ConfigProvider2 } = await Promise.resolve().then(() => (init_ConfigContext(), ConfigContext_exports));
|
|
7226
|
-
const config =
|
|
7342
|
+
const config = configService.load();
|
|
7227
7343
|
const projects = scanForProjects();
|
|
7228
7344
|
const exposedProjects = projects.filter((p) => {
|
|
7229
7345
|
const cfg = config.projects.find(
|
|
@@ -7370,15 +7486,15 @@ __export(update_flow_exports, {
|
|
|
7370
7486
|
});
|
|
7371
7487
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
7372
7488
|
import pc8 from "picocolors";
|
|
7373
|
-
import * as
|
|
7489
|
+
import * as fs31 from "fs";
|
|
7374
7490
|
import * as path32 from "path";
|
|
7375
7491
|
import { stringify as stringify2, parse } from "yaml";
|
|
7376
7492
|
function backupFile(filePath) {
|
|
7377
|
-
if (!
|
|
7493
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
7378
7494
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
7379
7495
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
7380
7496
|
try {
|
|
7381
|
-
|
|
7497
|
+
fs31.copyFileSync(filePath, backupPath);
|
|
7382
7498
|
return backupPath;
|
|
7383
7499
|
} catch (e) {
|
|
7384
7500
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -7389,8 +7505,8 @@ function getPackageVersion2() {
|
|
|
7389
7505
|
try {
|
|
7390
7506
|
const agentCoreDir = getAgentCoreDir();
|
|
7391
7507
|
const packageJsonPath = path32.join(path32.dirname(agentCoreDir), "package.json");
|
|
7392
|
-
if (
|
|
7393
|
-
return JSON.parse(
|
|
7508
|
+
if (fs31.existsSync(packageJsonPath)) {
|
|
7509
|
+
return JSON.parse(fs31.readFileSync(packageJsonPath, "utf8")).version;
|
|
7394
7510
|
}
|
|
7395
7511
|
} catch (e) {
|
|
7396
7512
|
}
|
|
@@ -7405,9 +7521,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7405
7521
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7406
7522
|
const configFilePath = getConfigPath(workspacePath);
|
|
7407
7523
|
let currentSyncedVersion;
|
|
7408
|
-
if (
|
|
7524
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7409
7525
|
try {
|
|
7410
|
-
const content =
|
|
7526
|
+
const content = fs31.readFileSync(configFilePath, "utf-8");
|
|
7411
7527
|
const config = parse(content);
|
|
7412
7528
|
currentSyncedVersion = config.last_synced_version;
|
|
7413
7529
|
} catch (e) {
|
|
@@ -7415,8 +7531,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7415
7531
|
}
|
|
7416
7532
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
7417
7533
|
const ideTargets = [];
|
|
7418
|
-
if (
|
|
7419
|
-
const configContent =
|
|
7534
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7535
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7420
7536
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7421
7537
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7422
7538
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7426,9 +7542,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7426
7542
|
const updatedFiles = [];
|
|
7427
7543
|
for (const dir of dirs) {
|
|
7428
7544
|
const srcDir = path32.join(agentCoreDir, dir);
|
|
7429
|
-
if (!
|
|
7545
|
+
if (!fs31.existsSync(srcDir)) continue;
|
|
7430
7546
|
const syncFiles = (src, rel) => {
|
|
7431
|
-
const entries =
|
|
7547
|
+
const entries = fs31.readdirSync(src, { withFileTypes: true });
|
|
7432
7548
|
for (const entry of entries) {
|
|
7433
7549
|
const entrySrc = path32.join(src, entry.name);
|
|
7434
7550
|
const entryRel = path32.join(rel, entry.name);
|
|
@@ -7441,7 +7557,7 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7441
7557
|
backupFile(entryDest);
|
|
7442
7558
|
}
|
|
7443
7559
|
ensureDir(path32.dirname(entryDest));
|
|
7444
|
-
|
|
7560
|
+
fs31.copyFileSync(entrySrc, entryDest);
|
|
7445
7561
|
updatedFiles.push(entryRel);
|
|
7446
7562
|
}
|
|
7447
7563
|
}
|
|
@@ -7456,8 +7572,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7456
7572
|
ensureDir(path32.join(rrceHome, "docs"));
|
|
7457
7573
|
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7458
7574
|
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7459
|
-
if (
|
|
7460
|
-
const configContent =
|
|
7575
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7576
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7461
7577
|
if (configContent.includes("copilot: true")) {
|
|
7462
7578
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7463
7579
|
ensureDir(copilotPath);
|
|
@@ -7479,21 +7595,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7479
7595
|
try {
|
|
7480
7596
|
const yaml = parse(configContent);
|
|
7481
7597
|
yaml.last_synced_version = runningVersion;
|
|
7482
|
-
|
|
7598
|
+
fs31.writeFileSync(configFilePath, stringify2(yaml));
|
|
7483
7599
|
} catch (e) {
|
|
7484
7600
|
console.error("Failed to update config.yaml version:", e);
|
|
7485
7601
|
}
|
|
7486
7602
|
}
|
|
7487
7603
|
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7488
|
-
if (
|
|
7604
|
+
if (fs31.existsSync(mcpPath)) {
|
|
7489
7605
|
try {
|
|
7490
|
-
const content =
|
|
7606
|
+
const content = fs31.readFileSync(mcpPath, "utf-8");
|
|
7491
7607
|
const yaml = parse(content);
|
|
7492
7608
|
if (yaml.projects) {
|
|
7493
7609
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7494
7610
|
if (project) {
|
|
7495
7611
|
project.last_synced_version = runningVersion;
|
|
7496
|
-
|
|
7612
|
+
fs31.writeFileSync(mcpPath, stringify2(yaml));
|
|
7497
7613
|
}
|
|
7498
7614
|
}
|
|
7499
7615
|
} catch (e) {
|
|
@@ -7529,9 +7645,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7529
7645
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7530
7646
|
const configFilePath = getConfigPath(workspacePath);
|
|
7531
7647
|
let currentSyncedVersion;
|
|
7532
|
-
if (
|
|
7648
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7533
7649
|
try {
|
|
7534
|
-
const content =
|
|
7650
|
+
const content = fs31.readFileSync(configFilePath, "utf-8");
|
|
7535
7651
|
const config = parse(content);
|
|
7536
7652
|
currentSyncedVersion = config.last_synced_version;
|
|
7537
7653
|
} catch (e) {
|
|
@@ -7555,8 +7671,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7555
7671
|
` \u2022 docs/ (documentation)`
|
|
7556
7672
|
];
|
|
7557
7673
|
const ideTargets = [];
|
|
7558
|
-
if (
|
|
7559
|
-
const configContent =
|
|
7674
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7675
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7560
7676
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7561
7677
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7562
7678
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7586,9 +7702,9 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7586
7702
|
const updatedFiles = [];
|
|
7587
7703
|
for (const dir of dirs) {
|
|
7588
7704
|
const srcDir = path32.join(agentCoreDir, dir);
|
|
7589
|
-
if (!
|
|
7705
|
+
if (!fs31.existsSync(srcDir)) continue;
|
|
7590
7706
|
const syncFiles = (src, rel) => {
|
|
7591
|
-
const entries =
|
|
7707
|
+
const entries = fs31.readdirSync(src, { withFileTypes: true });
|
|
7592
7708
|
for (const entry of entries) {
|
|
7593
7709
|
const entrySrc = path32.join(src, entry.name);
|
|
7594
7710
|
const entryRel = path32.join(rel, entry.name);
|
|
@@ -7601,7 +7717,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7601
7717
|
backupFile(entryDest);
|
|
7602
7718
|
}
|
|
7603
7719
|
ensureDir(path32.dirname(entryDest));
|
|
7604
|
-
|
|
7720
|
+
fs31.copyFileSync(entrySrc, entryDest);
|
|
7605
7721
|
updatedFiles.push(entryRel);
|
|
7606
7722
|
}
|
|
7607
7723
|
}
|
|
@@ -7616,8 +7732,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7616
7732
|
ensureDir(path32.join(rrceHome, "docs"));
|
|
7617
7733
|
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7618
7734
|
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7619
|
-
if (
|
|
7620
|
-
const configContent =
|
|
7735
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7736
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7621
7737
|
if (configContent.includes("copilot: true")) {
|
|
7622
7738
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7623
7739
|
ensureDir(copilotPath);
|
|
@@ -7639,21 +7755,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7639
7755
|
try {
|
|
7640
7756
|
const yaml = parse(configContent);
|
|
7641
7757
|
yaml.last_synced_version = runningVersion;
|
|
7642
|
-
|
|
7758
|
+
fs31.writeFileSync(configFilePath, stringify2(yaml));
|
|
7643
7759
|
} catch (e) {
|
|
7644
7760
|
console.error("Failed to update config.yaml version:", e);
|
|
7645
7761
|
}
|
|
7646
7762
|
}
|
|
7647
7763
|
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7648
|
-
if (
|
|
7764
|
+
if (fs31.existsSync(mcpPath)) {
|
|
7649
7765
|
try {
|
|
7650
|
-
const content =
|
|
7766
|
+
const content = fs31.readFileSync(mcpPath, "utf-8");
|
|
7651
7767
|
const yaml = parse(content);
|
|
7652
7768
|
if (yaml.projects) {
|
|
7653
7769
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7654
7770
|
if (project) {
|
|
7655
7771
|
project.last_synced_version = runningVersion;
|
|
7656
|
-
|
|
7772
|
+
fs31.writeFileSync(mcpPath, stringify2(yaml));
|
|
7657
7773
|
}
|
|
7658
7774
|
}
|
|
7659
7775
|
} catch (e) {
|
|
@@ -7712,7 +7828,7 @@ var init_update_flow = __esm({
|
|
|
7712
7828
|
// src/commands/wizard/index.ts
|
|
7713
7829
|
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";
|
|
7714
7830
|
import pc13 from "picocolors";
|
|
7715
|
-
import * as
|
|
7831
|
+
import * as fs35 from "fs";
|
|
7716
7832
|
import * as path34 from "path";
|
|
7717
7833
|
import { parse as parse2 } from "yaml";
|
|
7718
7834
|
|
|
@@ -8390,7 +8506,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
8390
8506
|
init_paths();
|
|
8391
8507
|
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";
|
|
8392
8508
|
import pc10 from "picocolors";
|
|
8393
|
-
import * as
|
|
8509
|
+
import * as fs32 from "fs";
|
|
8394
8510
|
init_detection();
|
|
8395
8511
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
8396
8512
|
const projects = scanForProjects({
|
|
@@ -8429,7 +8545,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
8429
8545
|
const s = spinner4();
|
|
8430
8546
|
s.start("Linking projects");
|
|
8431
8547
|
const configFilePath = getConfigPath(workspacePath);
|
|
8432
|
-
let configContent =
|
|
8548
|
+
let configContent = fs32.readFileSync(configFilePath, "utf-8");
|
|
8433
8549
|
if (configContent.includes("linked_projects:")) {
|
|
8434
8550
|
const lines = configContent.split("\n");
|
|
8435
8551
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -8456,7 +8572,7 @@ linked_projects:
|
|
|
8456
8572
|
`;
|
|
8457
8573
|
});
|
|
8458
8574
|
}
|
|
8459
|
-
|
|
8575
|
+
fs32.writeFileSync(configFilePath, configContent);
|
|
8460
8576
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
8461
8577
|
s.stop("Projects linked");
|
|
8462
8578
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -8492,7 +8608,7 @@ init_paths();
|
|
|
8492
8608
|
init_utils();
|
|
8493
8609
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
8494
8610
|
import pc11 from "picocolors";
|
|
8495
|
-
import * as
|
|
8611
|
+
import * as fs33 from "fs";
|
|
8496
8612
|
import * as path33 from "path";
|
|
8497
8613
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
8498
8614
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
@@ -8500,7 +8616,7 @@ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
|
8500
8616
|
const globalPath = path33.join(customGlobalPath, "workspaces", workspaceName);
|
|
8501
8617
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
8502
8618
|
const existingDirs = subdirs.filter(
|
|
8503
|
-
(dir) =>
|
|
8619
|
+
(dir) => fs33.existsSync(path33.join(localPath, dir))
|
|
8504
8620
|
);
|
|
8505
8621
|
if (existingDirs.length === 0) {
|
|
8506
8622
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -8555,7 +8671,7 @@ init_update_flow();
|
|
|
8555
8671
|
// src/commands/wizard/delete-flow.ts
|
|
8556
8672
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
8557
8673
|
import pc12 from "picocolors";
|
|
8558
|
-
import * as
|
|
8674
|
+
import * as fs34 from "fs";
|
|
8559
8675
|
init_detection();
|
|
8560
8676
|
init_config();
|
|
8561
8677
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -8594,13 +8710,13 @@ Are you sure?`,
|
|
|
8594
8710
|
const s = spinner6();
|
|
8595
8711
|
s.start("Deleting projects...");
|
|
8596
8712
|
try {
|
|
8597
|
-
const mcpConfig =
|
|
8713
|
+
const mcpConfig = configService.load();
|
|
8598
8714
|
let configChanged = false;
|
|
8599
8715
|
for (const projectName of projectsToDelete) {
|
|
8600
8716
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
8601
8717
|
if (!project) continue;
|
|
8602
|
-
if (
|
|
8603
|
-
|
|
8718
|
+
if (fs34.existsSync(project.dataPath)) {
|
|
8719
|
+
fs34.rmSync(project.dataPath, { recursive: true, force: true });
|
|
8604
8720
|
}
|
|
8605
8721
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
8606
8722
|
configChanged = true;
|
|
@@ -8623,8 +8739,8 @@ function getPackageVersion3() {
|
|
|
8623
8739
|
try {
|
|
8624
8740
|
const agentCoreDir = getAgentCoreDir();
|
|
8625
8741
|
const packageJsonPath = path34.join(path34.dirname(agentCoreDir), "package.json");
|
|
8626
|
-
if (
|
|
8627
|
-
return JSON.parse(
|
|
8742
|
+
if (fs35.existsSync(packageJsonPath)) {
|
|
8743
|
+
return JSON.parse(fs35.readFileSync(packageJsonPath, "utf8")).version;
|
|
8628
8744
|
}
|
|
8629
8745
|
} catch (e) {
|
|
8630
8746
|
}
|
|
@@ -8632,9 +8748,9 @@ function getPackageVersion3() {
|
|
|
8632
8748
|
}
|
|
8633
8749
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
8634
8750
|
const configFilePath = getConfigPath(workspacePath);
|
|
8635
|
-
if (
|
|
8751
|
+
if (fs35.existsSync(configFilePath)) {
|
|
8636
8752
|
try {
|
|
8637
|
-
const content =
|
|
8753
|
+
const content = fs35.readFileSync(configFilePath, "utf-8");
|
|
8638
8754
|
const config = parse2(content);
|
|
8639
8755
|
if (config.last_synced_version) {
|
|
8640
8756
|
return config.last_synced_version;
|
|
@@ -8644,9 +8760,9 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
8644
8760
|
}
|
|
8645
8761
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
8646
8762
|
const mcpPath = path34.join(rrceHome, "mcp.yaml");
|
|
8647
|
-
if (
|
|
8763
|
+
if (fs35.existsSync(mcpPath)) {
|
|
8648
8764
|
try {
|
|
8649
|
-
const content =
|
|
8765
|
+
const content = fs35.readFileSync(mcpPath, "utf-8");
|
|
8650
8766
|
const config = parse2(content);
|
|
8651
8767
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
8652
8768
|
if (project?.last_synced_version) {
|
|
@@ -8697,7 +8813,7 @@ async function runWizard() {
|
|
|
8697
8813
|
const workspaceName = getWorkspaceName(workspacePath);
|
|
8698
8814
|
const gitUser = getGitUser();
|
|
8699
8815
|
try {
|
|
8700
|
-
const mcpConfig =
|
|
8816
|
+
const mcpConfig = configService.load();
|
|
8701
8817
|
const { config: cleanConfig, removed } = cleanStaleProjects(mcpConfig);
|
|
8702
8818
|
if (removed.length > 0) {
|
|
8703
8819
|
saveMCPConfig(cleanConfig);
|
|
@@ -8716,18 +8832,18 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8716
8832
|
workspacePath
|
|
8717
8833
|
});
|
|
8718
8834
|
const configFilePath = getConfigPath(workspacePath);
|
|
8719
|
-
let isAlreadyConfigured =
|
|
8835
|
+
let isAlreadyConfigured = fs35.existsSync(configFilePath);
|
|
8720
8836
|
let currentStorageMode = null;
|
|
8721
8837
|
if (isAlreadyConfigured) {
|
|
8722
8838
|
try {
|
|
8723
|
-
const configContent =
|
|
8839
|
+
const configContent = fs35.readFileSync(configFilePath, "utf-8");
|
|
8724
8840
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
8725
8841
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
8726
8842
|
} catch {
|
|
8727
8843
|
}
|
|
8728
8844
|
} else {
|
|
8729
8845
|
try {
|
|
8730
|
-
const mcpConfig =
|
|
8846
|
+
const mcpConfig = configService.load();
|
|
8731
8847
|
const mcpProject = mcpConfig.projects.find((p) => p.path === workspacePath);
|
|
8732
8848
|
if (mcpProject) {
|
|
8733
8849
|
isAlreadyConfigured = true;
|
|
@@ -8737,7 +8853,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8737
8853
|
}
|
|
8738
8854
|
}
|
|
8739
8855
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
8740
|
-
const hasLocalData =
|
|
8856
|
+
const hasLocalData = fs35.existsSync(localDataPath);
|
|
8741
8857
|
if (isAlreadyConfigured) {
|
|
8742
8858
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
8743
8859
|
if (!continueToMenu) {
|