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.
Files changed (47) hide show
  1. package/bin/rrce-workflow.js +3 -33
  2. package/dist/commands/selector.d.ts +1 -0
  3. package/dist/commands/selector.js +29 -0
  4. package/dist/commands/wizard/index.d.ts +1 -0
  5. package/dist/commands/wizard/index.js +86 -0
  6. package/dist/commands/wizard/link-flow.d.ts +5 -0
  7. package/dist/commands/wizard/link-flow.js +97 -0
  8. package/dist/commands/wizard/setup-flow.d.ts +4 -0
  9. package/dist/commands/wizard/setup-flow.js +262 -0
  10. package/dist/commands/wizard/sync-flow.d.ts +4 -0
  11. package/dist/commands/wizard/sync-flow.js +67 -0
  12. package/dist/commands/wizard/update-flow.d.ts +4 -0
  13. package/dist/commands/wizard/update-flow.js +85 -0
  14. package/dist/commands/wizard/utils.d.ts +9 -0
  15. package/dist/commands/wizard/utils.js +33 -0
  16. package/dist/commands/wizard/vscode.d.ts +15 -0
  17. package/dist/commands/wizard/vscode.js +148 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1191 -0
  20. package/dist/lib/autocomplete-prompt.d.ts +14 -0
  21. package/dist/lib/autocomplete-prompt.js +167 -0
  22. package/dist/lib/detection.d.ts +44 -0
  23. package/dist/lib/detection.js +185 -0
  24. package/dist/lib/git.d.ts +12 -0
  25. package/dist/lib/git.js +37 -0
  26. package/dist/lib/paths.d.ts +108 -0
  27. package/dist/lib/paths.js +296 -0
  28. package/dist/lib/prompts.d.ts +18 -0
  29. package/dist/lib/prompts.js +62 -0
  30. package/dist/types/prompt.d.ts +54 -0
  31. package/dist/types/prompt.js +20 -0
  32. package/package.json +10 -7
  33. package/src/commands/selector.ts +0 -42
  34. package/src/commands/wizard/index.ts +0 -114
  35. package/src/commands/wizard/link-flow.ts +0 -118
  36. package/src/commands/wizard/setup-flow.ts +0 -347
  37. package/src/commands/wizard/sync-flow.ts +0 -93
  38. package/src/commands/wizard/update-flow.ts +0 -124
  39. package/src/commands/wizard/utils.ts +0 -38
  40. package/src/commands/wizard/vscode.ts +0 -197
  41. package/src/index.ts +0 -11
  42. package/src/lib/autocomplete-prompt.ts +0 -190
  43. package/src/lib/detection.ts +0 -235
  44. package/src/lib/git.ts +0 -37
  45. package/src/lib/paths.ts +0 -332
  46. package/src/lib/prompts.ts +0 -73
  47. 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.14",
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
- "module": "src/index.ts",
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
- "src",
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
  }
@@ -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
- }