rrce-workflow 0.3.28 → 0.3.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-core/prompts/cleanup.md +199 -0
- package/agent-core/prompts/design.md +8 -1
- package/agent-core/prompts/orchestrator.md +1 -0
- package/dist/index.js +276 -129
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RRCE Cleanup
|
|
3
|
+
description: Extract valuable knowledge from tasks and delete artifacts
|
|
4
|
+
argument-hint: "TASK_SLUG=<slug> [TASK_SLUG_2=<slug>] [--all]"
|
|
5
|
+
tools: ['rrce_get_task', 'rrce_search_knowledge', 'rrce_search_code', 'rrce_delete_task', 'rrce_index_knowledge', 'rrce_list_tasks', 'read', 'write']
|
|
6
|
+
required-args: []
|
|
7
|
+
optional-args:
|
|
8
|
+
- name: TASK_SLUG
|
|
9
|
+
prompt: "Enter task slug(s) to cleanup (comma-separated or leave blank for --all)"
|
|
10
|
+
- name: ALL
|
|
11
|
+
default: "false"
|
|
12
|
+
auto-identity:
|
|
13
|
+
user: "$GIT_USER"
|
|
14
|
+
model: "$AGENT_MODEL"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
You are the Knowledge Cleanup Agent. Extract valuable insights from tasks and safely delete artifacts.
|
|
18
|
+
|
|
19
|
+
## Pipeline Position
|
|
20
|
+
- **Maintenance Agent**: Runs on user-demand only (no automatic scheduling)
|
|
21
|
+
- **Scope**: Single task, multiple tasks, or --all mode for project
|
|
22
|
+
- **Write Scope**: Writes to `{{RRCE_DATA}}/knowledge/` and deletes from `{{RRCE_DATA}}/tasks/`
|
|
23
|
+
|
|
24
|
+
## Mission
|
|
25
|
+
Extract durable knowledge from task artifacts, deduplicate against existing knowledge base, merge or create appropriate knowledge files, then delete task directories.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
- Task must exist (get `{{TASK_SLUG}}` or use `rrce_list_tasks` to discover)
|
|
29
|
+
- Knowledge base should exist (`{{RRCE_DATA}}/knowledge/`)
|
|
30
|
+
|
|
31
|
+
## Workflow
|
|
32
|
+
|
|
33
|
+
### Step 1: Determine Cleanup Scope
|
|
34
|
+
Check `{{TASK_SLUG}}` and `{{ALL}}`:
|
|
35
|
+
- `{{ALL}} == "true"`: Cleanup all tasks for project
|
|
36
|
+
- Single value: Cleanup specific task
|
|
37
|
+
- Multiple comma-separated values: Bulk cleanup (max 10 per batch)
|
|
38
|
+
|
|
39
|
+
Get task list using `rrce_list_tasks(project: "{{WORKSPACE_NAME}}")`
|
|
40
|
+
|
|
41
|
+
### Step 2: For Each Task
|
|
42
|
+
|
|
43
|
+
**2A. Read Task Artifacts**
|
|
44
|
+
- `meta.json` - Status, title, summary, tags
|
|
45
|
+
- `research/*.md` - Requirements, alternatives, best practices
|
|
46
|
+
- `planning/*.md` - Task breakdown, chosen approach, implementation notes
|
|
47
|
+
- `execution/*.md` - Changes made, lessons learned, bugs discovered
|
|
48
|
+
- `docs/*.md` - Final documentation (if present)
|
|
49
|
+
|
|
50
|
+
**2B. Extract Knowledge by Status**
|
|
51
|
+
|
|
52
|
+
**Complete tasks**: Full extraction
|
|
53
|
+
- Research findings and technical decisions
|
|
54
|
+
- Implementation patterns and lessons learned
|
|
55
|
+
- Test results and edge cases
|
|
56
|
+
- Integration notes
|
|
57
|
+
|
|
58
|
+
**In-progress tasks**: Partial extraction
|
|
59
|
+
- Research completed
|
|
60
|
+
- Planning decisions made
|
|
61
|
+
- Implementation progress
|
|
62
|
+
- Blockers discovered
|
|
63
|
+
|
|
64
|
+
**Cancelled tasks**: Learning extraction
|
|
65
|
+
- Why cancelled (scope, blockers, priorities)
|
|
66
|
+
- What was learned (research, prototypes)
|
|
67
|
+
- Avoidable pitfalls
|
|
68
|
+
|
|
69
|
+
**Draft tasks**: Initial research
|
|
70
|
+
- Requirements gathered
|
|
71
|
+
- Alternatives considered
|
|
72
|
+
- Initial findings
|
|
73
|
+
|
|
74
|
+
**2C. Check for Duplicates**
|
|
75
|
+
Use `rrce_search_knowledge` to detect overlapping content:
|
|
76
|
+
- Query with key findings from task
|
|
77
|
+
- Similarity threshold: 0.85+
|
|
78
|
+
- If high match found, note which sections already exist
|
|
79
|
+
|
|
80
|
+
**2D. Decide: Merge vs Create New File**
|
|
81
|
+
|
|
82
|
+
**Criteria for Merging into Existing Files**:
|
|
83
|
+
- Domain-specific files exist (e.g., `authentication-oauth-2026-01-05.md`)
|
|
84
|
+
- Content directly extends existing knowledge
|
|
85
|
+
- File is under 400 lines (headroom for merge)
|
|
86
|
+
- High similarity found (>0.85) in RAG search
|
|
87
|
+
|
|
88
|
+
**Criteria for Creating New Files**:
|
|
89
|
+
- No existing domain file or generic target
|
|
90
|
+
- New technical domain not covered in knowledge base
|
|
91
|
+
- Existing files would exceed 500 lines after merge
|
|
92
|
+
- Content is significantly different from existing
|
|
93
|
+
|
|
94
|
+
**File Naming Convention**:
|
|
95
|
+
- Format: `{domain}-{YYYY-MM-DD}.md`
|
|
96
|
+
- Domain: Extracted from task content (e.g., "authentication", "ui-components", "api-design")
|
|
97
|
+
- Date: Current cleanup date
|
|
98
|
+
- Example: `authentication-oauth-2026-01-05.md`
|
|
99
|
+
|
|
100
|
+
**Merge Targets** (priority order):
|
|
101
|
+
1. Domain-specific file (if match found)
|
|
102
|
+
2. `project-context.md` - general project insights
|
|
103
|
+
3. `architecture.md` - architectural decisions
|
|
104
|
+
4. `mcp-server.md` - MCP tool/agent changes
|
|
105
|
+
5. Create new domain file
|
|
106
|
+
|
|
107
|
+
**2E. Write Knowledge File**
|
|
108
|
+
|
|
109
|
+
If merging:
|
|
110
|
+
- Read existing file
|
|
111
|
+
- Preserve structure and formatting
|
|
112
|
+
- Add new section with insights
|
|
113
|
+
- Update "Updated: YYYY-MM-DD" timestamp
|
|
114
|
+
- Keep file under 500 lines (split if needed)
|
|
115
|
+
|
|
116
|
+
If creating new:
|
|
117
|
+
- Use template structure:
|
|
118
|
+
```markdown
|
|
119
|
+
# [Domain] Insights
|
|
120
|
+
|
|
121
|
+
Updated: YYYY-MM-DD
|
|
122
|
+
|
|
123
|
+
## Summary
|
|
124
|
+
[Brief overview]
|
|
125
|
+
|
|
126
|
+
## Key Findings
|
|
127
|
+
- [Finding 1]
|
|
128
|
+
- [Finding 2]
|
|
129
|
+
|
|
130
|
+
## Related Files
|
|
131
|
+
- Code path or task artifact
|
|
132
|
+
|
|
133
|
+
## Checklist
|
|
134
|
+
- [ ] Follow-up item 1
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 3: Delete Task
|
|
138
|
+
After successful knowledge extraction:
|
|
139
|
+
- Call `rrce_delete_task(project: "{{WORKSPACE_NAME}}", task_slug: "<slug>")`
|
|
140
|
+
- If deletion fails, log error but keep knowledge for manual review
|
|
141
|
+
|
|
142
|
+
### Step 4: Reindex Knowledge
|
|
143
|
+
After all tasks processed:
|
|
144
|
+
- Call `rrce_index_knowledge(project: "{{WORKSPACE_NAME}}")`
|
|
145
|
+
- Report indexing results
|
|
146
|
+
|
|
147
|
+
## Non-Negotiables
|
|
148
|
+
|
|
149
|
+
1. **Extract insights, not artifacts** - Don't copy entire research/planning files verbatim. Summarize durable insights.
|
|
150
|
+
2. **Check duplicates before writing** - Use `rrce_search_knowledge` to avoid redundancy.
|
|
151
|
+
3. **Keep files lean** - Target <500 lines per file. Split if needed.
|
|
152
|
+
4. **Version updates** - Add `Updated: YYYY-MM-DD` to modified sections.
|
|
153
|
+
5. **Handle deletion failures gracefully** - Log errors, keep knowledge for manual review.
|
|
154
|
+
6. **Batch limit** - Max 10 tasks per bulk cleanup. Show progress: "Cleaning up task X of Y..."
|
|
155
|
+
|
|
156
|
+
## Error Handling
|
|
157
|
+
|
|
158
|
+
- Task not found: "Task '{slug}' does not exist. Skipping."
|
|
159
|
+
- No artifacts to extract: Log and proceed to deletion.
|
|
160
|
+
- Knowledge file write fails: "Failed to write knowledge file: {error}. Task not deleted."
|
|
161
|
+
- Delete task fails: "Task deletion failed: {error}. Knowledge preserved for manual review."
|
|
162
|
+
- RAG search fails: Proceed with extraction (may have duplicates).
|
|
163
|
+
|
|
164
|
+
## Progress Reporting
|
|
165
|
+
|
|
166
|
+
For bulk cleanup:
|
|
167
|
+
```
|
|
168
|
+
Cleaning up 3 tasks:
|
|
169
|
+
✓ task-auth-login (knowledge extracted, deleted)
|
|
170
|
+
✓ task-api-refactor (knowledge extracted, deleted)
|
|
171
|
+
⚠ task-ui-refresh (knowledge extracted, delete failed - see logs)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Deliverable
|
|
175
|
+
|
|
176
|
+
- **Knowledge files**: Updated or created in `{{RRCE_DATA}}/knowledge/`
|
|
177
|
+
- **Task directories**: Deleted (or error logged)
|
|
178
|
+
- **Reindex**: `rrce_index_knowledge` executed
|
|
179
|
+
- **Summary**: Tasks cleaned, files created/merged, any failures
|
|
180
|
+
|
|
181
|
+
## Example Flow
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
1. rrce_list_tasks(project) → [task-a, task-b, task-c]
|
|
185
|
+
2. For task-a:
|
|
186
|
+
- Read artifacts
|
|
187
|
+
- Extract insights (status: complete)
|
|
188
|
+
- rrce_search_knowledge("authentication") → Found existing file
|
|
189
|
+
- Merge into authentication-oauth-2025-12-15.md
|
|
190
|
+
- rrce_delete_task(project, "task-a") → Success
|
|
191
|
+
3. For task-b:
|
|
192
|
+
- Read artifacts
|
|
193
|
+
- Extract insights (status: cancelled)
|
|
194
|
+
- rrce_search_knowledge("payment flow") → No match
|
|
195
|
+
- Create payment-flow-2026-01-05.md
|
|
196
|
+
- rrce_delete_task(project, "task-b") → Success
|
|
197
|
+
4. rrce_index_knowledge(project) → Index updated
|
|
198
|
+
5. Report: "Cleaned up 2 tasks. Updated 1 file, created 1 file."
|
|
199
|
+
```
|
|
@@ -333,7 +333,14 @@ After saving plan:
|
|
|
333
333
|
- If **"y"**: Invoke development using task tool (see below)
|
|
334
334
|
- If **"n"**: Provide completion summary, end session
|
|
335
335
|
|
|
336
|
-
### 2.8 Development Handoff
|
|
336
|
+
### 2.8 Development Handoff (CRITICAL)
|
|
337
|
+
|
|
338
|
+
**NEVER call the task tool unless:**
|
|
339
|
+
1. You have completed both research AND planning phases
|
|
340
|
+
2. You have asked "Should I run `/rrce_develop {{TASK_SLUG}}`?"
|
|
341
|
+
3. **The user has explicitly responded with "y"**
|
|
342
|
+
|
|
343
|
+
The initial `/rrce_design` command invocation is NOT confirmation to proceed. It only starts the design session.
|
|
337
344
|
|
|
338
345
|
Wait for user to respond with "y" to the prompt above. Only after receiving explicit user confirmation, then use the `task` tool to delegate:
|
|
339
346
|
|
|
@@ -136,6 +136,7 @@ Use `rrce_search_tasks` to find relevant tasks:
|
|
|
136
136
|
| `/rrce_design` | task-slug "request" | Research + plan in single session |
|
|
137
137
|
| `/rrce_develop` | task-slug | Execute code changes |
|
|
138
138
|
| `/rrce_docs` | doc-type [task-slug] | Generate documentation |
|
|
139
|
+
| `/rrce_cleanup` | task-slug [--all] | Extract knowledge and delete tasks |
|
|
139
140
|
| `/rrce_sync` | [scope] | Sync knowledge base |
|
|
140
141
|
| `/rrce_doctor` | [focus-area] | Health check |
|
|
141
142
|
|
package/dist/index.js
CHANGED
|
@@ -5178,6 +5178,149 @@ var init_agent = __esm({
|
|
|
5178
5178
|
}
|
|
5179
5179
|
});
|
|
5180
5180
|
|
|
5181
|
+
// src/mcp/handlers/tools/cleanup.ts
|
|
5182
|
+
import * as path28 from "path";
|
|
5183
|
+
import * as fs25 from "fs";
|
|
5184
|
+
async function cleanupSingleTask(projectName, taskSlug, projectDataPath) {
|
|
5185
|
+
try {
|
|
5186
|
+
const task = getTask(projectName, taskSlug);
|
|
5187
|
+
if (!task) {
|
|
5188
|
+
return { success: false, message: `Task '${taskSlug}' not found in project '${projectName}'.` };
|
|
5189
|
+
}
|
|
5190
|
+
const taskDir = path28.join(projectDataPath, "tasks", taskSlug);
|
|
5191
|
+
const artifacts = { meta: task };
|
|
5192
|
+
const researchPath = path28.join(taskDir, "research", `${taskSlug}-research.md`);
|
|
5193
|
+
if (fs25.existsSync(researchPath)) {
|
|
5194
|
+
artifacts.research = fs25.readFileSync(researchPath, "utf-8");
|
|
5195
|
+
}
|
|
5196
|
+
const planningPath = path28.join(taskDir, "planning", `${taskSlug}-plan.md`);
|
|
5197
|
+
if (fs25.existsSync(planningPath)) {
|
|
5198
|
+
artifacts.planning = fs25.readFileSync(planningPath, "utf-8");
|
|
5199
|
+
}
|
|
5200
|
+
const executionPath = path28.join(taskDir, "execution", `${taskSlug}-execution.md`);
|
|
5201
|
+
if (fs25.existsSync(executionPath)) {
|
|
5202
|
+
artifacts.execution = fs25.readFileSync(executionPath, "utf-8");
|
|
5203
|
+
}
|
|
5204
|
+
const docsPath = path28.join(taskDir, "docs", `${taskSlug}-docs.md`);
|
|
5205
|
+
if (fs25.existsSync(docsPath)) {
|
|
5206
|
+
artifacts.docs = fs25.readFileSync(docsPath, "utf-8");
|
|
5207
|
+
}
|
|
5208
|
+
return {
|
|
5209
|
+
success: true,
|
|
5210
|
+
message: `Task '${taskSlug}' artifacts loaded. ${Object.keys(artifacts).length - 1} artifact files found.`,
|
|
5211
|
+
knowledgeFiles: []
|
|
5212
|
+
};
|
|
5213
|
+
} catch (error) {
|
|
5214
|
+
return {
|
|
5215
|
+
success: false,
|
|
5216
|
+
message: `Failed to load task '${taskSlug}': ${error.message}`
|
|
5217
|
+
};
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
function getProjectDataPath(projectName) {
|
|
5221
|
+
const config = loadMCPConfig();
|
|
5222
|
+
const projects = projectService.scan();
|
|
5223
|
+
const project = projects.find((p) => p.name === projectName && isProjectExposed(config, p.name, p.sourcePath || p.path));
|
|
5224
|
+
if (!project || !project.dataPath) {
|
|
5225
|
+
return null;
|
|
5226
|
+
}
|
|
5227
|
+
return project.dataPath;
|
|
5228
|
+
}
|
|
5229
|
+
async function handleCleanupTool(name, args) {
|
|
5230
|
+
if (!args) return null;
|
|
5231
|
+
if (name === "cleanup_task") {
|
|
5232
|
+
const params = args;
|
|
5233
|
+
const projectDataPath = getProjectDataPath(params.project);
|
|
5234
|
+
if (!projectDataPath) {
|
|
5235
|
+
return {
|
|
5236
|
+
content: [{ type: "text", text: `Project '${params.project}' not found or not exposed.` }],
|
|
5237
|
+
isError: true
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5240
|
+
let taskSlugs = [];
|
|
5241
|
+
if (params.cleanup_all) {
|
|
5242
|
+
const allTasks = getProjectTasks(params.project);
|
|
5243
|
+
taskSlugs = allTasks.map((t) => t.task_slug);
|
|
5244
|
+
} else if (params.task_slugs && params.task_slugs.length > 0) {
|
|
5245
|
+
taskSlugs = params.task_slugs;
|
|
5246
|
+
} else if (params.task_slug) {
|
|
5247
|
+
taskSlugs = [params.task_slug];
|
|
5248
|
+
}
|
|
5249
|
+
if (taskSlugs.length === 0) {
|
|
5250
|
+
return {
|
|
5251
|
+
content: [{ type: "text", text: "No tasks specified for cleanup. Provide task_slug, task_slugs array, or set cleanup_all=true." }],
|
|
5252
|
+
isError: true
|
|
5253
|
+
};
|
|
5254
|
+
}
|
|
5255
|
+
const MAX_BATCH_SIZE = 10;
|
|
5256
|
+
if (taskSlugs.length > MAX_BATCH_SIZE) {
|
|
5257
|
+
return {
|
|
5258
|
+
content: [{ type: "text", text: `Too many tasks (${taskSlugs.length}). Maximum ${MAX_BATCH_SIZE} tasks per batch.` }],
|
|
5259
|
+
isError: true
|
|
5260
|
+
};
|
|
5261
|
+
}
|
|
5262
|
+
const results = await Promise.all(
|
|
5263
|
+
taskSlugs.map((slug) => cleanupSingleTask(params.project, slug, projectDataPath))
|
|
5264
|
+
);
|
|
5265
|
+
const successCount = results.filter((r) => r.success).length;
|
|
5266
|
+
const failureCount = results.filter((r) => !r.success).length;
|
|
5267
|
+
let responseText = `Loaded ${successCount} task(s) for cleanup.`;
|
|
5268
|
+
if (failureCount > 0) {
|
|
5269
|
+
responseText += ` Failed to load ${failureCount} task(s):
|
|
5270
|
+
`;
|
|
5271
|
+
results.filter((r) => !r.success).forEach((r) => {
|
|
5272
|
+
responseText += ` - ${r.message}
|
|
5273
|
+
`;
|
|
5274
|
+
});
|
|
5275
|
+
}
|
|
5276
|
+
responseText += `
|
|
5277
|
+
|
|
5278
|
+
Task artifacts loaded. The cleanup agent will now:
|
|
5279
|
+
`;
|
|
5280
|
+
responseText += `1. Extract knowledge from each task
|
|
5281
|
+
`;
|
|
5282
|
+
responseText += `2. Check for duplicates in knowledge base
|
|
5283
|
+
`;
|
|
5284
|
+
responseText += `3. Merge or create knowledge files
|
|
5285
|
+
`;
|
|
5286
|
+
responseText += `4. Delete task directories
|
|
5287
|
+
`;
|
|
5288
|
+
responseText += `5. Reindex knowledge
|
|
5289
|
+
|
|
5290
|
+
`;
|
|
5291
|
+
responseText += `Proceeding with cleanup...`;
|
|
5292
|
+
return {
|
|
5293
|
+
content: [{ type: "text", text: responseText }]
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
return null;
|
|
5297
|
+
}
|
|
5298
|
+
var cleanupTools;
|
|
5299
|
+
var init_cleanup = __esm({
|
|
5300
|
+
"src/mcp/handlers/tools/cleanup.ts"() {
|
|
5301
|
+
"use strict";
|
|
5302
|
+
init_resources2();
|
|
5303
|
+
init_config();
|
|
5304
|
+
init_detection_service();
|
|
5305
|
+
cleanupTools = [
|
|
5306
|
+
{
|
|
5307
|
+
name: "cleanup_task",
|
|
5308
|
+
description: "Cleanup task(s) by extracting knowledge and deleting artifacts. Supports single task, multiple tasks, or --all mode.",
|
|
5309
|
+
inputSchema: {
|
|
5310
|
+
type: "object",
|
|
5311
|
+
properties: {
|
|
5312
|
+
project: { type: "string", description: "Name of the project" },
|
|
5313
|
+
task_slug: { type: "string", description: "Single task slug to cleanup" },
|
|
5314
|
+
task_slugs: { type: "array", items: { type: "string" }, description: "Multiple task slugs to cleanup" },
|
|
5315
|
+
cleanup_all: { type: "boolean", description: "Cleanup all tasks for project" }
|
|
5316
|
+
},
|
|
5317
|
+
required: ["project"]
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
];
|
|
5321
|
+
}
|
|
5322
|
+
});
|
|
5323
|
+
|
|
5181
5324
|
// src/mcp/handlers/tools.ts
|
|
5182
5325
|
import "@modelcontextprotocol/sdk/server/index.js";
|
|
5183
5326
|
import {
|
|
@@ -5191,7 +5334,8 @@ function registerToolHandlers(server) {
|
|
|
5191
5334
|
...searchTools,
|
|
5192
5335
|
...taskTools,
|
|
5193
5336
|
...sessionTools,
|
|
5194
|
-
...agentTools
|
|
5337
|
+
...agentTools,
|
|
5338
|
+
...cleanupTools
|
|
5195
5339
|
];
|
|
5196
5340
|
const projects = getExposedProjects();
|
|
5197
5341
|
if (projects.length === 0) {
|
|
@@ -5217,6 +5361,8 @@ function registerToolHandlers(server) {
|
|
|
5217
5361
|
if (sessionResult) return sessionResult;
|
|
5218
5362
|
const agentResult = await handleAgentTool(name, args);
|
|
5219
5363
|
if (agentResult) return agentResult;
|
|
5364
|
+
const cleanupResult = await handleCleanupTool(name, args);
|
|
5365
|
+
if (cleanupResult) return cleanupResult;
|
|
5220
5366
|
throw new Error(`Unknown tool: ${name}`);
|
|
5221
5367
|
} catch (error) {
|
|
5222
5368
|
logger.error(`Tool execution failed: ${name}`, error);
|
|
@@ -5234,6 +5380,7 @@ var init_tools = __esm({
|
|
|
5234
5380
|
init_task();
|
|
5235
5381
|
init_session();
|
|
5236
5382
|
init_agent();
|
|
5383
|
+
init_cleanup();
|
|
5237
5384
|
}
|
|
5238
5385
|
});
|
|
5239
5386
|
|
|
@@ -5459,8 +5606,8 @@ Hidden projects: ${projects.length - exposedCount}`,
|
|
|
5459
5606
|
}
|
|
5460
5607
|
async function handleConfigureGlobalPath() {
|
|
5461
5608
|
const { resolveGlobalPath: resolveGlobalPath2 } = await Promise.resolve().then(() => (init_tui_utils(), tui_utils_exports));
|
|
5462
|
-
const
|
|
5463
|
-
const
|
|
5609
|
+
const fs35 = await import("fs");
|
|
5610
|
+
const path35 = await import("path");
|
|
5464
5611
|
note3(
|
|
5465
5612
|
`MCP Hub requires a ${pc5.bold("global storage path")} to store its configuration
|
|
5466
5613
|
and coordinate across projects.
|
|
@@ -5474,8 +5621,8 @@ locally in each project. MCP needs a central location.`,
|
|
|
5474
5621
|
return false;
|
|
5475
5622
|
}
|
|
5476
5623
|
try {
|
|
5477
|
-
if (!
|
|
5478
|
-
|
|
5624
|
+
if (!fs35.existsSync(resolvedPath)) {
|
|
5625
|
+
fs35.mkdirSync(resolvedPath, { recursive: true });
|
|
5479
5626
|
}
|
|
5480
5627
|
const config = loadMCPConfig();
|
|
5481
5628
|
saveMCPConfig(config);
|
|
@@ -5483,7 +5630,7 @@ locally in each project. MCP needs a central location.`,
|
|
|
5483
5630
|
`${pc5.green("\u2713")} Global path configured: ${pc5.cyan(resolvedPath)}
|
|
5484
5631
|
|
|
5485
5632
|
MCP config will be stored at:
|
|
5486
|
-
${
|
|
5633
|
+
${path35.join(resolvedPath, "mcp.yaml")}`,
|
|
5487
5634
|
"Configuration Saved"
|
|
5488
5635
|
);
|
|
5489
5636
|
return true;
|
|
@@ -5587,15 +5734,15 @@ __export(ConfigContext_exports, {
|
|
|
5587
5734
|
useConfig: () => useConfig
|
|
5588
5735
|
});
|
|
5589
5736
|
import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
|
|
5590
|
-
import * as
|
|
5591
|
-
import * as
|
|
5737
|
+
import * as fs26 from "fs";
|
|
5738
|
+
import * as path29 from "path";
|
|
5592
5739
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
5593
5740
|
function getPackageVersion() {
|
|
5594
5741
|
try {
|
|
5595
5742
|
const agentCoreDir = getAgentCoreDir();
|
|
5596
|
-
const packageJsonPath =
|
|
5597
|
-
if (
|
|
5598
|
-
return JSON.parse(
|
|
5743
|
+
const packageJsonPath = path29.join(path29.dirname(agentCoreDir), "package.json");
|
|
5744
|
+
if (fs26.existsSync(packageJsonPath)) {
|
|
5745
|
+
return JSON.parse(fs26.readFileSync(packageJsonPath, "utf8")).version;
|
|
5599
5746
|
}
|
|
5600
5747
|
} catch (e) {
|
|
5601
5748
|
}
|
|
@@ -5662,16 +5809,16 @@ var init_ConfigContext = __esm({
|
|
|
5662
5809
|
});
|
|
5663
5810
|
|
|
5664
5811
|
// src/mcp/ui/lib/tasks-fs.ts
|
|
5665
|
-
import * as
|
|
5666
|
-
import * as
|
|
5812
|
+
import * as fs27 from "fs";
|
|
5813
|
+
import * as path30 from "path";
|
|
5667
5814
|
function readSession(project, taskSlug) {
|
|
5668
5815
|
const rrceData = getProjectRRCEData(project);
|
|
5669
|
-
const sessionPath =
|
|
5670
|
-
if (!
|
|
5816
|
+
const sessionPath = path30.join(rrceData, "tasks", taskSlug, "session.json");
|
|
5817
|
+
if (!fs27.existsSync(sessionPath)) {
|
|
5671
5818
|
return null;
|
|
5672
5819
|
}
|
|
5673
5820
|
try {
|
|
5674
|
-
const raw =
|
|
5821
|
+
const raw = fs27.readFileSync(sessionPath, "utf-8");
|
|
5675
5822
|
return JSON.parse(raw);
|
|
5676
5823
|
} catch {
|
|
5677
5824
|
return null;
|
|
@@ -5684,12 +5831,12 @@ function isSessionStale(session, thresholdMs = SESSION_STALE_THRESHOLD_MS) {
|
|
|
5684
5831
|
}
|
|
5685
5832
|
function readAgentTodos(project, taskSlug) {
|
|
5686
5833
|
const rrceData = getProjectRRCEData(project);
|
|
5687
|
-
const todosPath =
|
|
5688
|
-
if (!
|
|
5834
|
+
const todosPath = path30.join(rrceData, "tasks", taskSlug, "agent-todos.json");
|
|
5835
|
+
if (!fs27.existsSync(todosPath)) {
|
|
5689
5836
|
return null;
|
|
5690
5837
|
}
|
|
5691
5838
|
try {
|
|
5692
|
-
const raw =
|
|
5839
|
+
const raw = fs27.readFileSync(todosPath, "utf-8");
|
|
5693
5840
|
return JSON.parse(raw);
|
|
5694
5841
|
} catch {
|
|
5695
5842
|
return null;
|
|
@@ -5702,8 +5849,8 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5702
5849
|
if (configPath.startsWith(rrceHome)) {
|
|
5703
5850
|
return "global";
|
|
5704
5851
|
}
|
|
5705
|
-
if (
|
|
5706
|
-
const content =
|
|
5852
|
+
if (fs27.existsSync(configPath)) {
|
|
5853
|
+
const content = fs27.readFileSync(configPath, "utf-8");
|
|
5707
5854
|
if (content.includes("mode: workspace")) return "workspace";
|
|
5708
5855
|
if (content.includes("mode: global")) return "global";
|
|
5709
5856
|
}
|
|
@@ -5713,7 +5860,7 @@ function detectStorageModeFromConfig(workspaceRoot) {
|
|
|
5713
5860
|
}
|
|
5714
5861
|
function getEffectiveGlobalBase() {
|
|
5715
5862
|
const dummy = resolveDataPath("global", "__rrce_dummy__", "");
|
|
5716
|
-
return
|
|
5863
|
+
return path30.dirname(path30.dirname(dummy));
|
|
5717
5864
|
}
|
|
5718
5865
|
function getProjectRRCEData(project) {
|
|
5719
5866
|
const workspaceRoot = project.sourcePath || project.path;
|
|
@@ -5722,19 +5869,19 @@ function getProjectRRCEData(project) {
|
|
|
5722
5869
|
}
|
|
5723
5870
|
function listProjectTasks(project) {
|
|
5724
5871
|
const rrceData = getProjectRRCEData(project);
|
|
5725
|
-
const tasksPath =
|
|
5726
|
-
if (!
|
|
5872
|
+
const tasksPath = path30.join(rrceData, "tasks");
|
|
5873
|
+
if (!fs27.existsSync(tasksPath)) {
|
|
5727
5874
|
return { projectName: project.name, tasksPath, tasks: [] };
|
|
5728
5875
|
}
|
|
5729
5876
|
const tasks = [];
|
|
5730
5877
|
try {
|
|
5731
|
-
const entries =
|
|
5878
|
+
const entries = fs27.readdirSync(tasksPath, { withFileTypes: true });
|
|
5732
5879
|
for (const entry of entries) {
|
|
5733
5880
|
if (!entry.isDirectory()) continue;
|
|
5734
|
-
const metaPath =
|
|
5735
|
-
if (!
|
|
5881
|
+
const metaPath = path30.join(tasksPath, entry.name, "meta.json");
|
|
5882
|
+
if (!fs27.existsSync(metaPath)) continue;
|
|
5736
5883
|
try {
|
|
5737
|
-
const raw =
|
|
5884
|
+
const raw = fs27.readFileSync(metaPath, "utf-8");
|
|
5738
5885
|
const meta = JSON.parse(raw);
|
|
5739
5886
|
if (!meta.task_slug) meta.task_slug = entry.name;
|
|
5740
5887
|
tasks.push(meta);
|
|
@@ -5753,18 +5900,18 @@ function listProjectTasks(project) {
|
|
|
5753
5900
|
}
|
|
5754
5901
|
function updateTaskStatus(project, taskSlug, status) {
|
|
5755
5902
|
const rrceData = getProjectRRCEData(project);
|
|
5756
|
-
const metaPath =
|
|
5757
|
-
if (!
|
|
5903
|
+
const metaPath = path30.join(rrceData, "tasks", taskSlug, "meta.json");
|
|
5904
|
+
if (!fs27.existsSync(metaPath)) {
|
|
5758
5905
|
return { ok: false, error: `meta.json not found for task '${taskSlug}'` };
|
|
5759
5906
|
}
|
|
5760
5907
|
try {
|
|
5761
|
-
const meta = JSON.parse(
|
|
5908
|
+
const meta = JSON.parse(fs27.readFileSync(metaPath, "utf-8"));
|
|
5762
5909
|
const next = {
|
|
5763
5910
|
...meta,
|
|
5764
5911
|
status,
|
|
5765
5912
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5766
5913
|
};
|
|
5767
|
-
|
|
5914
|
+
fs27.writeFileSync(metaPath, JSON.stringify(next, null, 2));
|
|
5768
5915
|
return { ok: true, meta: next };
|
|
5769
5916
|
} catch (e) {
|
|
5770
5917
|
return { ok: false, error: String(e) };
|
|
@@ -6149,27 +6296,27 @@ var init_ProjectViews = __esm({
|
|
|
6149
6296
|
});
|
|
6150
6297
|
|
|
6151
6298
|
// src/mcp/ui/lib/projects.ts
|
|
6152
|
-
import * as
|
|
6153
|
-
import * as
|
|
6299
|
+
import * as fs28 from "fs";
|
|
6300
|
+
import * as path31 from "path";
|
|
6154
6301
|
function getIndexStats(project) {
|
|
6155
6302
|
const stats = { knowledgeCount: 0, codeCount: 0, lastIndexed: null };
|
|
6156
6303
|
try {
|
|
6157
6304
|
const knowledgePath = project.knowledgePath;
|
|
6158
6305
|
if (knowledgePath) {
|
|
6159
|
-
const embPath =
|
|
6160
|
-
const codeEmbPath =
|
|
6161
|
-
if (
|
|
6162
|
-
const stat =
|
|
6306
|
+
const embPath = path31.join(knowledgePath, "embeddings.json");
|
|
6307
|
+
const codeEmbPath = path31.join(knowledgePath, "code-embeddings.json");
|
|
6308
|
+
if (fs28.existsSync(embPath)) {
|
|
6309
|
+
const stat = fs28.statSync(embPath);
|
|
6163
6310
|
stats.lastIndexed = stat.mtime.toISOString();
|
|
6164
6311
|
try {
|
|
6165
|
-
const data = JSON.parse(
|
|
6312
|
+
const data = JSON.parse(fs28.readFileSync(embPath, "utf-8"));
|
|
6166
6313
|
stats.knowledgeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6167
6314
|
} catch {
|
|
6168
6315
|
}
|
|
6169
6316
|
}
|
|
6170
|
-
if (
|
|
6317
|
+
if (fs28.existsSync(codeEmbPath)) {
|
|
6171
6318
|
try {
|
|
6172
|
-
const data = JSON.parse(
|
|
6319
|
+
const data = JSON.parse(fs28.readFileSync(codeEmbPath, "utf-8"));
|
|
6173
6320
|
stats.codeCount = Array.isArray(data) ? data.length : Object.keys(data).length;
|
|
6174
6321
|
} catch {
|
|
6175
6322
|
}
|
|
@@ -6914,7 +7061,7 @@ __export(App_exports, {
|
|
|
6914
7061
|
});
|
|
6915
7062
|
import { useState as useState5, useEffect as useEffect6, useMemo as useMemo5, useCallback as useCallback3 } from "react";
|
|
6916
7063
|
import { Box as Box14, useInput as useInput5, useApp } from "ink";
|
|
6917
|
-
import
|
|
7064
|
+
import fs29 from "fs";
|
|
6918
7065
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
6919
7066
|
var App;
|
|
6920
7067
|
var init_App = __esm({
|
|
@@ -6987,18 +7134,18 @@ var init_App = __esm({
|
|
|
6987
7134
|
useEffect6(() => {
|
|
6988
7135
|
const logPath = getLogFilePath();
|
|
6989
7136
|
let lastSize = 0;
|
|
6990
|
-
if (
|
|
6991
|
-
const stats =
|
|
7137
|
+
if (fs29.existsSync(logPath)) {
|
|
7138
|
+
const stats = fs29.statSync(logPath);
|
|
6992
7139
|
lastSize = stats.size;
|
|
6993
7140
|
}
|
|
6994
7141
|
const interval = setInterval(() => {
|
|
6995
|
-
if (
|
|
6996
|
-
const stats =
|
|
7142
|
+
if (fs29.existsSync(logPath)) {
|
|
7143
|
+
const stats = fs29.statSync(logPath);
|
|
6997
7144
|
if (stats.size > lastSize) {
|
|
6998
7145
|
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
6999
|
-
const fd =
|
|
7000
|
-
|
|
7001
|
-
|
|
7146
|
+
const fd = fs29.openSync(logPath, "r");
|
|
7147
|
+
fs29.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
7148
|
+
fs29.closeSync(fd);
|
|
7002
7149
|
const newContent = buffer.toString("utf-8");
|
|
7003
7150
|
const newLines = newContent.split("\n").filter((l) => l.trim());
|
|
7004
7151
|
setLogs((prev) => {
|
|
@@ -7223,15 +7370,15 @@ __export(update_flow_exports, {
|
|
|
7223
7370
|
});
|
|
7224
7371
|
import { confirm as confirm5, spinner as spinner2, note as note6, outro as outro2, cancel as cancel2, isCancel as isCancel7 } from "@clack/prompts";
|
|
7225
7372
|
import pc8 from "picocolors";
|
|
7226
|
-
import * as
|
|
7227
|
-
import * as
|
|
7373
|
+
import * as fs30 from "fs";
|
|
7374
|
+
import * as path32 from "path";
|
|
7228
7375
|
import { stringify as stringify2, parse } from "yaml";
|
|
7229
7376
|
function backupFile(filePath) {
|
|
7230
|
-
if (!
|
|
7377
|
+
if (!fs30.existsSync(filePath)) return null;
|
|
7231
7378
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T")[0] + "-" + Date.now();
|
|
7232
7379
|
const backupPath = `${filePath}.${timestamp}.bak`;
|
|
7233
7380
|
try {
|
|
7234
|
-
|
|
7381
|
+
fs30.copyFileSync(filePath, backupPath);
|
|
7235
7382
|
return backupPath;
|
|
7236
7383
|
} catch (e) {
|
|
7237
7384
|
console.error(`Failed to backup ${filePath}:`, e);
|
|
@@ -7241,9 +7388,9 @@ function backupFile(filePath) {
|
|
|
7241
7388
|
function getPackageVersion2() {
|
|
7242
7389
|
try {
|
|
7243
7390
|
const agentCoreDir = getAgentCoreDir();
|
|
7244
|
-
const packageJsonPath =
|
|
7245
|
-
if (
|
|
7246
|
-
return JSON.parse(
|
|
7391
|
+
const packageJsonPath = path32.join(path32.dirname(agentCoreDir), "package.json");
|
|
7392
|
+
if (fs30.existsSync(packageJsonPath)) {
|
|
7393
|
+
return JSON.parse(fs30.readFileSync(packageJsonPath, "utf8")).version;
|
|
7247
7394
|
}
|
|
7248
7395
|
} catch (e) {
|
|
7249
7396
|
}
|
|
@@ -7258,9 +7405,9 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7258
7405
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7259
7406
|
const configFilePath = getConfigPath(workspacePath);
|
|
7260
7407
|
let currentSyncedVersion;
|
|
7261
|
-
if (
|
|
7408
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7262
7409
|
try {
|
|
7263
|
-
const content =
|
|
7410
|
+
const content = fs30.readFileSync(configFilePath, "utf-8");
|
|
7264
7411
|
const config = parse(content);
|
|
7265
7412
|
currentSyncedVersion = config.last_synced_version;
|
|
7266
7413
|
} catch (e) {
|
|
@@ -7268,8 +7415,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7268
7415
|
}
|
|
7269
7416
|
const driftReport = DriftService.checkDrift(dataPaths[0], currentSyncedVersion, runningVersion);
|
|
7270
7417
|
const ideTargets = [];
|
|
7271
|
-
if (
|
|
7272
|
-
const configContent =
|
|
7418
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7419
|
+
const configContent = fs30.readFileSync(configFilePath, "utf-8");
|
|
7273
7420
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7274
7421
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7275
7422
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7278,14 +7425,14 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7278
7425
|
const dirs = ["templates", "prompts", "docs"];
|
|
7279
7426
|
const updatedFiles = [];
|
|
7280
7427
|
for (const dir of dirs) {
|
|
7281
|
-
const srcDir =
|
|
7282
|
-
if (!
|
|
7428
|
+
const srcDir = path32.join(agentCoreDir, dir);
|
|
7429
|
+
if (!fs30.existsSync(srcDir)) continue;
|
|
7283
7430
|
const syncFiles = (src, rel) => {
|
|
7284
|
-
const entries =
|
|
7431
|
+
const entries = fs30.readdirSync(src, { withFileTypes: true });
|
|
7285
7432
|
for (const entry of entries) {
|
|
7286
|
-
const entrySrc =
|
|
7287
|
-
const entryRel =
|
|
7288
|
-
const entryDest =
|
|
7433
|
+
const entrySrc = path32.join(src, entry.name);
|
|
7434
|
+
const entryRel = path32.join(rel, entry.name);
|
|
7435
|
+
const entryDest = path32.join(dataPath, entryRel);
|
|
7289
7436
|
if (entry.isDirectory()) {
|
|
7290
7437
|
ensureDir(entryDest);
|
|
7291
7438
|
syncFiles(entrySrc, entryRel);
|
|
@@ -7293,8 +7440,8 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7293
7440
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
7294
7441
|
backupFile(entryDest);
|
|
7295
7442
|
}
|
|
7296
|
-
ensureDir(
|
|
7297
|
-
|
|
7443
|
+
ensureDir(path32.dirname(entryDest));
|
|
7444
|
+
fs30.copyFileSync(entrySrc, entryDest);
|
|
7298
7445
|
updatedFiles.push(entryRel);
|
|
7299
7446
|
}
|
|
7300
7447
|
}
|
|
@@ -7305,12 +7452,12 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7305
7452
|
DriftService.saveManifest(dataPath, manifest);
|
|
7306
7453
|
}
|
|
7307
7454
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
7308
|
-
ensureDir(
|
|
7309
|
-
ensureDir(
|
|
7310
|
-
copyDirRecursive(
|
|
7311
|
-
copyDirRecursive(
|
|
7312
|
-
if (
|
|
7313
|
-
const configContent =
|
|
7455
|
+
ensureDir(path32.join(rrceHome, "templates"));
|
|
7456
|
+
ensureDir(path32.join(rrceHome, "docs"));
|
|
7457
|
+
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7458
|
+
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7459
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7460
|
+
const configContent = fs30.readFileSync(configFilePath, "utf-8");
|
|
7314
7461
|
if (configContent.includes("copilot: true")) {
|
|
7315
7462
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7316
7463
|
ensureDir(copilotPath);
|
|
@@ -7332,21 +7479,21 @@ async function performUpdate(workspacePath, workspaceName, currentStorageMode, o
|
|
|
7332
7479
|
try {
|
|
7333
7480
|
const yaml = parse(configContent);
|
|
7334
7481
|
yaml.last_synced_version = runningVersion;
|
|
7335
|
-
|
|
7482
|
+
fs30.writeFileSync(configFilePath, stringify2(yaml));
|
|
7336
7483
|
} catch (e) {
|
|
7337
7484
|
console.error("Failed to update config.yaml version:", e);
|
|
7338
7485
|
}
|
|
7339
7486
|
}
|
|
7340
|
-
const mcpPath =
|
|
7341
|
-
if (
|
|
7487
|
+
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7488
|
+
if (fs30.existsSync(mcpPath)) {
|
|
7342
7489
|
try {
|
|
7343
|
-
const content =
|
|
7490
|
+
const content = fs30.readFileSync(mcpPath, "utf-8");
|
|
7344
7491
|
const yaml = parse(content);
|
|
7345
7492
|
if (yaml.projects) {
|
|
7346
7493
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7347
7494
|
if (project) {
|
|
7348
7495
|
project.last_synced_version = runningVersion;
|
|
7349
|
-
|
|
7496
|
+
fs30.writeFileSync(mcpPath, stringify2(yaml));
|
|
7350
7497
|
}
|
|
7351
7498
|
}
|
|
7352
7499
|
} catch (e) {
|
|
@@ -7382,9 +7529,9 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7382
7529
|
const dataPaths = resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspacePath, customGlobalPath);
|
|
7383
7530
|
const configFilePath = getConfigPath(workspacePath);
|
|
7384
7531
|
let currentSyncedVersion;
|
|
7385
|
-
if (
|
|
7532
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7386
7533
|
try {
|
|
7387
|
-
const content =
|
|
7534
|
+
const content = fs30.readFileSync(configFilePath, "utf-8");
|
|
7388
7535
|
const config = parse(content);
|
|
7389
7536
|
currentSyncedVersion = config.last_synced_version;
|
|
7390
7537
|
} catch (e) {
|
|
@@ -7408,8 +7555,8 @@ async function runUpdateFlow(workspacePath, workspaceName, currentStorageMode) {
|
|
|
7408
7555
|
` \u2022 docs/ (documentation)`
|
|
7409
7556
|
];
|
|
7410
7557
|
const ideTargets = [];
|
|
7411
|
-
if (
|
|
7412
|
-
const configContent =
|
|
7558
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7559
|
+
const configContent = fs30.readFileSync(configFilePath, "utf-8");
|
|
7413
7560
|
if (configContent.includes("opencode: true")) ideTargets.push("OpenCode agents");
|
|
7414
7561
|
if (configContent.includes("copilot: true")) ideTargets.push("GitHub Copilot");
|
|
7415
7562
|
if (configContent.includes("antigravity: true")) ideTargets.push("Antigravity");
|
|
@@ -7438,14 +7585,14 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7438
7585
|
const dirs = ["templates", "prompts", "docs"];
|
|
7439
7586
|
const updatedFiles = [];
|
|
7440
7587
|
for (const dir of dirs) {
|
|
7441
|
-
const srcDir =
|
|
7442
|
-
if (!
|
|
7588
|
+
const srcDir = path32.join(agentCoreDir, dir);
|
|
7589
|
+
if (!fs30.existsSync(srcDir)) continue;
|
|
7443
7590
|
const syncFiles = (src, rel) => {
|
|
7444
|
-
const entries =
|
|
7591
|
+
const entries = fs30.readdirSync(src, { withFileTypes: true });
|
|
7445
7592
|
for (const entry of entries) {
|
|
7446
|
-
const entrySrc =
|
|
7447
|
-
const entryRel =
|
|
7448
|
-
const entryDest =
|
|
7593
|
+
const entrySrc = path32.join(src, entry.name);
|
|
7594
|
+
const entryRel = path32.join(rel, entry.name);
|
|
7595
|
+
const entryDest = path32.join(dataPath, entryRel);
|
|
7449
7596
|
if (entry.isDirectory()) {
|
|
7450
7597
|
ensureDir(entryDest);
|
|
7451
7598
|
syncFiles(entrySrc, entryRel);
|
|
@@ -7453,8 +7600,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7453
7600
|
if (driftReport.modifiedFiles.includes(entryRel)) {
|
|
7454
7601
|
backupFile(entryDest);
|
|
7455
7602
|
}
|
|
7456
|
-
ensureDir(
|
|
7457
|
-
|
|
7603
|
+
ensureDir(path32.dirname(entryDest));
|
|
7604
|
+
fs30.copyFileSync(entrySrc, entryDest);
|
|
7458
7605
|
updatedFiles.push(entryRel);
|
|
7459
7606
|
}
|
|
7460
7607
|
}
|
|
@@ -7465,12 +7612,12 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7465
7612
|
DriftService.saveManifest(dataPath, manifest);
|
|
7466
7613
|
}
|
|
7467
7614
|
const rrceHome = customGlobalPath || getDefaultRRCEHome2();
|
|
7468
|
-
ensureDir(
|
|
7469
|
-
ensureDir(
|
|
7470
|
-
copyDirRecursive(
|
|
7471
|
-
copyDirRecursive(
|
|
7472
|
-
if (
|
|
7473
|
-
const configContent =
|
|
7615
|
+
ensureDir(path32.join(rrceHome, "templates"));
|
|
7616
|
+
ensureDir(path32.join(rrceHome, "docs"));
|
|
7617
|
+
copyDirRecursive(path32.join(agentCoreDir, "templates"), path32.join(rrceHome, "templates"));
|
|
7618
|
+
copyDirRecursive(path32.join(agentCoreDir, "docs"), path32.join(rrceHome, "docs"));
|
|
7619
|
+
if (fs30.existsSync(configFilePath)) {
|
|
7620
|
+
const configContent = fs30.readFileSync(configFilePath, "utf-8");
|
|
7474
7621
|
if (configContent.includes("copilot: true")) {
|
|
7475
7622
|
const copilotPath = getAgentPromptPath(workspacePath, "copilot");
|
|
7476
7623
|
ensureDir(copilotPath);
|
|
@@ -7492,21 +7639,21 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7492
7639
|
try {
|
|
7493
7640
|
const yaml = parse(configContent);
|
|
7494
7641
|
yaml.last_synced_version = runningVersion;
|
|
7495
|
-
|
|
7642
|
+
fs30.writeFileSync(configFilePath, stringify2(yaml));
|
|
7496
7643
|
} catch (e) {
|
|
7497
7644
|
console.error("Failed to update config.yaml version:", e);
|
|
7498
7645
|
}
|
|
7499
7646
|
}
|
|
7500
|
-
const mcpPath =
|
|
7501
|
-
if (
|
|
7647
|
+
const mcpPath = path32.join(rrceHome, "mcp.yaml");
|
|
7648
|
+
if (fs30.existsSync(mcpPath)) {
|
|
7502
7649
|
try {
|
|
7503
|
-
const content =
|
|
7650
|
+
const content = fs30.readFileSync(mcpPath, "utf-8");
|
|
7504
7651
|
const yaml = parse(content);
|
|
7505
7652
|
if (yaml.projects) {
|
|
7506
7653
|
const project = yaml.projects.find((p) => p.name === workspaceName);
|
|
7507
7654
|
if (project) {
|
|
7508
7655
|
project.last_synced_version = runningVersion;
|
|
7509
|
-
|
|
7656
|
+
fs30.writeFileSync(mcpPath, stringify2(yaml));
|
|
7510
7657
|
}
|
|
7511
7658
|
}
|
|
7512
7659
|
} catch (e) {
|
|
@@ -7541,8 +7688,8 @@ ${dataPaths.map((p) => ` \u2022 ${p}`).join("\n")}`,
|
|
|
7541
7688
|
}
|
|
7542
7689
|
}
|
|
7543
7690
|
function resolveAllDataPathsWithCustomGlobal(mode, workspaceName, workspaceRoot, customGlobalPath) {
|
|
7544
|
-
const globalPath =
|
|
7545
|
-
const workspacePath =
|
|
7691
|
+
const globalPath = path32.join(customGlobalPath, "workspaces", workspaceName);
|
|
7692
|
+
const workspacePath = path32.join(workspaceRoot, ".rrce-workflow");
|
|
7546
7693
|
switch (mode) {
|
|
7547
7694
|
case "global":
|
|
7548
7695
|
return [globalPath];
|
|
@@ -7565,8 +7712,8 @@ var init_update_flow = __esm({
|
|
|
7565
7712
|
// src/commands/wizard/index.ts
|
|
7566
7713
|
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";
|
|
7567
7714
|
import pc13 from "picocolors";
|
|
7568
|
-
import * as
|
|
7569
|
-
import * as
|
|
7715
|
+
import * as fs34 from "fs";
|
|
7716
|
+
import * as path34 from "path";
|
|
7570
7717
|
import { parse as parse2 } from "yaml";
|
|
7571
7718
|
|
|
7572
7719
|
// src/lib/git.ts
|
|
@@ -8243,7 +8390,7 @@ async function handlePostSetup(config, workspacePath, workspaceName, linkedProje
|
|
|
8243
8390
|
init_paths();
|
|
8244
8391
|
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";
|
|
8245
8392
|
import pc10 from "picocolors";
|
|
8246
|
-
import * as
|
|
8393
|
+
import * as fs31 from "fs";
|
|
8247
8394
|
init_detection();
|
|
8248
8395
|
async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
8249
8396
|
const projects = scanForProjects({
|
|
@@ -8282,7 +8429,7 @@ async function runLinkProjectsFlow(workspacePath, workspaceName) {
|
|
|
8282
8429
|
const s = spinner4();
|
|
8283
8430
|
s.start("Linking projects");
|
|
8284
8431
|
const configFilePath = getConfigPath(workspacePath);
|
|
8285
|
-
let configContent =
|
|
8432
|
+
let configContent = fs31.readFileSync(configFilePath, "utf-8");
|
|
8286
8433
|
if (configContent.includes("linked_projects:")) {
|
|
8287
8434
|
const lines = configContent.split("\n");
|
|
8288
8435
|
const linkedIndex = lines.findIndex((l) => l.trim() === "linked_projects:");
|
|
@@ -8309,7 +8456,7 @@ linked_projects:
|
|
|
8309
8456
|
`;
|
|
8310
8457
|
});
|
|
8311
8458
|
}
|
|
8312
|
-
|
|
8459
|
+
fs31.writeFileSync(configFilePath, configContent);
|
|
8313
8460
|
generateVSCodeWorkspace(workspacePath, workspaceName, selectedProjects, customGlobalPath);
|
|
8314
8461
|
s.stop("Projects linked");
|
|
8315
8462
|
const workspaceFile = `${workspaceName}.code-workspace`;
|
|
@@ -8345,15 +8492,15 @@ init_paths();
|
|
|
8345
8492
|
init_utils();
|
|
8346
8493
|
import { confirm as confirm8, spinner as spinner5, note as note9, outro as outro5, cancel as cancel5, isCancel as isCancel10 } from "@clack/prompts";
|
|
8347
8494
|
import pc11 from "picocolors";
|
|
8348
|
-
import * as
|
|
8349
|
-
import * as
|
|
8495
|
+
import * as fs32 from "fs";
|
|
8496
|
+
import * as path33 from "path";
|
|
8350
8497
|
async function runSyncToGlobalFlow(workspacePath, workspaceName) {
|
|
8351
8498
|
const localPath = getLocalWorkspacePath(workspacePath);
|
|
8352
8499
|
const customGlobalPath = getEffectiveRRCEHome(workspacePath);
|
|
8353
|
-
const globalPath =
|
|
8500
|
+
const globalPath = path33.join(customGlobalPath, "workspaces", workspaceName);
|
|
8354
8501
|
const subdirs = ["knowledge", "prompts", "templates", "tasks", "refs"];
|
|
8355
8502
|
const existingDirs = subdirs.filter(
|
|
8356
|
-
(dir) =>
|
|
8503
|
+
(dir) => fs32.existsSync(path33.join(localPath, dir))
|
|
8357
8504
|
);
|
|
8358
8505
|
if (existingDirs.length === 0) {
|
|
8359
8506
|
outro5(pc11.yellow("No data found in workspace storage to sync."));
|
|
@@ -8379,8 +8526,8 @@ Destination: ${pc11.cyan(globalPath)}`,
|
|
|
8379
8526
|
try {
|
|
8380
8527
|
ensureDir(globalPath);
|
|
8381
8528
|
for (const dir of existingDirs) {
|
|
8382
|
-
const srcDir =
|
|
8383
|
-
const destDir =
|
|
8529
|
+
const srcDir = path33.join(localPath, dir);
|
|
8530
|
+
const destDir = path33.join(globalPath, dir);
|
|
8384
8531
|
ensureDir(destDir);
|
|
8385
8532
|
copyDirRecursive(srcDir, destDir);
|
|
8386
8533
|
}
|
|
@@ -8408,7 +8555,7 @@ init_update_flow();
|
|
|
8408
8555
|
// src/commands/wizard/delete-flow.ts
|
|
8409
8556
|
import { multiselect as multiselect5, confirm as confirm9, spinner as spinner6, note as note10, cancel as cancel6, isCancel as isCancel11 } from "@clack/prompts";
|
|
8410
8557
|
import pc12 from "picocolors";
|
|
8411
|
-
import * as
|
|
8558
|
+
import * as fs33 from "fs";
|
|
8412
8559
|
init_detection();
|
|
8413
8560
|
init_config();
|
|
8414
8561
|
async function runDeleteGlobalProjectFlow(availableProjects) {
|
|
@@ -8452,8 +8599,8 @@ Are you sure?`,
|
|
|
8452
8599
|
for (const projectName of projectsToDelete) {
|
|
8453
8600
|
const project = globalProjects.find((p) => p.name === projectName);
|
|
8454
8601
|
if (!project) continue;
|
|
8455
|
-
if (
|
|
8456
|
-
|
|
8602
|
+
if (fs33.existsSync(project.dataPath)) {
|
|
8603
|
+
fs33.rmSync(project.dataPath, { recursive: true, force: true });
|
|
8457
8604
|
}
|
|
8458
8605
|
const newConfig = removeProjectConfig(mcpConfig, projectName);
|
|
8459
8606
|
configChanged = true;
|
|
@@ -8475,9 +8622,9 @@ init_config();
|
|
|
8475
8622
|
function getPackageVersion3() {
|
|
8476
8623
|
try {
|
|
8477
8624
|
const agentCoreDir = getAgentCoreDir();
|
|
8478
|
-
const packageJsonPath =
|
|
8479
|
-
if (
|
|
8480
|
-
return JSON.parse(
|
|
8625
|
+
const packageJsonPath = path34.join(path34.dirname(agentCoreDir), "package.json");
|
|
8626
|
+
if (fs34.existsSync(packageJsonPath)) {
|
|
8627
|
+
return JSON.parse(fs34.readFileSync(packageJsonPath, "utf8")).version;
|
|
8481
8628
|
}
|
|
8482
8629
|
} catch (e) {
|
|
8483
8630
|
}
|
|
@@ -8485,9 +8632,9 @@ function getPackageVersion3() {
|
|
|
8485
8632
|
}
|
|
8486
8633
|
function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
8487
8634
|
const configFilePath = getConfigPath(workspacePath);
|
|
8488
|
-
if (
|
|
8635
|
+
if (fs34.existsSync(configFilePath)) {
|
|
8489
8636
|
try {
|
|
8490
|
-
const content =
|
|
8637
|
+
const content = fs34.readFileSync(configFilePath, "utf-8");
|
|
8491
8638
|
const config = parse2(content);
|
|
8492
8639
|
if (config.last_synced_version) {
|
|
8493
8640
|
return config.last_synced_version;
|
|
@@ -8496,10 +8643,10 @@ function getLastSyncedVersion(workspacePath, workspaceName) {
|
|
|
8496
8643
|
}
|
|
8497
8644
|
}
|
|
8498
8645
|
const rrceHome = getEffectiveRRCEHome(workspacePath) || getDefaultRRCEHome2();
|
|
8499
|
-
const mcpPath =
|
|
8500
|
-
if (
|
|
8646
|
+
const mcpPath = path34.join(rrceHome, "mcp.yaml");
|
|
8647
|
+
if (fs34.existsSync(mcpPath)) {
|
|
8501
8648
|
try {
|
|
8502
|
-
const content =
|
|
8649
|
+
const content = fs34.readFileSync(mcpPath, "utf-8");
|
|
8503
8650
|
const config = parse2(content);
|
|
8504
8651
|
const project = config.projects?.find((p) => p.name === workspaceName);
|
|
8505
8652
|
if (project?.last_synced_version) {
|
|
@@ -8569,11 +8716,11 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8569
8716
|
workspacePath
|
|
8570
8717
|
});
|
|
8571
8718
|
const configFilePath = getConfigPath(workspacePath);
|
|
8572
|
-
let isAlreadyConfigured =
|
|
8719
|
+
let isAlreadyConfigured = fs34.existsSync(configFilePath);
|
|
8573
8720
|
let currentStorageMode = null;
|
|
8574
8721
|
if (isAlreadyConfigured) {
|
|
8575
8722
|
try {
|
|
8576
|
-
const configContent =
|
|
8723
|
+
const configContent = fs34.readFileSync(configFilePath, "utf-8");
|
|
8577
8724
|
const modeMatch = configContent.match(/mode:\s*(global|workspace)/);
|
|
8578
8725
|
currentStorageMode = modeMatch?.[1] ?? null;
|
|
8579
8726
|
} catch {
|
|
@@ -8590,7 +8737,7 @@ Workspace: ${pc13.bold(workspaceName)}`,
|
|
|
8590
8737
|
}
|
|
8591
8738
|
}
|
|
8592
8739
|
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
8593
|
-
const hasLocalData =
|
|
8740
|
+
const hasLocalData = fs34.existsSync(localDataPath);
|
|
8594
8741
|
if (isAlreadyConfigured) {
|
|
8595
8742
|
const continueToMenu = await checkAndPromptUpdate(workspacePath, workspaceName, currentStorageMode);
|
|
8596
8743
|
if (!continueToMenu) {
|