wiggum-cli 0.6.0 → 0.7.0

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 (55) hide show
  1. package/README.md +49 -28
  2. package/dist/commands/init.d.ts +18 -0
  3. package/dist/commands/init.d.ts.map +1 -1
  4. package/dist/commands/init.js +58 -26
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/new.js +1 -1
  7. package/dist/commands/new.js.map +1 -1
  8. package/dist/commands/run.js +1 -1
  9. package/dist/commands/run.js.map +1 -1
  10. package/dist/generator/config.d.ts +3 -3
  11. package/dist/generator/config.js +3 -3
  12. package/dist/generator/index.js +1 -1
  13. package/dist/generator/index.js.map +1 -1
  14. package/dist/generator/writer.js +1 -1
  15. package/dist/generator/writer.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +34 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/repl/command-parser.d.ts +5 -0
  21. package/dist/repl/command-parser.d.ts.map +1 -1
  22. package/dist/repl/command-parser.js +5 -0
  23. package/dist/repl/command-parser.js.map +1 -1
  24. package/dist/repl/repl-loop.d.ts.map +1 -1
  25. package/dist/repl/repl-loop.js +64 -3
  26. package/dist/repl/repl-loop.js.map +1 -1
  27. package/dist/repl/session-state.d.ts +4 -2
  28. package/dist/repl/session-state.d.ts.map +1 -1
  29. package/dist/repl/session-state.js +2 -1
  30. package/dist/repl/session-state.js.map +1 -1
  31. package/dist/templates/root/README.md.tmpl +1 -1
  32. package/dist/templates/scripts/feature-loop.sh.tmpl +17 -17
  33. package/dist/templates/scripts/loop.sh.tmpl +7 -7
  34. package/dist/templates/scripts/ralph-monitor.sh.tmpl +5 -5
  35. package/dist/utils/config.d.ts +7 -7
  36. package/dist/utils/config.js +4 -4
  37. package/dist/utils/config.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/commands/init.ts +81 -33
  40. package/src/commands/new.ts +1 -1
  41. package/src/commands/run.ts +1 -1
  42. package/src/generator/config.ts +3 -3
  43. package/src/generator/index.ts +1 -1
  44. package/src/generator/writer.ts +1 -1
  45. package/src/index.ts +46 -0
  46. package/src/repl/command-parser.ts +5 -0
  47. package/src/repl/repl-loop.ts +73 -3
  48. package/src/repl/session-state.ts +7 -3
  49. package/src/templates/config/ralph.config.cjs.tmpl +38 -0
  50. package/src/templates/root/README.md.tmpl +1 -1
  51. package/src/templates/scripts/feature-loop.sh.tmpl +17 -17
  52. package/src/templates/scripts/loop.sh.tmpl +7 -7
  53. package/src/templates/scripts/ralph-monitor.sh.tmpl +5 -5
  54. package/src/utils/config.ts +9 -9
  55. /package/{src/templates/config/ralph.config.js.tmpl → dist/templates/config/ralph.config.cjs.tmpl} +0 -0
@@ -121,6 +121,17 @@ export interface InitOptions {
121
121
  interactive?: boolean;
122
122
  }
123
123
 
124
+ /**
125
+ * Result of the init workflow
126
+ */
127
+ export interface InitResult {
128
+ success: boolean;
129
+ provider: AIProvider;
130
+ model: string;
131
+ scanResult: ScanResult;
132
+ config: import('../utils/config.js').RalphConfig | null;
133
+ }
134
+
124
135
  /**
125
136
  * Save API keys to .env.local file
126
137
  */
@@ -278,12 +289,15 @@ async function collectApiKeys(
278
289
  }
279
290
 
280
291
  /**
281
- * Initialize Ralph in the current project
282
- * Uses BYOK (Bring Your Own Keys) model with multi-agent AI analysis
292
+ * Run the init workflow
293
+ * Reusable core logic for both CLI and REPL usage
294
+ * Returns InitResult on success, null on cancellation
295
+ * Throws on hard errors
283
296
  */
