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.
Files changed (3) hide show
  1. package/README.md +109 -45
  2. package/dist/index.js +206 -88
  3. 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
- - **Structured Agent Pipelines**: 5 specialized agents (Init, Design, Develop, Docs, Sync, Doctor) for end-to-end development workflows.
15
- - **Task Management**: Built-in CRUD operations for tracking high-level tasks via MCP tools.
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
- | `list_agents` | List available RRCE agents and their arguments |
76
- | `get_agent_prompt` | Get the system prompt for a specific agent (with context injection) |
77
- | `list_tasks` | List all tasks for a project |
78
- | `get_task` | Get details of a task |
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`)**: Orchestrates the complete workflow lifecycle (tab-switchable)
107
- - **Subagents** (`@rrce_*`): Specialized agents for each phase (expert mode)
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
- ## The Agent Pipeline
190
+ ## 4-Phase Workflow
183
191
 
184
- RRCE provides two ways to work with agents, depending on your workflow needs:
192
+ RRCE uses a streamlined 4-phase pipeline for end-to-end development:
185
193
 
186
- ### Primary Orchestrator (Recommended for OpenCode)
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
- The **RRCE Orchestrator** is a primary agent that manages the complete workflow lifecycle:
208
+ ### Slash Commands (In-Context Execution)
189
209
 
190
- - **Access**: Press `Tab` in OpenCode to cycle to the RRCE agent
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
- ### Specialized Subagents
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
- For expert control, invoke subagents directly via your AI assistant or MCP tools:
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 and semantic index | `PROJECT_NAME` (optional) |
202
- | **Research** | `@rrce_research_discussion` | Interactive requirements clarification through dialogue | `TASK_SLUG`, `REQUEST` |
203
- | **Planning** | `@rrce_planning_discussion` | Transform research into actionable execution plan | `TASK_SLUG` |
204
- | **Executor** | `@rrce_executor` | Implement the plan - the ONLY agent authorized to modify code | `TASK_SLUG`, `BRANCH` |
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, identify issues, recommend improvements | `PROJECT_NAME`, `FOCUS_AREA` |
233
+ | **Doctor** | `@rrce_doctor` | Analyze codebase health, recommend improvements | `PROJECT_NAME`, `FOCUS_AREA` |
234
+
235
+ ### OpenCode Integration
208
236
 
209
- ### Workflow Comparison
237
+ OpenCode provides specialized UX optimizations:
210
238
 
211
- | Approach | When to Use | Example |
212
- |----------|-------------|---------|
213
- | **Orchestrator** | Complex features, want automatic flow | Switch to RRCE agent: "Add user authentication" → Auto-orchestrates all phases |
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. **`@rrce_init`** (or orchestrator): "Analyze this codebase." → Creates `project-context.md` and semantic index.
219
- 2. **RRCE Orchestrator**: "I need to add user auth." Runs research, planning, execution phases automatically.
220
- 3. **`@rrce_sync`**: "Update knowledge." → Refreshes context for the next task.
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 skip lists like `node_modules`, `.git`).
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
- ## 🛠 AI Agent Effectiveness & Code Health
276
+ ## MCP Dashboard (TUI)
277
+
278
+ The **MCP Dashboard** provides a cockpit-style interface for managing your RRCE workflow:
238
279
 
239
- We've optimized the codebase to be **highly navigatable for AI coding agents**:
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
- ### Codebase Optimization (v0.3.13)
242
- - **Deduplication**: Consolidated install/uninstall logic and project sorting.
243
- - **Function Extraction**: Split monolithic functions (like `index_knowledge`) into manageable helpers.
244
- - **Improved Type Safety**: Replaced `any` types with structured interfaces (`TaskMeta`, `AgentInfo`).
245
- - **AI Agent Guide**: Added [AI Agent Architecture Guide](docs/AI_AGENT_GUIDE.md) to help coding assistants navigate the project.
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 fs35 = await import("fs");
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 (!fs35.existsSync(resolvedPath)) {
5625
- fs35.mkdirSync(resolvedPath, { recursive: true });
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 fs26 from "fs";
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 (fs26.existsSync(packageJsonPath)) {
5745
- return JSON.parse(fs26.readFileSync(packageJsonPath, "utf8")).version;
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 fs27 from "fs";
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 (!fs27.existsSync(sessionPath)) {
5935
+ if (!fs28.existsSync(sessionPath)) {
5818
5936
  return null;
5819
5937
  }
5820
5938
  try {
5821
- const raw = fs27.readFileSync(sessionPath, "utf-8");
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 (!fs27.existsSync(todosPath)) {
5953
+ if (!fs28.existsSync(todosPath)) {
5836
5954
  return null;
5837
5955
  }
5838
5956
  try {
5839
- const raw = fs27.readFileSync(todosPath, "utf-8");
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 (fs27.existsSync(configPath)) {
5853
- const content = fs27.readFileSync(configPath, "utf-8");
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 (!fs27.existsSync(tasksPath)) {
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 = fs27.readdirSync(tasksPath, { withFileTypes: true });
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 (!fs27.existsSync(metaPath)) continue;
6000
+ if (!fs28.existsSync(metaPath)) continue;
5883
6001
  try {
5884
- const raw = fs27.readFileSync(metaPath, "utf-8");
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 (!fs27.existsSync(metaPath)) {
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(fs27.readFileSync(metaPath, "utf-8"));
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
- fs27.writeFileSync(metaPath, JSON.stringify(next, null, 2));
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 fs28 from "fs";
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 (fs28.existsSync(embPath)) {
6309
- const stat = fs28.statSync(embPath);
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(fs28.readFileSync(embPath, "utf-8"));
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 (fs28.existsSync(codeEmbPath)) {
6435
+ if (fs29.existsSync(codeEmbPath)) {
6318
6436
  try {
6319
- const data = JSON.parse(fs28.readFileSync(codeEmbPath, "utf-8"));
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
- onConfigChange?.();
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
- onConfigChange?.();
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 fs29 from "fs";
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 (fs29.existsSync(logPath)) {
7138
- const stats = fs29.statSync(logPath);
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 (fs29.existsSync(logPath)) {
7143
- const stats = fs29.statSync(logPath);
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 = fs29.openSync(logPath, "r");
7147
- fs29.readSync(fd, buffer, 0, buffer.length, lastSize);
7148
- fs29.closeSync(fd);
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 fs30 from "fs";
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 (!fs30.existsSync(filePath)) return null;
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
- fs30.copyFileSync(filePath, backupPath);
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 (fs30.existsSync(packageJsonPath)) {
7393
- return JSON.parse(fs30.readFileSync(packageJsonPath, "utf8")).version;
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 (fs30.existsSync(configFilePath)) {
7526
+ if (fs31.existsSync(configFilePath)) {
7409
7527
  try {
7410
- const content = fs30.readFileSync(configFilePath, "utf-8");
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 (fs30.existsSync(configFilePath)) {
7419
- const configContent = fs30.readFileSync(configFilePath, "utf-8");
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 (!fs30.existsSync(srcDir)) continue;
7547
+ if (!fs31.existsSync(srcDir)) continue;
7430
7548
  const syncFiles = (src, rel) => {
7431
- const entries = fs30.readdirSync(src, { withFileTypes: true });
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
- fs30.copyFileSync(entrySrc, entryDest);
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 (fs30.existsSync(configFilePath)) {
7460
- const configContent = fs30.readFileSync(configFilePath, "utf-8");
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
- fs30.writeFileSync(configFilePath, stringify2(yaml));
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 (fs30.existsSync(mcpPath)) {
7606
+ if (fs31.existsSync(mcpPath)) {
7489
7607
  try {
7490
- const content = fs30.readFileSync(mcpPath, "utf-8");
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
- fs30.writeFileSync(mcpPath, stringify2(yaml));
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 (fs30.existsSync(configFilePath)) {
7650
+ if (fs31.existsSync(configFilePath)) {
7533
7651
  try {
7534
- const content = fs30.readFileSync(configFilePath, "utf-8");
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 (fs30.existsSync(configFilePath)) {
7559
- const configContent = fs30.readFileSync(configFilePath, "utf-8");
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 (!fs30.existsSync(srcDir)) continue;
7707
+ if (!fs31.existsSync(srcDir)) continue;
7590
7708
  const syncFiles = (src, rel) => {
7591
- const entries = fs30.readdirSync(src, { withFileTypes: true });
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
- fs30.copyFileSync(entrySrc, entryDest);
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 (fs30.existsSync(configFilePath)) {
7620
- const configContent = fs30.readFileSync(configFilePath, "utf-8");
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
- fs30.writeFileSync(configFilePath, stringify2(yaml));
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 (fs30.existsSync(mcpPath)) {
7766
+ if (fs31.existsSync(mcpPath)) {
7649
7767
  try {
7650
- const content = fs30.readFileSync(mcpPath, "utf-8");
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
- fs30.writeFileSync(mcpPath, stringify2(yaml));
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 fs34 from "fs";
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 fs31 from "fs";
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 = fs31.readFileSync(configFilePath, "utf-8");
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
- fs31.writeFileSync(configFilePath, configContent);
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 fs32 from "fs";
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) => fs32.existsSync(path33.join(localPath, 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 fs33 from "fs";
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 (fs33.existsSync(project.dataPath)) {
8603
- fs33.rmSync(project.dataPath, { recursive: true, force: true });
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 (fs34.existsSync(packageJsonPath)) {
8627
- return JSON.parse(fs34.readFileSync(packageJsonPath, "utf8")).version;
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 (fs34.existsSync(configFilePath)) {
8753
+ if (fs35.existsSync(configFilePath)) {
8636
8754
  try {
8637
- const content = fs34.readFileSync(configFilePath, "utf-8");
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 (fs34.existsSync(mcpPath)) {
8765
+ if (fs35.existsSync(mcpPath)) {
8648
8766
  try {
8649
- const content = fs34.readFileSync(mcpPath, "utf-8");
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 = fs34.existsSync(configFilePath);
8837
+ let isAlreadyConfigured = fs35.existsSync(configFilePath);
8720
8838
  let currentStorageMode = null;
8721
8839
  if (isAlreadyConfigured) {
8722
8840
  try {
8723
- const configContent = fs34.readFileSync(configFilePath, "utf-8");
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 = fs34.existsSync(localDataPath);
8858
+ const hasLocalData = fs35.existsSync(localDataPath);
8741
8859
  if (isAlreadyConfigured) {
8742
8860
  const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
8743
8861
  if (!continueToMenu) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrce-workflow",
3
- "version": "0.3.30",
3
+ "version": "0.3.31",
4
4
  "description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
5
5
  "author": "RRCE Team",
6
6
  "license": "MIT",