rrce-workflow 0.2.14 → 0.2.15
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/bin/rrce-workflow.js +3 -33
- package/dist/commands/selector.d.ts +1 -0
- package/dist/commands/selector.js +29 -0
- package/dist/commands/wizard/index.d.ts +1 -0
- package/dist/commands/wizard/index.js +86 -0
- package/dist/commands/wizard/link-flow.d.ts +5 -0
- package/dist/commands/wizard/link-flow.js +97 -0
- package/dist/commands/wizard/setup-flow.d.ts +4 -0
- package/dist/commands/wizard/setup-flow.js +262 -0
- package/dist/commands/wizard/sync-flow.d.ts +4 -0
- package/dist/commands/wizard/sync-flow.js +67 -0
- package/dist/commands/wizard/update-flow.d.ts +4 -0
- package/dist/commands/wizard/update-flow.js +85 -0
- package/dist/commands/wizard/utils.d.ts +9 -0
- package/dist/commands/wizard/utils.js +33 -0
- package/dist/commands/wizard/vscode.d.ts +15 -0
- package/dist/commands/wizard/vscode.js +148 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1191 -0
- package/dist/lib/autocomplete-prompt.d.ts +14 -0
- package/dist/lib/autocomplete-prompt.js +167 -0
- package/dist/lib/detection.d.ts +44 -0
- package/dist/lib/detection.js +185 -0
- package/dist/lib/git.d.ts +12 -0
- package/dist/lib/git.js +37 -0
- package/dist/lib/paths.d.ts +108 -0
- package/dist/lib/paths.js +296 -0
- package/dist/lib/prompts.d.ts +18 -0
- package/dist/lib/prompts.js +62 -0
- package/dist/types/prompt.d.ts +54 -0
- package/dist/types/prompt.js +20 -0
- package/package.json +10 -7
- package/src/commands/selector.ts +0 -42
- package/src/commands/wizard/index.ts +0 -114
- package/src/commands/wizard/link-flow.ts +0 -118
- package/src/commands/wizard/setup-flow.ts +0 -347
- package/src/commands/wizard/sync-flow.ts +0 -93
- package/src/commands/wizard/update-flow.ts +0 -124
- package/src/commands/wizard/utils.ts +0 -38
- package/src/commands/wizard/vscode.ts +0 -197
- package/src/index.ts +0 -11
- package/src/lib/autocomplete-prompt.ts +0 -190
- package/src/lib/detection.ts +0 -235
- package/src/lib/git.ts +0 -37
- package/src/lib/paths.ts +0 -332
- package/src/lib/prompts.ts +0 -73
- package/src/types/prompt.ts +0 -54
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
// Environment variables
|
|
4
|
+
const RRCE_HOME = process.env.RRCE_HOME || path.join(process.env.HOME || '~', '.rrce-workflow');
|
|
5
|
+
const RRCE_WORKSPACE = process.env.RRCE_WORKSPACE;
|
|
6
|
+
/**
|
|
7
|
+
* Detect workspace root by walking up from CWD
|
|
8
|
+
*/
|
|
9
|
+
export function detectWorkspaceRoot() {
|
|
10
|
+
if (RRCE_WORKSPACE) {
|
|
11
|
+
return RRCE_WORKSPACE;
|
|
12
|
+
}
|
|
13
|
+
let current = process.cwd();
|
|
14
|
+
while (current !== '/') {
|
|
15
|
+
// Check for .git or .rrce-workflow/config.yaml (new location)
|
|
16
|
+
// Also check legacy .rrce-workflow.yaml for backwards compatibility
|
|
17
|
+
if (fs.existsSync(path.join(current, '.git')) ||
|
|
18
|
+
fs.existsSync(path.join(current, '.rrce-workflow', 'config.yaml')) ||
|
|
19
|
+
fs.existsSync(path.join(current, '.rrce-workflow.yaml'))) {
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
current = path.dirname(current);
|
|
23
|
+
}
|
|
24
|
+
return process.cwd();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the config file path for a workspace
|
|
28
|
+
* New location: .rrce-workflow/config.yaml
|
|
29
|
+
* Legacy location: .rrce-workflow.yaml (for backwards compatibility)
|
|
30
|
+
*/
|
|
31
|
+
export function getConfigPath(workspaceRoot) {
|
|
32
|
+
const newPath = path.join(workspaceRoot, '.rrce-workflow', 'config.yaml');
|
|
33
|
+
const legacyPath = path.join(workspaceRoot, '.rrce-workflow.yaml');
|
|
34
|
+
// Prefer new location, fall back to legacy
|
|
35
|
+
if (fs.existsSync(newPath)) {
|
|
36
|
+
return newPath;
|
|
37
|
+
}
|
|
38
|
+
if (fs.existsSync(legacyPath)) {
|
|
39
|
+
return legacyPath;
|
|
40
|
+
}
|
|
41
|
+
// Default to new location for new configs
|
|
42
|
+
return newPath;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get workspace name from directory or config
|
|
46
|
+
*/
|
|
47
|
+
export function getWorkspaceName(workspaceRoot) {
|
|
48
|
+
// TODO: Check .rrce-workflow.yaml for project.name
|
|
49
|
+
return path.basename(workspaceRoot);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve primary data path based on storage mode
|
|
53
|
+
* Note: For 'both' mode, use resolveAllDataPaths() to get all paths
|
|
54
|
+
*/
|
|
55
|
+
export function resolveDataPath(mode, workspaceName, workspaceRoot) {
|
|
56
|
+
switch (mode) {
|
|
57
|
+
case 'global':
|
|
58
|
+
return path.join(RRCE_HOME, 'workspaces', workspaceName);
|
|
59
|
+
case 'workspace':
|
|
60
|
+
return path.join(workspaceRoot, '.rrce-workflow');
|
|
61
|
+
case 'both':
|
|
62
|
+
// Primary is workspace for 'both' mode
|
|
63
|
+
return path.join(workspaceRoot, '.rrce-workflow');
|
|
64
|
+
default:
|
|
65
|
+
return path.join(RRCE_HOME, 'workspaces', workspaceName);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve ALL data paths based on storage mode
|
|
70
|
+
* Returns array of paths where data should be stored:
|
|
71
|
+
* - 'global': [~/.rrce-workflow/workspaces/<name>]
|
|
72
|
+
* - 'workspace': [<workspace>/.rrce-workflow]
|
|
73
|
+
* - 'both': [<workspace>/.rrce-workflow, ~/.rrce-workflow/workspaces/<name>]
|
|
74
|
+
*/
|
|
75
|
+
export function resolveAllDataPaths(mode, workspaceName, workspaceRoot) {
|
|
76
|
+
const globalPath = path.join(RRCE_HOME, 'workspaces', workspaceName);
|
|
77
|
+
const workspacePath = path.join(workspaceRoot, '.rrce-workflow');
|
|
78
|
+
switch (mode) {
|
|
79
|
+
case 'global':
|
|
80
|
+
return [globalPath];
|
|
81
|
+
case 'workspace':
|
|
82
|
+
return [workspacePath];
|
|
83
|
+
case 'both':
|
|
84
|
+
return [workspacePath, globalPath];
|
|
85
|
+
default:
|
|
86
|
+
return [globalPath];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get RRCE home directory
|
|
91
|
+
*/
|
|
92
|
+
export function getRRCEHome() {
|
|
93
|
+
return RRCE_HOME;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* List all projects in global storage
|
|
97
|
+
* @param excludeWorkspace - Workspace name to exclude from the list (typically current workspace)
|
|
98
|
+
* @returns Array of project names found in ~/.rrce-workflow/workspaces/
|
|
99
|
+
*/
|
|
100
|
+
export function listGlobalProjects(excludeWorkspace) {
|
|
101
|
+
const workspacesDir = path.join(RRCE_HOME, 'workspaces');
|
|
102
|
+
if (!fs.existsSync(workspacesDir)) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const entries = fs.readdirSync(workspacesDir, { withFileTypes: true });
|
|
107
|
+
return entries
|
|
108
|
+
.filter(entry => entry.isDirectory() && entry.name !== excludeWorkspace)
|
|
109
|
+
.map(entry => entry.name);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the knowledge path for a global project
|
|
117
|
+
*/
|
|
118
|
+
export function getGlobalProjectKnowledgePath(projectName) {
|
|
119
|
+
return path.join(RRCE_HOME, 'workspaces', projectName, 'knowledge');
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the global workspace data path for a project
|
|
123
|
+
*/
|
|
124
|
+
export function getGlobalWorkspacePath(workspaceName) {
|
|
125
|
+
return path.join(RRCE_HOME, 'workspaces', workspaceName);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the local workspace data path
|
|
129
|
+
*/
|
|
130
|
+
export function getLocalWorkspacePath(workspaceRoot) {
|
|
131
|
+
return path.join(workspaceRoot, '.rrce-workflow');
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Ensure directory exists
|
|
135
|
+
*/
|
|
136
|
+
export function ensureDir(dirPath) {
|
|
137
|
+
if (!fs.existsSync(dirPath)) {
|
|
138
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get path for agent prompts based on tool
|
|
143
|
+
* IDE-specific locations so IDEs can auto-discover prompts
|
|
144
|
+
*/
|
|
145
|
+
export function getAgentPromptPath(workspaceRoot, tool) {
|
|
146
|
+
if (tool === 'copilot') {
|
|
147
|
+
return path.join(workspaceRoot, '.github', 'agents');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
return path.join(workspaceRoot, '.agent', 'workflows');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Copy a file to all storage paths
|
|
155
|
+
* @param sourceFile - Absolute path to source file
|
|
156
|
+
* @param relativePath - Relative path within the data directory (e.g., 'knowledge/context.md')
|
|
157
|
+
* @param dataPaths - Array of data paths from resolveAllDataPaths()
|
|
158
|
+
*/
|
|
159
|
+
export function copyToAllStoragePaths(sourceFile, relativePath, dataPaths) {
|
|
160
|
+
const content = fs.readFileSync(sourceFile);
|
|
161
|
+
for (const dataPath of dataPaths) {
|
|
162
|
+
const targetPath = path.join(dataPath, relativePath);
|
|
163
|
+
ensureDir(path.dirname(targetPath));
|
|
164
|
+
fs.writeFileSync(targetPath, content);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Write content to a file in all storage paths
|
|
169
|
+
* @param content - Content to write
|
|
170
|
+
* @param relativePath - Relative path within the data directory
|
|
171
|
+
* @param dataPaths - Array of data paths from resolveAllDataPaths()
|
|
172
|
+
*/
|
|
173
|
+
export function writeToAllStoragePaths(content, relativePath, dataPaths) {
|
|
174
|
+
for (const dataPath of dataPaths) {
|
|
175
|
+
const targetPath = path.join(dataPath, relativePath);
|
|
176
|
+
ensureDir(path.dirname(targetPath));
|
|
177
|
+
fs.writeFileSync(targetPath, content);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Copy a directory recursively to all storage paths
|
|
182
|
+
* @param sourceDir - Absolute path to source directory
|
|
183
|
+
* @param relativeDir - Relative directory path within the data directory
|
|
184
|
+
* @param dataPaths - Array of data paths from resolveAllDataPaths()
|
|
185
|
+
*/
|
|
186
|
+
export function copyDirToAllStoragePaths(sourceDir, relativeDir, dataPaths) {
|
|
187
|
+
if (!fs.existsSync(sourceDir)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
191
|
+
for (const entry of entries) {
|
|
192
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
193
|
+
const relativePath = path.join(relativeDir, entry.name);
|
|
194
|
+
if (entry.isDirectory()) {
|
|
195
|
+
copyDirToAllStoragePaths(sourcePath, relativePath, dataPaths);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
copyToAllStoragePaths(sourcePath, relativePath, dataPaths);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Sync metadata subdirectories (knowledge, refs, tasks) to all storage paths
|
|
204
|
+
* Copies from agent-core to all configured storage locations
|
|
205
|
+
*/
|
|
206
|
+
export function syncMetadataToAll(agentCorePath, dataPaths) {
|
|
207
|
+
const metadataDirs = ['knowledge', 'refs', 'tasks'];
|
|
208
|
+
for (const dir of metadataDirs) {
|
|
209
|
+
const sourceDir = path.join(agentCorePath, dir);
|
|
210
|
+
copyDirToAllStoragePaths(sourceDir, dir, dataPaths);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if a directory path is writable
|
|
215
|
+
* Creates a test file and removes it to verify write access
|
|
216
|
+
*/
|
|
217
|
+
export function checkWriteAccess(dirPath) {
|
|
218
|
+
const testFile = path.join(dirPath, '.rrce-write-test');
|
|
219
|
+
try {
|
|
220
|
+
// Ensure directory exists first
|
|
221
|
+
if (!fs.existsSync(dirPath)) {
|
|
222
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
// Try to write and delete a test file
|
|
225
|
+
fs.writeFileSync(testFile, 'write-test');
|
|
226
|
+
fs.unlinkSync(testFile);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Clean up if test file was created but couldn't be deleted
|
|
231
|
+
try {
|
|
232
|
+
if (fs.existsSync(testFile)) {
|
|
233
|
+
fs.unlinkSync(testFile);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Ignore cleanup errors
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the default RRCE_HOME path (from env or ~/.rrce-workflow)
|
|
244
|
+
*/
|
|
245
|
+
export function getDefaultRRCEHome() {
|
|
246
|
+
return process.env.RRCE_HOME || path.join(process.env.HOME || '~', '.rrce-workflow');
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get suggested global paths for user selection
|
|
250
|
+
* Returns array of { path, label, isWritable } objects
|
|
251
|
+
*/
|
|
252
|
+
export function getSuggestedGlobalPaths() {
|
|
253
|
+
const suggestions = [];
|
|
254
|
+
// Option 1: RRCE_HOME environment variable (if explicitly set)
|
|
255
|
+
if (process.env.RRCE_HOME) {
|
|
256
|
+
suggestions.push({
|
|
257
|
+
path: process.env.RRCE_HOME,
|
|
258
|
+
label: 'RRCE_HOME (environment)',
|
|
259
|
+
isWritable: checkWriteAccess(process.env.RRCE_HOME),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Option 2: Standard ~/.rrce-workflow
|
|
263
|
+
const homeDefault = path.join(process.env.HOME || '~', '.rrce-workflow');
|
|
264
|
+
if (!process.env.RRCE_HOME || process.env.RRCE_HOME !== homeDefault) {
|
|
265
|
+
suggestions.push({
|
|
266
|
+
path: homeDefault,
|
|
267
|
+
label: '~/.rrce-workflow (default)',
|
|
268
|
+
isWritable: checkWriteAccess(homeDefault),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return suggestions;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get effective RRCE_HOME by reading from workspace config if available
|
|
275
|
+
* Falls back to default RRCE_HOME if no custom path is configured
|
|
276
|
+
*/
|
|
277
|
+
export function getEffectiveRRCEHome(workspaceRoot) {
|
|
278
|
+
// Check workspace config for custom globalPath
|
|
279
|
+
if (workspaceRoot) {
|
|
280
|
+
const configPath = getConfigPath(workspaceRoot);
|
|
281
|
+
if (fs.existsSync(configPath)) {
|
|
282
|
+
try {
|
|
283
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
284
|
+
const globalPathMatch = content.match(/globalPath:\s*["']?([^"'\n]+)["']?/);
|
|
285
|
+
if (globalPathMatch?.[1]) {
|
|
286
|
+
return globalPathMatch[1].trim();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Ignore parse errors
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Fall back to default
|
|
295
|
+
return getDefaultRRCEHome();
|
|
296
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ParsedPrompt } from '../types/prompt';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a prompt file and extract frontmatter + content
|
|
4
|
+
*/
|
|
5
|
+
export declare function parsePromptFile(filePath: string): ParsedPrompt | null;
|
|
6
|
+
/**
|
|
7
|
+
* Load all prompts from a directory
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadPromptsFromDir(dirPath: string): ParsedPrompt[];
|
|
10
|
+
/**
|
|
11
|
+
* Get the agent-core root directory
|
|
12
|
+
* Works with both npm/tsx and Bun
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAgentCoreDir(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Get the agent-core prompts directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function getAgentCorePromptsDir(): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
import { PromptFrontmatterSchema } from '../types/prompt';
|
|
6
|
+
// Get __dirname equivalent for ESM (works with both npm/tsx and Bun)
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
/**
|
|
10
|
+
* Parse a prompt file and extract frontmatter + content
|
|
11
|
+
*/
|
|
12
|
+
export function parsePromptFile(filePath) {
|
|
13
|
+
try {
|
|
14
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
15
|
+
const { data, content } = matter(fileContent);
|
|
16
|
+
const parsed = PromptFrontmatterSchema.safeParse(data);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
console.error(`Failed to parse frontmatter in ${filePath}:`, parsed.error);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
frontmatter: parsed.data,
|
|
23
|
+
content: content.trim(),
|
|
24
|
+
filePath,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`Error reading prompt file ${filePath}:`, error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load all prompts from a directory
|
|
34
|
+
*/
|
|
35
|
+
export function loadPromptsFromDir(dirPath) {
|
|
36
|
+
if (!fs.existsSync(dirPath)) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.md'));
|
|
40
|
+
const prompts = [];
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
const prompt = parsePromptFile(path.join(dirPath, file));
|
|
43
|
+
if (prompt) {
|
|
44
|
+
prompts.push(prompt);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return prompts;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the agent-core root directory
|
|
51
|
+
* Works with both npm/tsx and Bun
|
|
52
|
+
*/
|
|
53
|
+
export function getAgentCoreDir() {
|
|
54
|
+
// Relative to this file: src/lib/prompts.ts -> ../../agent-core
|
|
55
|
+
return path.join(__dirname, '..', '..', 'agent-core');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the agent-core prompts directory
|
|
59
|
+
*/
|
|
60
|
+
export function getAgentCorePromptsDir() {
|
|
61
|
+
return path.join(getAgentCoreDir(), 'prompts');
|
|
62
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const PromptArgSchema: z.ZodObject<{
|
|
3
|
+
name: z.ZodString;
|
|
4
|
+
default: z.ZodOptional<z.ZodString>;
|
|
5
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export declare const AutoIdentitySchema: z.ZodObject<{
|
|
8
|
+
user: z.ZodString;
|
|
9
|
+
model: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export declare const PromptFrontmatterSchema: z.ZodObject<{
|
|
12
|
+
name: z.ZodString;
|
|
13
|
+
description: z.ZodString;
|
|
14
|
+
'argument-hint': z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
15
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
16
|
+
'required-args': z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
default: z.ZodOptional<z.ZodString>;
|
|
19
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strip>>>;
|
|
21
|
+
'optional-args': z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
22
|
+
name: z.ZodString;
|
|
23
|
+
default: z.ZodOptional<z.ZodString>;
|
|
24
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>>>;
|
|
26
|
+
'auto-identity': z.ZodOptional<z.ZodObject<{
|
|
27
|
+
user: z.ZodString;
|
|
28
|
+
model: z.ZodString;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
export type PromptArg = z.infer<typeof PromptArgSchema>;
|
|
32
|
+
export type AutoIdentity = z.infer<typeof AutoIdentitySchema>;
|
|
33
|
+
export type PromptFrontmatter = z.infer<typeof PromptFrontmatterSchema>;
|
|
34
|
+
export interface ParsedPrompt {
|
|
35
|
+
frontmatter: PromptFrontmatter;
|
|
36
|
+
content: string;
|
|
37
|
+
filePath: string;
|
|
38
|
+
}
|
|
39
|
+
export type StorageMode = 'global' | 'workspace' | 'both';
|
|
40
|
+
export interface RRCEConfig {
|
|
41
|
+
version: number;
|
|
42
|
+
storage: {
|
|
43
|
+
mode: StorageMode;
|
|
44
|
+
globalPath?: string;
|
|
45
|
+
};
|
|
46
|
+
project: {
|
|
47
|
+
name: string;
|
|
48
|
+
};
|
|
49
|
+
tools: {
|
|
50
|
+
copilot: boolean;
|
|
51
|
+
antigravity: boolean;
|
|
52
|
+
};
|
|
53
|
+
linked_projects?: string[];
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Prompt frontmatter schema
|
|
3
|
+
export const PromptArgSchema = z.object({
|
|
4
|
+
name: z.string(),
|
|
5
|
+
default: z.string().optional(),
|
|
6
|
+
prompt: z.string().optional(),
|
|
7
|
+
});
|
|
8
|
+
export const AutoIdentitySchema = z.object({
|
|
9
|
+
user: z.string(),
|
|
10
|
+
model: z.string(),
|
|
11
|
+
});
|
|
12
|
+
export const PromptFrontmatterSchema = z.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
description: z.string(),
|
|
15
|
+
'argument-hint': z.union([z.string(), z.array(z.string())]).optional(),
|
|
16
|
+
tools: z.array(z.string()).optional(),
|
|
17
|
+
'required-args': z.array(PromptArgSchema).optional(),
|
|
18
|
+
'optional-args': z.array(PromptArgSchema).optional(),
|
|
19
|
+
'auto-identity': AutoIdentitySchema.optional(),
|
|
20
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rrce-workflow",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "RRCE-Workflow TUI - Agentic code workflow generator for AI-assisted development",
|
|
5
5
|
"author": "RRCE Team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
"tui",
|
|
21
21
|
"cli",
|
|
22
22
|
"code-generation",
|
|
23
|
-
"agentic"
|
|
24
|
-
"ink"
|
|
23
|
+
"agentic"
|
|
25
24
|
],
|
|
26
25
|
"type": "module",
|
|
27
|
-
"
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
28
|
"bin": {
|
|
29
29
|
"rrce-workflow": "bin/rrce-workflow.js"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
|
-
"
|
|
32
|
+
"dist",
|
|
33
33
|
"agent-core",
|
|
34
34
|
"docs",
|
|
35
35
|
"bin"
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
"dev": "npx tsx src/index.ts",
|
|
39
39
|
"wizard": "npx tsx src/index.ts wizard",
|
|
40
40
|
"select": "npx tsx src/index.ts select",
|
|
41
|
-
"start": "npx tsx src/index.ts"
|
|
41
|
+
"start": "npx tsx src/index.ts",
|
|
42
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
42
44
|
},
|
|
43
45
|
"engines": {
|
|
44
46
|
"node": ">=18"
|
|
@@ -48,11 +50,12 @@
|
|
|
48
50
|
"@clack/prompts": "^0.11.0",
|
|
49
51
|
"gray-matter": "^4.0.3",
|
|
50
52
|
"picocolors": "^1.1.1",
|
|
51
|
-
"tsx": "^4.21.0",
|
|
52
53
|
"zod": "^4.2.1"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"@types/node": "^25.0.3",
|
|
57
|
+
"esbuild": "^0.27.2",
|
|
58
|
+
"tsx": "^4.21.0",
|
|
56
59
|
"typescript": "^5.9.3"
|
|
57
60
|
}
|
|
58
61
|
}
|
package/src/commands/selector.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { intro, select, note, cancel, isCancel, outro } from '@clack/prompts';
|
|
2
|
-
import pc from 'picocolors';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { loadPromptsFromDir, getAgentCorePromptsDir } from '../lib/prompts';
|
|
5
|
-
import type { ParsedPrompt } from '../types/prompt';
|
|
6
|
-
|
|
7
|
-
export async function runSelector() {
|
|
8
|
-
const workspaceName = path.basename(process.cwd());
|
|
9
|
-
|
|
10
|
-
intro(pc.cyan(pc.inverse(` RRCE-Workflow | ${workspaceName} `)));
|
|
11
|
-
|
|
12
|
-
const prompts = loadPromptsFromDir(getAgentCorePromptsDir());
|
|
13
|
-
|
|
14
|
-
if (prompts.length === 0) {
|
|
15
|
-
cancel('No agents found. Run `rrce-workflow` to set up.');
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const selection = await select({
|
|
20
|
-
message: 'Select an agent:',
|
|
21
|
-
options: prompts.map(p => ({
|
|
22
|
-
value: p,
|
|
23
|
-
label: p.frontmatter.name,
|
|
24
|
-
hint: p.frontmatter.description
|
|
25
|
-
})),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (isCancel(selection)) {
|
|
29
|
-
cancel('Selection cancelled.');
|
|
30
|
-
process.exit(0);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const prompt = selection as ParsedPrompt;
|
|
34
|
-
|
|
35
|
-
note(
|
|
36
|
-
`Use this agent in your IDE by invoking:
|
|
37
|
-
${pc.bold(pc.cyan(`@${prompt.frontmatter.name}`))}`,
|
|
38
|
-
'Agent Selected'
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
outro('Done');
|
|
42
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { intro, select, spinner, note, outro, cancel, isCancel } from '@clack/prompts';
|
|
2
|
-
import pc from 'picocolors';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import { getGitUser } from '../../lib/git';
|
|
5
|
-
import {
|
|
6
|
-
detectWorkspaceRoot,
|
|
7
|
-
getWorkspaceName,
|
|
8
|
-
listGlobalProjects,
|
|
9
|
-
getLocalWorkspacePath,
|
|
10
|
-
getConfigPath
|
|
11
|
-
} from '../../lib/paths';
|
|
12
|
-
|
|
13
|
-
// Import flows
|
|
14
|
-
import { runSetupFlow } from './setup-flow';
|
|
15
|
-
import { runLinkProjectsFlow } from './link-flow';
|
|
16
|
-
import { runSyncToGlobalFlow } from './sync-flow';
|
|
17
|
-
import { runUpdateFlow } from './update-flow';
|
|
18
|
-
|
|
19
|
-
export async function runWizard() {
|
|
20
|
-
intro(pc.cyan(pc.inverse(' RRCE-Workflow Setup ')));
|
|
21
|
-
|
|
22
|
-
const s = spinner();
|
|
23
|
-
s.start('Detecting environment');
|
|
24
|
-
|
|
25
|
-
const workspacePath = detectWorkspaceRoot();
|
|
26
|
-
const workspaceName = getWorkspaceName(workspacePath);
|
|
27
|
-
const gitUser = getGitUser();
|
|
28
|
-
|
|
29
|
-
await new Promise(r => setTimeout(r, 800)); // Dramatic pause
|
|
30
|
-
s.stop('Environment detected');
|
|
31
|
-
|
|
32
|
-
note(
|
|
33
|
-
`Git User: ${pc.bold(gitUser || '(not found)')}
|
|
34
|
-
Workspace: ${pc.bold(workspaceName)}`,
|
|
35
|
-
'Context'
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Check for existing projects in global storage
|
|
39
|
-
const existingProjects = listGlobalProjects(workspaceName);
|
|
40
|
-
|
|
41
|
-
// Check if already configured (using getConfigPath for new/legacy support)
|
|
42
|
-
const configFilePath = getConfigPath(workspacePath);
|
|
43
|
-
const isAlreadyConfigured = fs.existsSync(configFilePath);
|
|
44
|
-
|
|
45
|
-
// Check current storage mode from config
|
|
46
|
-
let currentStorageMode: string | null = null;
|
|
47
|
-
if (isAlreadyConfigured) {
|
|
48
|
-
try {
|
|
49
|
-
const configContent = fs.readFileSync(configFilePath, 'utf-8');
|
|
50
|
-
const modeMatch = configContent.match(/mode:\s*(global|workspace|both)/);
|
|
51
|
-
currentStorageMode = modeMatch?.[1] ?? null;
|
|
52
|
-
} catch {
|
|
53
|
-
// Ignore parse errors
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Check if workspace has local data that could be synced
|
|
58
|
-
const localDataPath = getLocalWorkspacePath(workspacePath);
|
|
59
|
-
const hasLocalData = fs.existsSync(localDataPath);
|
|
60
|
-
|
|
61
|
-
// If already configured, show menu
|
|
62
|
-
if (isAlreadyConfigured) {
|
|
63
|
-
const menuOptions: { value: string; label: string; hint?: string }[] = [];
|
|
64
|
-
|
|
65
|
-
// Add link option if other projects exist
|
|
66
|
-
if (existingProjects.length > 0) {
|
|
67
|
-
menuOptions.push({
|
|
68
|
-
value: 'link',
|
|
69
|
-
label: 'Link other project knowledge',
|
|
70
|
-
hint: `${existingProjects.length} projects available`
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add sync to global option if using workspace-only mode
|
|
75
|
-
if (currentStorageMode === 'workspace' && hasLocalData) {
|
|
76
|
-
menuOptions.push({
|
|
77
|
-
value: 'sync-global',
|
|
78
|
-
label: 'Sync to global storage',
|
|
79
|
-
hint: 'Share knowledge with other projects'
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
menuOptions.push({ value: 'update', label: 'Update from package', hint: 'Get latest prompts & templates' });
|
|
84
|
-
menuOptions.push({ value: 'exit', label: 'Exit' });
|
|
85
|
-
|
|
86
|
-
const action = await select({
|
|
87
|
-
message: 'This workspace is already configured. What would you like to do?',
|
|
88
|
-
options: menuOptions,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (isCancel(action) || action === 'exit') {
|
|
92
|
-
outro('Exited.');
|
|
93
|
-
process.exit(0);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (action === 'link') {
|
|
97
|
-
await runLinkProjectsFlow(workspacePath, workspaceName, existingProjects);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (action === 'sync-global') {
|
|
102
|
-
await runSyncToGlobalFlow(workspacePath, workspaceName);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (action === 'update') {
|
|
107
|
-
await runUpdateFlow(workspacePath, workspaceName, currentStorageMode);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Run full setup flow for new workspaces
|
|
113
|
-
await runSetupFlow(workspacePath, workspaceName, existingProjects);
|
|
114
|
-
}
|