rrce-workflow 0.3.30 → 0.3.31
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 +206 -88
- 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
|
|
|
@@ -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
|
}
|
|
@@ -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...");
|
|
@@ -5478,6 +5575,21 @@ async function startMCPServer(options = {}) {
|
|
|
5478
5575
|
registerResourceHandlers(mcpServer);
|
|
5479
5576
|
registerToolHandlers(mcpServer);
|
|
5480
5577
|
registerPromptHandlers(mcpServer);
|
|
5578
|
+
const configPath = configService.getConfigPath();
|
|
5579
|
+
if (!options.interactive) {
|
|
5580
|
+
try {
|
|
5581
|
+
if (fs26.existsSync(configPath)) {
|
|
5582
|
+
configWatcher = fs26.watch(configPath, () => {
|
|
5583
|
+
configService.invalidate();
|
|
5584
|
+
const exposed2 = getExposedProjects().map((p) => p.name).join(", ");
|
|
5585
|
+
logger.info("Config file changed, refreshed exposed projects", { exposedProjects: exposed2 });
|
|
5586
|
+
});
|
|
5587
|
+
logger.info("Watching config file for changes", { configPath });
|
|
5588
|
+
}
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
logger.warn("Failed to watch config file", { error: String(err) });
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5481
5593
|
if (!options.interactive) {
|
|
5482
5594
|
const transport = new StdioServerTransport();
|
|
5483
5595
|
await mcpServer.connect(transport);
|
|
@@ -5498,6 +5610,10 @@ async function startMCPServer(options = {}) {
|
|
|
5498
5610
|
}
|
|
5499
5611
|
}
|
|
5500
5612
|
function stopMCPServer() {
|
|
5613
|
+
if (configWatcher) {
|
|
5614
|
+
configWatcher.close();
|
|
5615
|
+
configWatcher = null;
|
|
5616
|
+
}
|
|
5501
5617
|
if (mcpServer) {
|
|
5502
5618
|
logger.info("Stopping MCP Server...");
|
|
5503
5619
|
mcpServer.close();
|
|
@@ -5509,18 +5625,20 @@ function stopMCPServer() {
|
|
|
5509
5625
|
function getMCPServerStatus() {
|
|
5510
5626
|
return { ...serverState };
|
|
5511
5627
|
}
|
|
5512
|
-
var serverState, mcpServer;
|
|
5628
|
+
var serverState, mcpServer, configWatcher;
|
|
5513
5629
|
var init_server = __esm({
|
|
5514
5630
|
"src/mcp/server.ts"() {
|
|
5515
5631
|
"use strict";
|
|
5516
5632
|
init_logger();
|
|
5517
5633
|
init_config();
|
|
5634
|
+
init_config_service();
|
|
5518
5635
|
init_resources2();
|
|
5519
5636
|
init_resources3();
|
|
5520
5637
|
init_tools();
|
|
5521
5638
|
init_prompts3();
|
|
5522
5639
|
serverState = { running: false };
|
|
5523
5640
|
mcpServer = null;
|
|
5641
|
+
configWatcher = null;
|
|
5524
5642
|
}
|
|
5525
5643
|
});
|
|
5526
5644
|
|
|
@@ -5606,7 +5724,7 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
5606
5724
|
}
|
|
5607
5725
|
async function handleConfigureGlobalPath() {
|
|
5608
5726
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
5609
|
-
const
|
|
5727
|
+
const fs36 = await import("fs");
|
|
5610
5728
|
const path35 = await import("path");
|
|
5611
5729
|
note3(
|
|
5612
5730
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
@@ -5621,8 +5739,8 @@ locally in each project. MCP needs a central location.`,
|
|
|
5621
5739
|
return false;
|
|
5622
5740
|
}
|
|
5623
5741
|
try {
|
|
5624
|
-
if (!
|
|
5625
|
-
|
|
5742
|
+
if (!fs36.existsSync(resolvedPath)) {
|
|
5743
|
+
fs36.mkdirSync(resolvedPath, { recursive: true });
|
|
5626
5744
|
}
|
|
5627
5745
|
const config = loadMCPConfig();
|
|
5628
5746
|
saveMCPConfig(config);
|
|
@@ -5734,15 +5852,15 @@ __export(ConfigContext_exports, {
|
|
|
5734
5852
|
useConfig: () => useConfig
|
|
5735
5853
|
});
|
|
5736
5854
|
import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
|
|
5737
|
-
import * as
|
|
5855
|
+
import * as fs27 from "fs";
|
|
5738
5856
|
import * as path29 from "path";
|
|
5739
5857
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
5740
5858
|
function getPackageVersion() {
|
|
5741
5859
|
try {
|
|
5742
5860
|
const agentCoreDir = getAgentCoreDir();
|
|
5743
5861
|
const packageJsonPath = path29.join(path29.dirname(agentCoreDir), "package.json");
|
|
5744
|
-
if (
|
|
5745
|
-
return JSON.parse(
|
|
5862
|
+
if (fs27.existsSync(packageJsonPath)) {
|
|
5863
|
+
return JSON.parse(fs27.readFileSync(packageJsonPath, "utf8")).version;
|
|
5746
5864
|
}
|
|
5747
5865
|
} catch (e) {
|
|
5748
5866
|
}
|
|
@@ -5809,16 +5927,16 @@ var init_ConfigContext = __esm({
|
|
|
5809
5927
|
});
|
|
5810
5928
|
|
|
5811
5929
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
5812
|
-
import * as
|
|
5930
|
+
import * as fs28 from "fs";
|
|
5813
5931
|
import * as path30 from "path";
|
|
5814
5932
|
function readSession(project, taskSlug) {
|
|
5815
5933
|
const rrceData = getProjectRRCEData(project);
|
|
5816
5934
|
const sessionPath = path30.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5817
|
-
if (!
|
|
5935
|
+
if (!fs28.existsSync(sessionPath)) {
|
|
5818
5936
|
return null;
|
|
5819
5937
|
}
|
|
5820
5938
|
try {
|
|
5821
|
-
const raw =
|
|
5939
|
+
const raw = fs28.readFileSync(sessionPath, "utf-8");
|
|
5822
5940
|
return JSON.parse(raw);
|
|
5823
5941
|
} catch {
|
|
5824
5942
|
return null;
|
|
@@ -5832,11 +5950,11 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
|
5832
5950
|
function readAgentTodos(project, taskSlug) {
|
|
5833
5951
|
const rrceData = getProjectRRCEData(project);
|
|
5834
5952
|
const todosPath = path30.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5835
|
-
if (!
|
|
5953
|
+
if (!fs28.existsSync(todosPath)) {
|
|
5836
5954
|
return null;
|
|
5837
5955
|
}
|
|
5838
5956
|
try {
|
|
5839
|
-
const raw =
|
|
5957
|
+
const raw = fs28.readFileSync(todosPath, "utf-8");
|
|
5840
5958
|
return JSON.parse(raw);
|
|
5841
5959
|
} catch {
|
|
5842
5960
|
return null;
|
|
@@ -5849,8 +5967,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5849
5967
|
if (configPath.startsWith(rrceHome)) {
|
|
5850
5968
|
return "global";
|
|
5851
5969
|
}
|
|
5852
|
-
if (
|
|
5853
|
-
const content =
|
|
5970
|
+
if (fs28.existsSync(configPath)) {
|
|
5971
|
+
const content = fs28.readFileSync(configPath, "utf-8");
|
|
5854
5972
|
if (content.includes("mode: workspace")) return "workspace";
|
|
5855
5973
|
if (content.includes("mode: global")) return "global";
|
|
5856
5974
|
}
|
|
@@ -5870,18 +5988,18 @@ function getProjectRRCEData(project) {
|
|
|
5870
5988
|
function listProjectTasks(project) {
|
|
5871
5989
|
const rrceData = getProjectRRCEData(project);
|
|
5872
5990
|
const tasksPath = path30.join(rrceData, "tasks");
|
|
5873
|
-
if (!
|
|
5991
|
+
if (!fs28.existsSync(tasksPath)) {
|
|
5874
5992
|
return { projectName: project.name, tasksPath, tasks: [] };
|
|
5875
5993
|
}
|
|
5876
5994
|
const tasks = [];
|
|
5877
5995
|
try {
|
|
5878
|
-
const entries =
|
|
5996
|
+
const entries = fs28.readdirSync(tasksPath, { withFileTypes: true });
|
|
5879
5997
|
for (const entry of entries) {
|
|
5880
5998
|
if (!entry.isDirectory()) continue;
|
|
5881
5999
|
const metaPath = path30.join(tasksPath, entry.name, "meta.json");
|
|
5882
|
-
if (!
|
|
6000
|
+
if (!fs28.existsSync(metaPath)) continue;
|
|
5883
6001
|
try {
|
|
5884
|
-
const raw =
|
|
6002
|
+
const raw = fs28.readFileSync(metaPath, "utf-8");
|
|
5885
6003
|
const meta = JSON.parse(raw);
|
|
5886
6004
|
if (!meta.task_slug) meta.task_slug = entry.name;
|
|
5887
6005
|
tasks.push(meta);
|
|
@@ -5901,17 +6019,17 @@ function listProjectTasks(project) {
|
|
|
5901
6019
|
function updateTaskStatus(project, taskSlug, status) {
|
|
5902
6020
|
const rrceData = getProjectRRCEData(project);
|
|
5903
6021
|
const metaPath = path30.join(rrceData, "tasks", taskSlug, "meta.json");
|
|
5904
|
-
if (!
|
|
6022
|
+
if (!fs28.existsSync(metaPath)) {
|
|
5905
6023
|
return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
|
|
5906
6024
|
}
|
|
5907
6025
|
try {
|
|
5908
|
-
const meta = JSON.parse(
|
|
6026
|
+
const meta = JSON.parse(fs28.readFileSync(metaPath, "utf-8"));
|
|
5909
6027
|
const next = {
|
|
5910
6028
|
...meta,
|
|
5911
6029
|
status,
|
|
5912
6030
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5913
6031
|
};
|
|
5914
|
-
|
|
6032
|
+
fs28.writeFileSync(metaPath, JSON.stringify(next, null, 2));
|
|
5915
6033
|
return { ok: true, meta: next };
|
|
5916
6034
|
} catch (e) {
|
|
5917
6035
|
return { ok: false, error: String(e) };
|
|
@@ -6296,7 +6414,7 @@ var init_ProjectViews = __esm({
|
|
|
6296
6414
|
});
|
|
6297
6415
|
|
|
6298
6416
|
// src/mcp/ui/lib/projects.ts
|
|
6299
|
-
import * as
|
|
6417
|
+
import * as fs29 from "fs";
|
|
6300
6418
|
import * as path31 from "path";
|
|
6301
6419
|
function getIndexStats(project) {
|
|
6302
6420
|
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
@@ -6305,18 +6423,18 @@ function getIndexStats(project) {
|
|
|
6305
6423
|
if (knowledgePath) {
|
|
6306
6424
|
const embPath = path31.join(knowledgePath, "embeddings.json");
|
|
6307
6425
|
const codeEmbPath = path31.join(knowledgePath, "code-embeddings.json");
|
|
6308
|
-
if (
|
|
6309
|
-
const stat =
|
|
6426
|
+
if (fs29.existsSync(embPath)) {
|
|
6427
|
+
const stat = fs29.statSync(embPath);
|
|
6310
6428
|
stats.lastIndexed = stat.mtime.toISOString();
|
|
6311
6429
|
try {
|
|
6312
|
-
const data = JSON.parse(
|
|
6430
|
+
const data = JSON.parse(fs29.readFileSync(embPath, "utf-8"));
|
|
6313
6431
|
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6314
6432
|
} catch {
|
|
6315
6433
|
}
|
|
6316
6434
|
}
|
|
6317
|
-
if (
|
|
6435
|
+
if (fs29.existsSync(codeEmbPath)) {
|
|
6318
6436
|
try {
|
|
6319
|
-
const data = JSON.parse(
|
|
6437
|
+
const data = JSON.parse(fs29.readFileSync(codeEmbPath, "utf-8"));
|
|
6320
6438
|
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6321
6439
|
} catch {
|
|
6322
6440
|
}
|
|
@@ -6349,7 +6467,7 @@ var init_ProjectsView = __esm({
|
|
|
6349
6467
|
init_ui_helpers();
|
|
6350
6468
|
init_ProjectViews();
|
|
6351
6469
|
init_projects2();
|
|
6352
|
-
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange, workspacePath }) => {
|
|
6470
|
+
ProjectsView = ({ config: initialConfig, projects: allProjects, onConfigChange: onConfigChange2, workspacePath }) => {
|
|
6353
6471
|
const { driftReports, checkAllDrift } = useConfig();
|
|
6354
6472
|
const [config, setConfig] = useState3(initialConfig);
|
|
6355
6473
|
const [indexingStats, setIndexingStats] = useState3({});
|
|
@@ -6386,7 +6504,7 @@ var init_ProjectsView = __esm({
|
|
|
6386
6504
|
};
|
|
6387
6505
|
saveMCPConfig(newConfig);
|
|
6388
6506
|
setConfig(newConfig);
|
|
6389
|
-
|
|
6507
|
+
onConfigChange2?.();
|
|
6390
6508
|
}
|
|
6391
6509
|
});
|
|
6392
6510
|
const projectItems = useMemo3(() => {
|
|
@@ -6452,7 +6570,7 @@ ${statsRow}` : label;
|
|
|
6452
6570
|
});
|
|
6453
6571
|
saveMCPConfig(newConfig);
|
|
6454
6572
|
setConfig(newConfig);
|
|
6455
|
-
|
|
6573
|
+
onConfigChange2?.();
|
|
6456
6574
|
};
|
|
6457
6575
|
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "white", flexGrow: 1, children: [
|
|
6458
6576
|
/* @__PURE__ */ jsx6(ProjectsHeader, { autoExpose: config.defaults.includeNew }),
|
|
@@ -7061,7 +7179,7 @@ __export(App_exports, {
|
|
|
7061
7179
|
});
|
|
7062
7180
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
7063
7181
|
import { Box as Box14, useInput as useInput5, useApp } from "ink";
|
|
7064
|
-
import
|
|
7182
|
+
import fs30 from "fs";
|
|
7065
7183
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
7066
7184
|
var App;
|
|
7067
7185
|
var init_App = __esm({
|
|
@@ -7134,18 +7252,18 @@ var init_App = __esm({
|
|
|
7134
7252
|
useEffect6(() => {
|
|
7135
7253
|
const logPath = getLogFilePath();
|
|
7136
7254
|
let lastSize = 0;
|
|
7137
|
-
if (
|
|
7138
|
-
const stats =
|
|
7255
|
+
if (fs30.existsSync(logPath)) {
|
|
7256
|
+
const stats = fs30.statSync(logPath);
|
|
7139
7257
|
lastSize = stats.size;
|
|
7140
7258
|
}
|
|
7141
7259
|
const interval = setInterval(() => {
|
|
7142
|
-
if (
|
|
7143
|
-
const stats =
|
|
7260
|
+
if (fs30.existsSync(logPath)) {
|
|
7261
|
+
const stats = fs30.statSync(logPath);
|
|
7144
7262
|
if (stats.size > lastSize) {
|
|
7145
7263
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
7146
|
-
const fd =
|
|
7147
|
-
|
|
7148
|
-
|
|
7264
|
+
const fd = fs30.openSync(logPath, "r");
|
|
7265
|
+
fs30.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
7266
|
+
fs30.closeSync(fd);
|
|
7149
7267
|
const newContent = buffer.toString("utf-8");
|
|
7150
7268
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
7151
7269
|
setLogs((prev) => {
|
|
@@ -7370,15 +7488,15 @@ __export(update_flow_exports, {
|
|
|
7370
7488
|
});
|
|
7371
7489
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
7372
7490
|
import pc8 from "picocolors";
|
|
7373
|
-
import * as
|
|
7491
|
+
import * as fs31 from "fs";
|
|
7374
7492
|
import * as path32 from "path";
|
|
7375
7493
|
import { stringify as stringify2, parse } from "yaml";
|
|
7376
7494
|
function backupFile(filePath) {
|
|
7377
|
-
if (!
|
|
7495
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
7378
7496
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
7379
7497
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
7380
7498
|
try {
|
|
7381
|
-
|
|
7499
|
+
fs31.copyFileSync(filePath, backupPath);
|
|
7382
7500
|
return backupPath;
|
|
7383
7501
|
} catch (e) {
|
|
7384
7502
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -7389,8 +7507,8 @@ function getPackageVersion2() {
|
|
|
7389
7507
|
try {
|
|
7390
7508
|
const agentCoreDir = getAgentCoreDir();
|
|
7391
7509
|
const packageJsonPath = path32.join(path32.dirname(agentCoreDir), "package.json");
|
|
7392
|
-
if (
|
|
7393
|
-
return JSON.parse(
|
|
7510
|
+
if (fs31.existsSync(packageJsonPath)) {
|
|
7511
|
+
return JSON.parse(fs31.readFileSync(packageJsonPath, "utf8")).version;
|
|
7394
7512
|
}
|
|
7395
7513
|
} catch (e) {
|
|
7396
7514
|
}
|
|
@@ -7405,9 +7523,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7405
7523
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7406
7524
|
const configFilePath = getConfigPath(workspacePath);
|
|
7407
7525
|
let currentSyncedVersion;
|
|
7408
|
-
if (
|
|
7526
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7409
7527
|
try {
|
|
7410
|
-
const content =
|
|
7528
|
+
const content = fs31.readFileSync(configFilePath, "utf-8");
|
|
7411
7529
|
const config = parse(content);
|
|
7412
7530
|
currentSyncedVersion = config.last_synced_version;
|
|
7413
7531
|
} catch (e) {
|
|
@@ -7415,8 +7533,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7415
7533
|
}
|
|
7416
7534
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
7417
7535
|
const ideTargets = [];
|
|
7418
|
-
if (
|
|
7419
|
-
const configContent =
|
|
7536
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7537
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7420
7538
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7421
7539
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7422
7540
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7426,9 +7544,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7426
7544
|
const updatedFiles = [];
|
|
7427
7545
|
for (const dir of dirs) {
|
|
7428
7546
|
const srcDir = path32.join(agentCoreDir, dir);
|
|
7429
|
-
if (!
|
|
7547
|
+
if (!fs31.existsSync(srcDir)) continue;
|
|
7430
7548
|
const syncFiles = (src, rel) => {
|
|
7431
|
-
const entries =
|
|
7549
|
+
const entries = fs31.readdirSync(src, { withFileTypes: true });
|
|
7432
7550
|
for (const entry of entries) {
|
|
7433
7551
|
const entrySrc = path32.join(src, entry.name);
|
|
7434
7552
|
const entryRel = path32.join(rel, entry.name);
|
|
@@ -7441,7 +7559,7 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7441
7559
|
backupFile(entryDest);
|
|
7442
7560
|
}
|
|
7443
7561
|
ensureDir(path32.dirname(entryDest));
|
|
7444
|
-
|
|
7562
|
+
fs31.copyFileSync(entrySrc, entryDest);
|
|
7445
7563
|
updatedFiles.push(entryRel);
|
|
7446
7564
|
}
|
|
7447
7565
|
}
|
|
@@ -7456,8 +7574,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7456
7574
|
ensureDir(path32.join(rrceHome, "docs"));
|
|
7457
7575
|
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7458
7576
|
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7459
|
-
if (
|
|
7460
|
-
const configContent =
|
|
7577
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7578
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7461
7579
|
if (configContent.includes("copilot: true")) {
|
|
7462
7580
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7463
7581
|
ensureDir(copilotPath);
|
|
@@ -7479,21 +7597,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7479
7597
|
try {
|
|
7480
7598
|
const yaml = parse(configContent);
|
|
7481
7599
|
yaml.last_synced_version = runningVersion;
|
|
7482
|
-
|
|
7600
|
+
fs31.writeFileSync(configFilePath, stringify2(yaml));
|
|
7483
7601
|
} catch (e) {
|
|
7484
7602
|
console.error("Failed to update config.yaml version:", e);
|
|
7485
7603
|
}
|
|
7486
7604
|
}
|
|
7487
7605
|
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7488
|
-
if (
|
|
7606
|
+
if (fs31.existsSync(mcpPath)) {
|
|
7489
7607
|
try {
|
|
7490
|
-
const content =
|
|
7608
|
+
const content = fs31.readFileSync(mcpPath, "utf-8");
|
|
7491
7609
|
const yaml = parse(content);
|
|
7492
7610
|
if (yaml.projects) {
|
|
7493
7611
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7494
7612
|
if (project) {
|
|
7495
7613
|
project.last_synced_version = runningVersion;
|
|
7496
|
-
|
|
7614
|
+
fs31.writeFileSync(mcpPath, stringify2(yaml));
|
|
7497
7615
|
}
|
|
7498
7616
|
}
|
|
7499
7617
|
} catch (e) {
|
|
@@ -7529,9 +7647,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7529
7647
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7530
7648
|
const configFilePath = getConfigPath(workspacePath);
|
|
7531
7649
|
let currentSyncedVersion;
|
|
7532
|
-
if (
|
|
7650
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7533
7651
|
try {
|
|
7534
|
-
const content =
|
|
7652
|
+
const content = fs31.readFileSync(configFilePath, "utf-8");
|
|
7535
7653
|
const config = parse(content);
|
|
7536
7654
|
currentSyncedVersion = config.last_synced_version;
|
|
7537
7655
|
} catch (e) {
|
|
@@ -7555,8 +7673,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7555
7673
|
` \u2022 docs/ (documentation)`
|
|
7556
7674
|
];
|
|
7557
7675
|
const ideTargets = [];
|
|
7558
|
-
if (
|
|
7559
|
-
const configContent =
|
|
7676
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7677
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7560
7678
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7561
7679
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7562
7680
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7586,9 +7704,9 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7586
7704
|
const updatedFiles = [];
|
|
7587
7705
|
for (const dir of dirs) {
|
|
7588
7706
|
const srcDir = path32.join(agentCoreDir, dir);
|
|
7589
|
-
if (!
|
|
7707
|
+
if (!fs31.existsSync(srcDir)) continue;
|
|
7590
7708
|
const syncFiles = (src, rel) => {
|
|
7591
|
-
const entries =
|
|
7709
|
+
const entries = fs31.readdirSync(src, { withFileTypes: true });
|
|
7592
7710
|
for (const entry of entries) {
|
|
7593
7711
|
const entrySrc = path32.join(src, entry.name);
|
|
7594
7712
|
const entryRel = path32.join(rel, entry.name);
|
|
@@ -7601,7 +7719,7 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7601
7719
|
backupFile(entryDest);
|
|
7602
7720
|
}
|
|
7603
7721
|
ensureDir(path32.dirname(entryDest));
|
|
7604
|
-
|
|
7722
|
+
fs31.copyFileSync(entrySrc, entryDest);
|
|
7605
7723
|
updatedFiles.push(entryRel);
|
|
7606
7724
|
}
|
|
7607
7725
|
}
|
|
@@ -7616,8 +7734,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7616
7734
|
ensureDir(path32.join(rrceHome, "docs"));
|
|
7617
7735
|
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7618
7736
|
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7619
|
-
if (
|
|
7620
|
-
const configContent =
|
|
7737
|
+
if (fs31.existsSync(configFilePath)) {
|
|
7738
|
+
const configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
7621
7739
|
if (configContent.includes("copilot: true")) {
|
|
7622
7740
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7623
7741
|
ensureDir(copilotPath);
|
|
@@ -7639,21 +7757,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7639
7757
|
try {
|
|
7640
7758
|
const yaml = parse(configContent);
|
|
7641
7759
|
yaml.last_synced_version = runningVersion;
|
|
7642
|
-
|
|
7760
|
+
fs31.writeFileSync(configFilePath, stringify2(yaml));
|
|
7643
7761
|
} catch (e) {
|
|
7644
7762
|
console.error("Failed to update config.yaml version:", e);
|
|
7645
7763
|
}
|
|
7646
7764
|
}
|
|
7647
7765
|
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7648
|
-
if (
|
|
7766
|
+
if (fs31.existsSync(mcpPath)) {
|
|
7649
7767
|
try {
|
|
7650
|
-
const content =
|
|
7768
|
+
const content = fs31.readFileSync(mcpPath, "utf-8");
|
|
7651
7769
|
const yaml = parse(content);
|
|
7652
7770
|
if (yaml.projects) {
|
|
7653
7771
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7654
7772
|
if (project) {
|
|
7655
7773
|
project.last_synced_version = runningVersion;
|
|
7656
|
-
|
|
7774
|
+
fs31.writeFileSync(mcpPath, stringify2(yaml));
|
|
7657
7775
|
}
|
|
7658
7776
|
}
|
|
7659
7777
|
} catch (e) {
|
|
@@ -7712,7 +7830,7 @@ var init_update_flow = __esm({
|
|
|
7712
7830
|
// src/commands/wizard/index.ts
|
|
7713
7831
|
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
7832
|
import pc13 from "picocolors";
|
|
7715
|
-
import * as
|
|
7833
|
+
import * as fs35 from "fs";
|
|
7716
7834
|
import * as path34 from "path";
|
|
7717
7835
|
import { parse as parse2 } from "yaml";
|
|
7718
7836
|
|
|
@@ -8390,7 +8508,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
8390
8508
|
init_paths();
|
|
8391
8509
|
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
8510
|
import pc10 from "picocolors";
|
|
8393
|
-
import * as
|
|
8511
|
+
import * as fs32 from "fs";
|
|
8394
8512
|
init_detection();
|
|
8395
8513
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
8396
8514
|
const projects = scanForProjects({
|
|
@@ -8429,7 +8547,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
8429
8547
|
const s = spinner4();
|
|
8430
8548
|
s.start("Linking projects");
|
|
8431
8549
|
const configFilePath = getConfigPath(workspacePath);
|
|
8432
|
-
let configContent =
|
|
8550
|
+
let configContent = fs32.readFileSync(configFilePath, "utf-8");
|
|
8433
8551
|
if (configContent.includes("linked_projects:")) {
|
|
8434
8552
|
const lines = configContent.split("\n");
|
|
8435
8553
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -8456,7 +8574,7 @@ linked_projects:
|
|
|
8456
8574
|
`;
|
|
8457
8575
|
});
|
|
8458
8576
|
}
|
|
8459
|
-
|
|
8577
|
+
fs32.writeFileSync(configFilePath, configContent);
|
|
8460
8578
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
8461
8579
|
s.stop("Projects linked");
|
|
8462
8580
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -8492,7 +8610,7 @@ init_paths();
|
|
|
8492
8610
|
init_utils();
|
|
8493
8611
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
8494
8612
|
import pc11 from "picocolors";
|
|
8495
|
-
import * as
|
|
8613
|
+
import * as fs33 from "fs";
|
|
8496
8614
|
import * as path33 from "path";
|
|
8497
8615
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
8498
8616
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
@@ -8500,7 +8618,7 @@ async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
|
8500
8618
|
const globalPath = path33.join(customGlobalPath, "workspaces", workspaceName);
|
|
8501
8619
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
8502
8620
|
const existingDirs = subdirs.filter(
|
|
8503
|
-
(dir) =>
|
|
8621
|
+
(dir) => fs33.existsSync(path33.join(localPath, dir))
|
|
8504
8622
|
);
|
|
8505
8623
|
if (existingDirs.length === 0) {
|
|
8506
8624
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -8555,7 +8673,7 @@ init_update_flow();
|
|
|
8555
8673
|
// src/commands/wizard/delete-flow.ts
|
|
8556
8674
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
8557
8675
|
import pc12 from "picocolors";
|
|
8558
|
-
import * as
|
|
8676
|
+
import * as fs34 from "fs";
|
|
8559
8677
|
init_detection();
|
|
8560
8678
|
init_config();
|
|
8561
8679
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -8599,8 +8717,8 @@ Are you sure?`,
|
|
|
8599
8717
|
for (const projectName of projectsToDelete) {
|
|
8600
8718
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
8601
8719
|
if (!project) continue;
|
|
8602
|
-
if (
|
|
8603
|
-
|
|
8720
|
+
if (fs34.existsSync(project.dataPath)) {
|
|
8721
|
+
fs34.rmSync(project.dataPath, { recursive: true, force: true });
|
|
8604
8722
|
}
|
|
8605
8723
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
8606
8724
|
configChanged = true;
|
|
@@ -8623,8 +8741,8 @@ function getPackageVersion3() {
|
|
|
8623
8741
|
try {
|
|
8624
8742
|
const agentCoreDir = getAgentCoreDir();
|
|
8625
8743
|
const packageJsonPath = path34.join(path34.dirname(agentCoreDir), "package.json");
|
|
8626
|
-
if (
|
|
8627
|
-
return JSON.parse(
|
|
8744
|
+
if (fs35.existsSync(packageJsonPath)) {
|
|
8745
|
+
return JSON.parse(fs35.readFileSync(packageJsonPath, "utf8")).version;
|
|
8628
8746
|
}
|
|
8629
8747
|
} catch (e) {
|
|
8630
8748
|
}
|
|
@@ -8632,9 +8750,9 @@ function getPackageVersion3() {
|
|
|
8632
8750
|
}
|
|
8633
8751
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
8634
8752
|
const configFilePath = getConfigPath(workspacePath);
|
|
8635
|
-
if (
|
|
8753
|
+
if (fs35.existsSync(configFilePath)) {
|
|
8636
8754
|
try {
|
|
8637
|
-
const content =
|
|
8755
|
+
const content = fs35.readFileSync(configFilePath, "utf-8");
|
|
8638
8756
|
const config = parse2(content);
|
|
8639
8757
|
if (config.last_synced_version) {
|
|
8640
8758
|
return config.last_synced_version;
|
|
@@ -8644,9 +8762,9 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
8644
8762
|
}
|
|
8645
8763
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
8646
8764
|
const mcpPath = path34.join(rrceHome, "mcp.yaml");
|
|
8647
|
-
if (
|
|
8765
|
+
if (fs35.existsSync(mcpPath)) {
|
|
8648
8766
|
try {
|
|
8649
|
-
const content =
|
|
8767
|
+
const content = fs35.readFileSync(mcpPath, "utf-8");
|
|
8650
8768
|
const config = parse2(content);
|
|
8651
8769
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
8652
8770
|
if (project?.last_synced_version) {
|
|
@@ -8716,11 +8834,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8716
8834
|
workspacePath
|
|
8717
8835
|
});
|
|
8718
8836
|
const configFilePath = getConfigPath(workspacePath);
|
|
8719
|
-
let isAlreadyConfigured =
|
|
8837
|
+
let isAlreadyConfigured = fs35.existsSync(configFilePath);
|
|
8720
8838
|
let currentStorageMode = null;
|
|
8721
8839
|
if (isAlreadyConfigured) {
|
|
8722
8840
|
try {
|
|
8723
|
-
const configContent =
|
|
8841
|
+
const configContent = fs35.readFileSync(configFilePath, "utf-8");
|
|
8724
8842
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
8725
8843
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
8726
8844
|
} catch {
|
|
@@ -8737,7 +8855,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8737
8855
|
}
|
|
8738
8856
|
}
|
|
8739
8857
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
8740
|
-
const hasLocalData =
|
|
8858
|
+
const hasLocalData = fs35.existsSync(localDataPath);
|
|
8741
8859
|
if (isAlreadyConfigured) {
|
|
8742
8860
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
8743
8861
|
if (!continueToMenu) {
|