284
- export async function initCommand(options: InitOptions): Promise<void> {
285
- const projectRoot = process.cwd();
286
-
297
+ export async function runInitWorkflow(
298
+ projectRoot: string,
299
+ options: InitOptions
300
+ ): Promise<InitResult | null> {
287
301
  logger.info('Initializing Ralph...');
288
302
  logger.info(`Project: ${projectRoot}`);
289
303
  console.log('');
@@ -300,9 +314,8 @@ export async function initCommand(options: InitOptions): Promise<void> {
300
314
  scanSpinner.stop('Project scanned');
301
315
  } catch (error) {
302
316
  scanSpinner.fail('Scan failed');
303
- logger.error(`Failed to scan project: ${error instanceof Error ? error.message : String(error)}`);
304
317
  await flushTracing();
305
- process.exit(1);
318
+ throw new Error(`Failed to scan project: ${error instanceof Error ? error.message : String(error)}`);
306
319
  }
307
320
 
308
321
  // Step 2: Show detected stack
@@ -320,14 +333,9 @@ export async function initCommand(options: InitOptions): Promise<void> {
320
333
  const apiKeys = await collectApiKeys(projectRoot, options);
321
334
 
322
335
  if (!apiKeys) {
323
- // In --yes mode, null means missing API key (hard failure)
324
- // In interactive mode, null means user cancelled
336
+ // User cancelled or missing API key in --yes mode
325
337
  await flushTracing();
326
- if (options.yes) {
327
- process.exit(1);
328
- }
329
- logger.info('Initialization cancelled');
330
- return;
338
+ return null;
331
339
  }
332
340
 
333
341
  // Step 4: Run AI analysis
@@ -390,8 +398,7 @@ export async function initCommand(options: InitOptions): Promise<void> {
390
398
 
391
399
  if (prompts.isCancel(shouldContinue) || !shouldContinue) {
392
400
  await flushTracing();
393
- logger.info('Initialization cancelled');
394
- return;
401
+ return null;
395
402
  }
396
403
  }
397
404
 
@@ -427,36 +434,77 @@ export async function initCommand(options: InitOptions): Promise<void> {
427
434
  await flushTracing();
428
435
 
429
436
  if (generationResult.success) {
430
- // Show next steps
437
+ // Show next steps (REPL-aware)
431
438
  console.log(nextStepsBox([
432
- { command: 'wiggum new my-feature', description: 'Create a feature specification' },
433
- { command: 'wiggum run my-feature', description: 'Start the development loop' },
434
- { command: 'wiggum monitor my-feature', description: 'Watch progress in real-time' },
439
+ { command: '/new my-feature', description: 'Create a feature specification' },
440
+ { command: '/run my-feature', description: 'Start the development loop' },
441
+ { command: '/help', description: 'Show all available commands' },
435
442
  ]));
436
443
 
437
444
  console.log(` ${simpson.brown('Documentation:')} .ralph/guides/AGENTS.md`);
438
445
  console.log('');
439
446
  logger.success('Wiggum initialized successfully!');
440
447
 
441
- // Start interactive REPL if requested
442
- if (options.interactive) {
443
- const config = await loadConfigWithDefaults(projectRoot);
444
- const sessionState = createSessionState(
445
- projectRoot,
446
- apiKeys.provider,
447
- apiKeys.model,
448
- scanResult,
449
- config
450
- );
451
- await startRepl(sessionState);
452
- }
448
+ // Load config and return result
449
+ const config = await loadConfigWithDefaults(projectRoot);
450
+ return {
451
+ success: true,
452
+ provider: apiKeys.provider,
453
+ model: apiKeys.model,
454
+ scanResult,
455
+ config,
456
+ };
453
457
  } else {
454
458
  logger.warn('Initialization completed with some errors');
459
+ const config = await loadConfigWithDefaults(projectRoot);
460
+ return {
461
+ success: true, // Still return success to continue
462
+ provider: apiKeys.provider,
463
+ model: apiKeys.model,
464
+ scanResult,
465
+ config,
466
+ };
455
467
  }
456
468
  } catch (error) {
457
469
  genSpinner.fail('Generation failed');
458
- logger.error(`Failed to generate files: ${error instanceof Error ? error.message : String(error)}`);
459
470
  await flushTracing();
471
+ throw new Error(`Failed to generate files: ${error instanceof Error ? error.message : String(error)}`);
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Initialize Ralph in the current project
477
+ * Uses BYOK (Bring Your Own Keys) model with multi-agent AI analysis
478
+ */
479
+ export async function initCommand(options: InitOptions): Promise<void> {
480
+ const projectRoot = process.cwd();
481
+
482
+ try {
483
+ const result = await runInitWorkflow(projectRoot, options);
484
+
485
+ if (!result) {
486
+ // Cancelled by user or missing API key
487
+ if (options.yes) {
488
+ process.exit(1);
489
+ }
490
+ logger.info('Initialization cancelled');
491
+ return;
492
+ }
493
+
494
+ // Start interactive REPL if requested
495
+ if (options.interactive) {
496
+ const sessionState = createSessionState(
497
+ projectRoot,
498
+ result.provider,
499
+ result.model,
500
+ result.scanResult,
501
+ result.config,
502
+ true // initialized
503
+ );
504
+ await startRepl(sessionState);
505
+ }
506
+ } catch (error) {
507
+ logger.error(error instanceof Error ? error.message : String(error));
460
508
  process.exit(1);
461
509
  }
462
510
  }
@@ -214,7 +214,7 @@ export async function newCommand(feature: string, options: NewOptions = {}): Pro
214
214
 
215
215
  // Check for config
216
216
  if (!hasConfig(projectRoot)) {
217
- logger.warn('No ralph.config.js found. Run "wiggum init" first to configure your project.');
217
+ logger.warn('No ralph.config.cjs found. Run "wiggum init" first to configure your project.');
218
218
  logger.info('Using default paths...');
219
219
  console.log('');
220
220
  }
@@ -94,7 +94,7 @@ export async function runCommand(feature: string, options: RunOptions = {}): Pro
94
94
 
95
95
  // Check for config
96
96
  if (!hasConfig(projectRoot)) {
97
- logger.warn('No ralph.config.js found. Run "wiggum init" first to configure your project.');
97
+ logger.warn('No ralph.config.cjs found. Run "wiggum init" first to configure your project.');
98
98
  logger.info('Attempting to run with default settings...');
99
99
  console.log('');
100
100
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Config Generator
3
- * Generates ralph.config.js file from scan results
3
+ * Generates ralph.config.cjs file from scan results
4
4
  */
5
5
 
6
6
  import type { ScanResult } from '../scanner/types.js';
@@ -95,7 +95,7 @@ export function generateConfig(scanResult: ScanResult, customVars: Record<string
95
95
  }
96
96
 
97
97
  /**
98
- * Generate ralph.config.js file content as JavaScript module
98
+ * Generate ralph.config.cjs file content as CommonJS module
99
99
  */
100
100
  export function generateConfigFile(config: RalphConfig): string {
101
101
  // Use CommonJS module.exports for compatibility with both CJS and ESM projects
@@ -110,7 +110,7 @@ export function generateConfigFile(config: RalphConfig): string {
110
110
  }
111
111
 
112
112
  /**
113
- * Generate ralph.config.js from scan result
113
+ * Generate ralph.config.cjs from scan result
114
114
  */
115
115
  export function generateConfigFileFromScan(scanResult: ScanResult, customVars: Record<string, string> = {}): string {
116
116
  const config = generateConfig(scanResult, customVars);
@@ -128,7 +128,7 @@ export class Generator {
128
128
  if (this.options.generateConfig) {
129
129
  config = generateConfig(scanResult, this.options.customVariables || {});
130
130
  const configContent = generateConfigFile(config);
131
- processedTemplates.set('config/ralph.config.js', configContent);
131
+ processedTemplates.set('config/ralph.config.cjs', configContent);
132
132
  }
133
133
 
134
134
  // Map template outputs to final paths
@@ -242,7 +242,7 @@ export function mapTemplateOutputPaths(templateOutputs: Map<string, string>): Ma
242
242
  // Scripts go to .ralph/scripts/
243
243
  finalPath = `.ralph/${outputPath}`;
244
244
  } else if (outputPath.startsWith('config/')) {
245
- // ralph.config.js goes to project root
245
+ // ralph.config.cjs goes to project root
246
246
  finalPath = outputPath.replace('config/', '');
247
247
  } else if (outputPath.startsWith('root/')) {
248
248
  // Root files go to .ralph/
package/src/index.ts CHANGED
@@ -1,9 +1,55 @@
1
1
  import { createCli } from './cli.js';
2
+ import { startRepl, createSessionState } from './repl/index.js';
3
+ import { hasConfig, loadConfigWithDefaults } from './utils/config.js';
4
+ import { getAvailableProvider } from './ai/providers.js';
5
+ import { displayHeader } from './utils/header.js';
6
+
7
+ /**
8
+ * Start REPL-first mode
9
+ * Called when wiggum is invoked with no arguments
10
+ */
11
+ async function startReplFirst(): Promise<void> {
12
+ const projectRoot = process.cwd();
13
+ const provider = getAvailableProvider();
14
+
15
+ // Display header
16
+ displayHeader();
17
+
18
+ // Check if already initialized
19
+ const isInitialized = hasConfig(projectRoot);
20
+ let config = null;
21
+
22
+ if (isInitialized) {
23
+ config = await loadConfigWithDefaults(projectRoot);
24
+ }
25
+
26
+ // Create initial state (may not have config yet)
27
+ const initialState = createSessionState(
28
+ projectRoot,
29
+ provider, // May be null if no API key
30
+ 'sonnet', // Default model, will be updated after /init
31
+ undefined, // No scan result yet
32
+ config,
33
+ isInitialized
34
+ );
35
+
36
+ await startRepl(initialState);
37
+ }
2
38
 
3
39
  /**
4
40
  * Main entry point for the Ralph CLI
41
+ * REPL-first: no args = start REPL, otherwise use CLI
5
42
  */
6
43
  export async function main(): Promise<void> {
44
+ const args = process.argv.slice(2);
45
+
46
+ // REPL-first: no args = start REPL
47
+ if (args.length === 0) {
48
+ await startReplFirst();
49
+ return;
50
+ }
51
+
52
+ // Legacy CLI mode for backward compatibility
7
53
  const program = createCli();
8
54
  await program.parseAsync(process.argv);
9
55
  }
@@ -33,6 +33,11 @@ export interface ParsedInput {
33
33
  * Available REPL commands
34
34
  */
35
35
  export const REPL_COMMANDS = {
36
+ init: {
37
+ description: 'Initialize Wiggum in this project',
38
+ usage: '/init',
39
+ aliases: ['i'],
40
+ },
36
41
  new: {
37
42
  description: 'Create a new feature specification',
38
43
  usage: '/new <feature-name>',
@@ -9,6 +9,8 @@ import { logger } from '../utils/logger.js';
9
9
  import { simpson } from '../utils/colors.js';
10
10
  import { runCommand } from '../commands/run.js';
11
11
  import { monitorCommand } from '../commands/monitor.js';
12
+ import { runInitWorkflow } from '../commands/init.js';
13
+ import { hasConfig } from '../utils/config.js';
12
14
  import type { SessionState } from './session-state.js';
13
15
  import { updateSessionState } from './session-state.js';
14
16
  import {
@@ -21,13 +23,64 @@ import {
21
23
  const PROMPT = `${simpson.yellow('wiggum')}${simpson.brown('>')} `;
22
24
 
23
25
  /**
24
- * Handler for the /new command - will be enhanced in Phase 3
26
+ * Handler for the /init command
27
+ */
28
+ async function handleInitCommand(
29
+ _args: string[],
30
+ state: SessionState,
31
+ rl: readline.Interface
32
+ ): Promise<SessionState> {
33
+ // Check if already initialized
34
+ if (state.initialized && hasConfig(state.projectRoot)) {
35
+ logger.warn('Project is already initialized. Re-running init will update configuration.');
36
+ console.log('');
37
+ }
38
+
39
+ // Pause REPL readline to avoid conflicts with subcommand's stdin usage
40
+ rl.pause();
41
+
42
+ try {
43
+ const result = await runInitWorkflow(state.projectRoot, {
44
+ yes: false, // Always interactive in REPL
45
+ });
46
+
47
+ if (result) {
48
+ // Update state with init result
49
+ return updateSessionState(state, {
50
+ provider: result.provider,
51
+ model: result.model,
52
+ scanResult: result.scanResult,
53
+ config: result.config,
54
+ initialized: true,
55
+ });
56
+ }
57
+
58
+ // User cancelled
59
+ return state;
60
+ } catch (error) {
61
+ logger.error(`Init failed: ${error instanceof Error ? error.message : String(error)}`);
62
+ return state;
63
+ } finally {
64
+ // Resume REPL readline after subcommand completes
65
+ rl.resume();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Handler for the /new command
71
+ * Always uses AI interview mode in REPL (falls back to template if no API key)
25
72
  */
26
73
  async function handleNewCommand(
27
74
  args: string[],
28
75
  state: SessionState,
29
76
  rl: readline.Interface
30
77
  ): Promise<SessionState> {
78
+ // Check if initialized
79
+ if (!state.initialized && !hasConfig(state.projectRoot)) {
80
+ logger.warn('Project not initialized. Run /init first.');
81
+ return state;
82
+ }
83
+
31
84
  if (args.length === 0) {
32
85
  logger.error('Feature name required. Usage: /new <feature-name>');
33
86
  return state;
@@ -40,12 +93,14 @@ async function handleNewCommand(
40
93
 
41
94
  try {
42
95
  // Delegate to the existing new command behavior
96
+ // Always use AI mode in REPL (the command handles fallback to template if no API key)
43
97
  const { newCommand } = await import('../commands/new.js');
44
98
  await newCommand(featureName, {
45
99
  yes: false,
46
100
  scanResult: state.scanResult,
47
- provider: state.provider,
101
+ provider: state.provider ?? undefined,
48
102
  model: state.model,
103
+ ai: true, // Always use AI interview in REPL
49
104
  });
50
105
  } finally {
51
106
  // Resume REPL readline after subcommand completes
@@ -63,6 +118,12 @@ async function handleRunCommand(
63
118
  state: SessionState,
64
119
  rl: readline.Interface
65
120
  ): Promise<SessionState> {
121
+ // Check if initialized
122
+ if (!state.initialized && !hasConfig(state.projectRoot)) {
123
+ logger.warn('Project not initialized. Run /init first.');
124
+ return state;
125
+ }
126
+
66
127
  if (args.length === 0) {
67
128
  logger.error('Feature name required. Usage: /run <feature-name>');
68
129
  return state;
@@ -128,6 +189,9 @@ async function executeCommand(
128
189
  rl: readline.Interface
129
190
  ): Promise<{ state: SessionState; shouldExit: boolean }> {
130
191
  switch (commandName) {
192
+ case 'init':
193
+ return { state: await handleInitCommand(args, state, rl), shouldExit: false };
194
+
131
195
  case 'new':
132
196
  return { state: await handleNewCommand(args, state, rl), shouldExit: false };
133
197
 
@@ -218,7 +282,13 @@ export async function startRepl(initialState: SessionState): Promise<void> {
218
282
 
219
283
  console.log('');
220
284
  console.log(simpson.yellow('Wiggum Interactive Mode'));
221
- console.log(pc.dim('Type /help for commands, /exit to quit'));
285
+
286
+ // Show context-aware welcome message
287
+ if (!state.initialized && !hasConfig(state.projectRoot)) {
288
+ console.log(pc.dim('Not initialized. Run /init to set up this project.'));
289
+ } else {
290
+ console.log(pc.dim('Type /help for commands, /exit to quit'));
291
+ }
222
292
  console.log('');
223
293
 
224
294
  const rl = readline.createInterface({
@@ -16,7 +16,7 @@ export interface SessionState {
16
16
  /** Loaded Ralph configuration */
17
17
  config: RalphConfig | null;
18
18
  /** AI provider being used */
19
- provider: AIProvider;
19
+ provider: AIProvider | null;
20
20
  /** Model to use for AI operations */
21
21
  model: string;
22
22
  /** Cached scan result from init */
@@ -25,6 +25,8 @@ export interface SessionState {
25
25
  conversationMode: boolean;
26
26
  /** Current conversation context (e.g., 'spec-generation') */
27
27
  conversationContext?: string;
28
+ /** Whether /init has been run in this session */
29
+ initialized: boolean;
28
30
  }
29
31
 
30
32
  /**
@@ -32,10 +34,11 @@ export interface SessionState {
32
34
  */
33
35
  export function createSessionState(
34
36
  projectRoot: string,
35
- provider: AIProvider,
37
+ provider: AIProvider | null,
36
38
  model: string,
37
39
  scanResult?: ScanResult,
38
- config?: RalphConfig | null
40
+ config?: RalphConfig | null,
41
+ initialized?: boolean
39
42
  ): SessionState {
40
43
  return {
41
44
  projectRoot,
@@ -45,6 +48,7 @@ export function createSessionState(
45
48
  scanResult,
46
49
  conversationMode: false,
47
50
  conversationContext: undefined,
51
+ initialized: initialized ?? false,
48
52
  };
49
53
  }
50
54
 
@@ -0,0 +1,38 @@
1
+ module.exports = {
2
+ name: '{{projectName}}',
3
+ stack: {
4
+ framework: {
5
+ name: '{{framework}}',
6
+ version: '{{frameworkVersion}}',
7
+ variant: '{{frameworkVariant}}',
8
+ },
9
+ packageManager: '{{packageManager}}',
10
+ testing: {
11
+ unit: '{{unitTest}}',
12
+ e2e: '{{e2eTest}}',
13
+ },
14
+ styling: '{{styling}}',
15
+ },
16
+ commands: {
17
+ dev: '{{devCommand}}',
18
+ build: '{{buildCommand}}',
19
+ test: '{{testCommand}}',
20
+ lint: '{{lintCommand}}',
21
+ typecheck: '{{typecheckCommand}}',
22
+ },
23
+ paths: {
24
+ root: '.ralph',
25
+ prompts: '.ralph/prompts',
26
+ guides: '.ralph/guides',
27
+ specs: '.ralph/specs',
28
+ scripts: '.ralph/scripts',
29
+ learnings: '.ralph/LEARNINGS.md',
30
+ agents: '.ralph/AGENTS.md',
31
+ },
32
+ loop: {
33
+ maxIterations: 10,
34
+ maxE2eAttempts: 5,
35
+ defaultModel: 'sonnet',
36
+ planningModel: 'opus',
37
+ },
38
+ };
@@ -58,4 +58,4 @@ Add new feature specs to `.ralph/specs/`:
58
58
 
59
59
  ## Configuration
60
60
 
61
- See `ralph.config.js` in project root for full configuration.
61
+ See `ralph.config.cjs` in project root for full configuration.
@@ -14,23 +14,23 @@ set -o pipefail
14
14
  # Get script directory
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
 
17
- # Load config from ralph.config.js if available
18
- if [ -f "$SCRIPT_DIR/../ralph.config.js" ]; then
19
- RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').paths?.root || '.ralph')" 2>/dev/null || echo ".ralph")
20
- SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
21
- PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
22
- DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
23
- PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').loop?.planningModel || 'opus')" 2>/dev/null || echo "opus")
24
- DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').loop?.maxIterations || 10)" 2>/dev/null || echo "10")
25
- DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').loop?.maxE2eAttempts || 5)" 2>/dev/null || echo "5")
26
- elif [ -f "$SCRIPT_DIR/../../ralph.config.js" ]; then
27
- RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').paths?.root || '.ralph')" 2>/dev/null || echo ".ralph")
28
- SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
29
- PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
30
- DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
31
- PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').loop?.planningModel || 'opus')" 2>/dev/null || echo "opus")
32
- DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').loop?.maxIterations || 10)" 2>/dev/null || echo "10")
33
- DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').loop?.maxE2eAttempts || 5)" 2>/dev/null || echo "5")
17
+ # Load config from ralph.config.cjs if available
18
+ if [ -f "$SCRIPT_DIR/../ralph.config.cjs" ]; then
19
+ RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').paths?.root || '.ralph')" 2>/dev/null || echo ".ralph")
20
+ SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
21
+ PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
22
+ DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
23
+ PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').loop?.planningModel || 'opus')" 2>/dev/null || echo "opus")
24
+ DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').loop?.maxIterations || 10)" 2>/dev/null || echo "10")
25
+ DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').loop?.maxE2eAttempts || 5)" 2>/dev/null || echo "5")
26
+ elif [ -f "$SCRIPT_DIR/../../ralph.config.cjs" ]; then
27
+ RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').paths?.root || '.ralph')" 2>/dev/null || echo ".ralph")
28
+ SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
29
+ PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
30
+ DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
31
+ PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').loop?.planningModel || 'opus')" 2>/dev/null || echo "opus")
32
+ DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').loop?.maxIterations || 10)" 2>/dev/null || echo "10")
33
+ DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').loop?.maxE2eAttempts || 5)" 2>/dev/null || echo "5")
34
34
  else
35
35
  # Default paths
36
36
  RALPH_ROOT=".ralph"
@@ -8,13 +8,13 @@ set -e
8
8
  # Get script directory
9
9
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
10
 
11
- # Load config from ralph.config.js if available
12
- if [ -f "$SCRIPT_DIR/../ralph.config.js" ]; then
13
- PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
14
- DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
15
- elif [ -f "$SCRIPT_DIR/../../ralph.config.js" ]; then
16
- PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
17
- DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
11
+ # Load config from ralph.config.cjs if available
12
+ if [ -f "$SCRIPT_DIR/../ralph.config.cjs" ]; then
13
+ PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
14
+ DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
15
+ elif [ -f "$SCRIPT_DIR/../../ralph.config.cjs" ]; then
16
+ PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').paths?.prompts || '.ralph/prompts')" 2>/dev/null || echo ".ralph/prompts")
17
+ DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').loop?.defaultModel || 'sonnet')" 2>/dev/null || echo "sonnet")
18
18
  else
19
19
  PROMPTS_DIR=".ralph/prompts"
20
20
  DEFAULT_MODEL="sonnet"
@@ -6,11 +6,11 @@
6
6
  FEATURE="${1:?Usage: ./ralph-monitor.sh <feature-name>}"
7
7
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
8
 
9
- # Load config from ralph.config.js if available
10
- if [ -f "$SCRIPT_DIR/../ralph.config.js" ]; then
11
- SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.js').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
12
- elif [ -f "$SCRIPT_DIR/../../ralph.config.js" ]; then
13
- SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.js').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
9
+ # Load config from ralph.config.cjs if available
10
+ if [ -f "$SCRIPT_DIR/../ralph.config.cjs" ]; then
11
+ SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.cjs').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
12
+ elif [ -f "$SCRIPT_DIR/../../ralph.config.cjs" ]; then
13
+ SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.cjs').paths?.specs || '.ralph/specs')" 2>/dev/null || echo ".ralph/specs")
14
14
  else
15
15
  SPEC_DIR=".ralph/specs"
16
16
  fi