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.
- package/README.md +49 -28
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +58 -26
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/new.js +1 -1
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/generator/config.d.ts +3 -3
- package/dist/generator/config.js +3 -3
- package/dist/generator/index.js +1 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/writer.js +1 -1
- package/dist/generator/writer.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -1
- package/dist/repl/command-parser.d.ts +5 -0
- package/dist/repl/command-parser.d.ts.map +1 -1
- package/dist/repl/command-parser.js +5 -0
- package/dist/repl/command-parser.js.map +1 -1
- package/dist/repl/repl-loop.d.ts.map +1 -1
- package/dist/repl/repl-loop.js +64 -3
- package/dist/repl/repl-loop.js.map +1 -1
- package/dist/repl/session-state.d.ts +4 -2
- package/dist/repl/session-state.d.ts.map +1 -1
- package/dist/repl/session-state.js +2 -1
- package/dist/repl/session-state.js.map +1 -1
- package/dist/templates/root/README.md.tmpl +1 -1
- package/dist/templates/scripts/feature-loop.sh.tmpl +17 -17
- package/dist/templates/scripts/loop.sh.tmpl +7 -7
- package/dist/templates/scripts/ralph-monitor.sh.tmpl +5 -5
- package/dist/utils/config.d.ts +7 -7
- package/dist/utils/config.js +4 -4
- package/dist/utils/config.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +81 -33
- package/src/commands/new.ts +1 -1
- package/src/commands/run.ts +1 -1
- package/src/generator/config.ts +3 -3
- package/src/generator/index.ts +1 -1
- package/src/generator/writer.ts +1 -1
- package/src/index.ts +46 -0
- package/src/repl/command-parser.ts +5 -0
- package/src/repl/repl-loop.ts +73 -3
- package/src/repl/session-state.ts +7 -3
- package/src/templates/config/ralph.config.cjs.tmpl +38 -0
- package/src/templates/root/README.md.tmpl +1 -1
- package/src/templates/scripts/feature-loop.sh.tmpl +17 -17
- package/src/templates/scripts/loop.sh.tmpl +7 -7
- package/src/templates/scripts/ralph-monitor.sh.tmpl +5 -5
- package/src/utils/config.ts +9 -9
- /package/{src/templates/config/ralph.config.js.tmpl → dist/templates/config/ralph.config.cjs.tmpl} +0 -0
package/src/commands/init.ts
CHANGED
|
@@ -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
|
-
*
|
|
282
|
-
*
|
|
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
|
|
285
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
324
|
-
// In interactive mode, null means user cancelled
|
|
336
|
+
// User cancelled or missing API key in --yes mode
|
|
325
337
|
await flushTracing();
|
|
326
|
-
|
|
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
|
-
|
|
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: '
|
|
433
|
-
{ command: '
|
|
434
|
-
{ command: '
|
|
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
|
-
//
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
}
|
package/src/commands/new.ts
CHANGED
|
@@ -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.
|
|
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
|
}
|
package/src/commands/run.ts
CHANGED
|
@@ -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.
|
|
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
|
}
|
package/src/generator/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config Generator
|
|
3
|
-
* Generates ralph.config.
|
|
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.
|
|
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.
|
|
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);
|
package/src/generator/index.ts
CHANGED
|
@@ -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.
|
|
131
|
+
processedTemplates.set('config/ralph.config.cjs', configContent);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
// Map template outputs to final paths
|
package/src/generator/writer.ts
CHANGED
|
@@ -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.
|
|
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>',
|
package/src/repl/repl-loop.ts
CHANGED
|
@@ -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 /
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -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.
|
|
18
|
-
if [ -f "$SCRIPT_DIR/../ralph.config.
|
|
19
|
-
RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
20
|
-
SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
21
|
-
PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
22
|
-
DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
23
|
-
PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
24
|
-
DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
25
|
-
DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
26
|
-
elif [ -f "$SCRIPT_DIR/../../ralph.config.
|
|
27
|
-
RALPH_ROOT=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
28
|
-
SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
29
|
-
PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
30
|
-
DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
31
|
-
PLANNING_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
32
|
-
DEFAULT_MAX_ITERATIONS=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
33
|
-
DEFAULT_MAX_E2E=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
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.
|
|
12
|
-
if [ -f "$SCRIPT_DIR/../ralph.config.
|
|
13
|
-
PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
14
|
-
DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
15
|
-
elif [ -f "$SCRIPT_DIR/../../ralph.config.
|
|
16
|
-
PROMPTS_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
17
|
-
DEFAULT_MODEL=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
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.
|
|
10
|
-
if [ -f "$SCRIPT_DIR/../ralph.config.
|
|
11
|
-
SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../ralph.config.
|
|
12
|
-
elif [ -f "$SCRIPT_DIR/../../ralph.config.
|
|
13
|
-
SPEC_DIR=$(node -e "console.log(require('$SCRIPT_DIR/../../ralph.config.
|
|
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
|