rrce-workflow 0.2.7 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -48
- package/agent-core/prompts/documentation.md +15 -11
- package/agent-core/prompts/executor.md +15 -8
- package/agent-core/prompts/init.md +26 -12
- package/agent-core/prompts/planning_orchestrator.md +15 -11
- package/agent-core/prompts/research_discussion.md +15 -11
- package/agent-core/prompts/sync.md +15 -11
- package/agent-core/templates/documentation_output.md +1 -1
- package/agent-core/templates/executor_output.md +1 -1
- package/agent-core/templates/init_output.md +1 -1
- package/agent-core/templates/planning_output.md +1 -1
- package/agent-core/templates/research_output.md +1 -1
- package/bin/rrce-workflow.js +9 -2
- package/package.json +10 -8
- package/src/commands/wizard/link-flow.ts +32 -15
- package/src/commands/wizard/setup-flow.ts +9 -10
- package/src/commands/wizard/vscode.ts +153 -22
- package/src/lib/autocomplete-prompt.ts +190 -0
- package/src/lib/detection.ts +235 -0
- package/src/lib/prompts.ts +9 -2
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { StorageMode } from '../types/prompt';
|
|
4
|
+
import { getDefaultRRCEHome } from './paths';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Detected rrce-workflow project information
|
|
8
|
+
*/
|
|
9
|
+
export interface DetectedProject {
|
|
10
|
+
name: string;
|
|
11
|
+
path: string; // Absolute path to project root
|
|
12
|
+
dataPath: string; // Path to .rrce-workflow data directory
|
|
13
|
+
source: 'global' | 'sibling' | 'parent';
|
|
14
|
+
storageMode?: StorageMode;
|
|
15
|
+
knowledgePath?: string;
|
|
16
|
+
refsPath?: string;
|
|
17
|
+
tasksPath?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ScanOptions {
|
|
21
|
+
excludeWorkspace?: string; // Current workspace name to exclude
|
|
22
|
+
workspacePath?: string; // Current workspace path for sibling detection
|
|
23
|
+
scanSiblings?: boolean; // Whether to scan sibling directories (default: true)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Scan for rrce-workflow projects in various locations
|
|
28
|
+
*/
|
|
29
|
+
export function scanForProjects(options: ScanOptions = {}): DetectedProject[] {
|
|
30
|
+
const { excludeWorkspace, workspacePath, scanSiblings = true } = options;
|
|
31
|
+
const projects: DetectedProject[] = [];
|
|
32
|
+
const seenPaths = new Set<string>();
|
|
33
|
+
|
|
34
|
+
// 1. Scan global storage (~/.rrce-workflow/workspaces/)
|
|
35
|
+
const globalProjects = scanGlobalStorage(excludeWorkspace);
|
|
36
|
+
for (const project of globalProjects) {
|
|
37
|
+
if (!seenPaths.has(project.path)) {
|
|
38
|
+
seenPaths.add(project.path);
|
|
39
|
+
projects.push(project);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2. Scan sibling directories (same parent as current workspace)
|
|
44
|
+
if (scanSiblings && workspacePath) {
|
|
45
|
+
const siblingProjects = scanSiblingDirectories(workspacePath, excludeWorkspace);
|
|
46
|
+
for (const project of siblingProjects) {
|
|
47
|
+
if (!seenPaths.has(project.path)) {
|
|
48
|
+
seenPaths.add(project.path);
|
|
49
|
+
projects.push(project);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return projects;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Scan global storage for projects
|
|
59
|
+
*/
|
|
60
|
+
function scanGlobalStorage(excludeWorkspace?: string): DetectedProject[] {
|
|
61
|
+
const rrceHome = getDefaultRRCEHome();
|
|
62
|
+
const workspacesDir = path.join(rrceHome, 'workspaces');
|
|
63
|
+
const projects: DetectedProject[] = [];
|
|
64
|
+
|
|
65
|
+
if (!fs.existsSync(workspacesDir)) {
|
|
66
|
+
return projects;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const entries = fs.readdirSync(workspacesDir, { withFileTypes: true });
|
|
71
|
+
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (!entry.isDirectory()) continue;
|
|
74
|
+
if (entry.name === excludeWorkspace) continue;
|
|
75
|
+
|
|
76
|
+
const projectDataPath = path.join(workspacesDir, entry.name);
|
|
77
|
+
const knowledgePath = path.join(projectDataPath, 'knowledge');
|
|
78
|
+
const refsPath = path.join(projectDataPath, 'refs');
|
|
79
|
+
const tasksPath = path.join(projectDataPath, 'tasks');
|
|
80
|
+
|
|
81
|
+
projects.push({
|
|
82
|
+
name: entry.name,
|
|
83
|
+
path: projectDataPath, // For global projects, path is the data path
|
|
84
|
+
dataPath: projectDataPath,
|
|
85
|
+
source: 'global',
|
|
86
|
+
knowledgePath: fs.existsSync(knowledgePath) ? knowledgePath : undefined,
|
|
87
|
+
refsPath: fs.existsSync(refsPath) ? refsPath : undefined,
|
|
88
|
+
tasksPath: fs.existsSync(tasksPath) ? tasksPath : undefined,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Ignore errors
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return projects;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Scan sibling directories for workspace-scoped projects
|
|
100
|
+
*/
|
|
101
|
+
function scanSiblingDirectories(workspacePath: string, excludeWorkspace?: string): DetectedProject[] {
|
|
102
|
+
const parentDir = path.dirname(workspacePath);
|
|
103
|
+
const projects: DetectedProject[] = [];
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const entries = fs.readdirSync(parentDir, { withFileTypes: true });
|
|
107
|
+
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
if (!entry.isDirectory()) continue;
|
|
110
|
+
|
|
111
|
+
const projectPath = path.join(parentDir, entry.name);
|
|
112
|
+
|
|
113
|
+
// Skip current workspace
|
|
114
|
+
if (projectPath === workspacePath) continue;
|
|
115
|
+
if (entry.name === excludeWorkspace) continue;
|
|
116
|
+
|
|
117
|
+
// Check for .rrce-workflow/config.yaml
|
|
118
|
+
const configPath = path.join(projectPath, '.rrce-workflow', 'config.yaml');
|
|
119
|
+
if (!fs.existsSync(configPath)) continue;
|
|
120
|
+
|
|
121
|
+
// Parse config to get project details
|
|
122
|
+
const config = parseWorkspaceConfig(configPath);
|
|
123
|
+
if (!config) continue;
|
|
124
|
+
|
|
125
|
+
const dataPath = path.join(projectPath, '.rrce-workflow');
|
|
126
|
+
const knowledgePath = path.join(dataPath, 'knowledge');
|
|
127
|
+
const refsPath = path.join(dataPath, 'refs');
|
|
128
|
+
const tasksPath = path.join(dataPath, 'tasks');
|
|
129
|
+
|
|
130
|
+
projects.push({
|
|
131
|
+
name: config.name || entry.name,
|
|
132
|
+
path: projectPath,
|
|
133
|
+
dataPath,
|
|
134
|
+
source: 'sibling',
|
|
135
|
+
storageMode: config.storageMode,
|
|
136
|
+
knowledgePath: fs.existsSync(knowledgePath) ? knowledgePath : undefined,
|
|
137
|
+
refsPath: fs.existsSync(refsPath) ? refsPath : undefined,
|
|
138
|
+
tasksPath: fs.existsSync(tasksPath) ? tasksPath : undefined,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
// Ignore errors
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return projects;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parse a workspace config file
|
|
150
|
+
*/
|
|
151
|
+
export function parseWorkspaceConfig(configPath: string): {
|
|
152
|
+
name: string;
|
|
153
|
+
storageMode: StorageMode;
|
|
154
|
+
linkedProjects?: string[];
|
|
155
|
+
} | null {
|
|
156
|
+
try {
|
|
157
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
158
|
+
|
|
159
|
+
// Simple YAML parsing (we don't want to add a full YAML library)
|
|
160
|
+
const nameMatch = content.match(/name:\s*["']?([^"'\n]+)["']?/);
|
|
161
|
+
const modeMatch = content.match(/mode:\s*(global|workspace|both)/);
|
|
162
|
+
|
|
163
|
+
// Parse linked projects
|
|
164
|
+
const linkedProjects: string[] = [];
|
|
165
|
+
const linkedMatch = content.match(/linked_projects:\s*\n((?:\s+-\s+[^\n]+\n?)+)/);
|
|
166
|
+
if (linkedMatch && linkedMatch[1]) {
|
|
167
|
+
const lines = linkedMatch[1].split('\n');
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
const projectMatch = line.match(/^\s+-\s+(.+)$/);
|
|
170
|
+
if (projectMatch && projectMatch[1]) {
|
|
171
|
+
linkedProjects.push(projectMatch[1].trim());
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
name: nameMatch?.[1]?.trim() || path.basename(path.dirname(path.dirname(configPath))),
|
|
178
|
+
storageMode: (modeMatch?.[1] as StorageMode) || 'global',
|
|
179
|
+
linkedProjects: linkedProjects.length > 0 ? linkedProjects : undefined,
|
|
180
|
+
};
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get display label for a detected project
|
|
188
|
+
*/
|
|
189
|
+
export function getProjectDisplayLabel(project: DetectedProject): string {
|
|
190
|
+
switch (project.source) {
|
|
191
|
+
case 'global':
|
|
192
|
+
return `global: ~/.rrce-workflow/workspaces/${project.name}`;
|
|
193
|
+
case 'sibling':
|
|
194
|
+
return `sibling: ${project.path}/.rrce-workflow`;
|
|
195
|
+
default:
|
|
196
|
+
return project.dataPath;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get all linkable folders from a detected project
|
|
202
|
+
*/
|
|
203
|
+
export function getProjectFolders(project: DetectedProject): Array<{
|
|
204
|
+
path: string;
|
|
205
|
+
type: 'knowledge' | 'refs' | 'tasks';
|
|
206
|
+
displayName: string;
|
|
207
|
+
}> {
|
|
208
|
+
const folders: Array<{ path: string; type: 'knowledge' | 'refs' | 'tasks'; displayName: string }> = [];
|
|
209
|
+
|
|
210
|
+
if (project.knowledgePath) {
|
|
211
|
+
folders.push({
|
|
212
|
+
path: project.knowledgePath,
|
|
213
|
+
type: 'knowledge',
|
|
214
|
+
displayName: `📚 ${project.name} (knowledge)`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (project.refsPath) {
|
|
219
|
+
folders.push({
|
|
220
|
+
path: project.refsPath,
|
|
221
|
+
type: 'refs',
|
|
222
|
+
displayName: `📎 ${project.name} (refs)`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (project.tasksPath) {
|
|
227
|
+
folders.push({
|
|
228
|
+
path: project.tasksPath,
|
|
229
|
+
type: 'tasks',
|
|
230
|
+
displayName: `📋 ${project.name} (tasks)`,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return folders;
|
|
235
|
+
}
|
package/src/lib/prompts.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
3
4
|
import matter from 'gray-matter';
|
|
4
5
|
import { PromptFrontmatterSchema, type ParsedPrompt } from '../types/prompt';
|
|
5
6
|
|
|
7
|
+
// Get __dirname equivalent for ESM (works with both npm/tsx and Bun)
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
6
11
|
/**
|
|
7
12
|
* Parse a prompt file and extract frontmatter + content
|
|
8
13
|
*/
|
|
@@ -52,10 +57,11 @@ export function loadPromptsFromDir(dirPath: string): ParsedPrompt[] {
|
|
|
52
57
|
|
|
53
58
|
/**
|
|
54
59
|
* Get the agent-core root directory
|
|
60
|
+
* Works with both npm/tsx and Bun
|
|
55
61
|
*/
|
|
56
62
|
export function getAgentCoreDir(): string {
|
|
57
|
-
// Relative to
|
|
58
|
-
return path.join(
|
|
63
|
+
// Relative to this file: src/lib/prompts.ts -> ../../agent-core
|
|
64
|
+
return path.join(__dirname, '..', '..', 'agent-core');
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
@@ -64,3 +70,4 @@ export function getAgentCoreDir(): string {
|
|
|
64
70
|
export function getAgentCorePromptsDir(): string {
|
|
65
71
|
return path.join(getAgentCoreDir(), 'prompts');
|
|
66
72
|
}
|
|
73
|
+
|