Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ralph-starter",
"version": "0.4.2",
"version": "0.4.3",
"description": "Ralph Wiggum made easy. One command to run autonomous AI coding loops with auto-commit, PRs, and Docker sandbox.",
"main": "dist/index.js",
"bin": {
Expand Down Expand Up @@ -86,6 +86,9 @@
"yaml": "^2.7.0",
"zod": "^4.3.6"
},
"optionalDependencies": {
"@sourcegraph/amp-sdk": "0.1.0-20260312122132-g783443e"
},
"devDependencies": {
"@biomejs/biome": "^2.3.13",
"@commitlint/cli": "^20.3.1",
Expand Down
158 changes: 158 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ program
.option('--docker', 'Run in Docker sandbox (coming soon)')
.option('--prd <file>', 'Read tasks from a PRD markdown file')
.option('--max-iterations <n>', 'Maximum loop iterations (auto-calculated if not specified)')
.option('--agent <name>', 'Specify agent (claude-code, cursor, codex, opencode, openclaw)')
.option('--agent <name>', 'Specify agent (claude-code, cursor, codex, opencode, openclaw, amp)')
.option('--model <name>', 'Model to use (e.g., claude-sonnet-4-5-20250929, claude-opus-4-6)')
.option(
'--amp-mode <mode>',
'Amp agent mode: smart (frontier), rush (fast), deep (extended reasoning)'
)
.option('--from <source>', 'Fetch spec from source (file, url, github, todoist, linear, notion)')
.option('--project <name>', 'Project/repo name for --from integrations')
.option('--label <name>', 'Label filter for --from integrations')
Expand Down Expand Up @@ -315,6 +319,10 @@ program
.option('--dry-run', 'Preview mode - show tasks without executing')
.option('--skip-pr', 'Skip PR creation (commit only)')
.option('--agent <name>', 'Specify agent to use')
.option(
'--amp-mode <mode>',
'Amp agent mode: smart (frontier), rush (fast), deep (extended reasoning)'
)
.option('--validate', 'Run validation after each task', true)
.option('--no-validate', 'Skip validation')
.option('--max-iterations <n>', 'Max iterations per task (default: 15)')
Expand Down Expand Up @@ -407,7 +415,11 @@ program
.option('--pr', 'Create a pull request when done')
.option('--validate', 'Run tests/lint/build after each iteration')
.option('--max-iterations <n>', 'Maximum loop iterations')
.option('--agent <name>', 'Specify agent (claude-code, cursor, codex, opencode, openclaw)')
.option('--agent <name>', 'Specify agent (claude-code, cursor, codex, opencode, openclaw, amp)')
.option(
'--amp-mode <mode>',
'Amp agent mode: smart (frontier), rush (fast), deep (extended reasoning)'
)
.action(async (action: string | undefined, args: string[], options) => {
await templateCommand(action, args, options);
});
Expand Down
8 changes: 8 additions & 0 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ export interface RunCommandOptions {
// Swarm mode
swarm?: boolean;
strategy?: 'race' | 'consensus' | 'pipeline';
// Amp options
ampMode?: 'smart' | 'rush' | 'deep';
}

export async function runCommand(
Expand Down Expand Up @@ -971,6 +973,11 @@ export async function runCommand(
console.log();
console.log(chalk.yellow('Please install one of these:'));
Comment thread
rubenmarcus marked this conversation as resolved.
console.log(chalk.gray(' Claude Code: npm install -g @anthropic-ai/claude-code'));
console.log(
chalk.gray(
' Amp: npm install -g @sourcegraph/amp (or see https://ampcode.com/install)'
)
);
console.log(chalk.gray(' Cursor: https://cursor.sh'));
console.log(chalk.gray(' Codex: npm install -g codex'));
console.log(chalk.gray(' OpenCode: npm install -g opencode'));
Expand Down Expand Up @@ -1426,6 +1433,7 @@ Focus on one task at a time. After completing a task, update IMPLEMENTATION_PLAN
designImagePath,
visualValidation,
figmaScreenshotPaths,
ampMode: options.ampMode,
};

// Swarm mode: run with multiple agents in parallel
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type { LLMProvider } from './llm/providers.js';
export { OPENROUTER_MODEL_ALIASES, resolveOpenRouterModel } from './llm/providers.js';
export type { AcceptanceCriteria, AcceptanceCriterion } from './loop/acceptance-criteria.js';
export { extractAcceptanceCriteria } from './loop/acceptance-criteria.js';
export type { Agent, AgentType } from './loop/agents.js';
export type { Agent, AgentType, AmpMode } from './loop/agents.js';
export { detectAvailableAgents, detectBestAgent } from './loop/agents.js';
export type { CircuitBreakerConfig, CircuitBreakerState } from './loop/circuit-breaker.js';
// Loop
Expand Down
26 changes: 21 additions & 5 deletions src/loop/__tests__/agents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@ describe('agents', () => {
.mockRejectedValueOnce(new Error('not found')) // cursor
.mockRejectedValueOnce(new Error('not found')) // codex
.mockRejectedValueOnce(new Error('not found')) // opencode
.mockRejectedValueOnce(new Error('not found')); // openclaw
.mockRejectedValueOnce(new Error('not found')) // openclaw
.mockRejectedValueOnce(new Error('not found')); // amp

const agents = await detectAvailableAgents();

expect(agents).toHaveLength(5);
expect(agents).toHaveLength(6);
expect(agents.find((a) => a.type === 'claude-code')?.available).toBe(true);
expect(agents.find((a) => a.type === 'cursor')?.available).toBe(false);
});
Expand Down Expand Up @@ -121,15 +122,30 @@ describe('agents', () => {
expect(agent?.type).toBe('claude-code');
});

it('should fall back to cursor if claude-code is not available', async () => {
it('should fall back to amp if claude-code is not available', async () => {
mockExeca
.mockRejectedValueOnce(new Error('not found')) // claude-code
.mockRejectedValueOnce(new Error('not found')) // cursor
.mockRejectedValueOnce(new Error('not found')) // codex
.mockRejectedValueOnce(new Error('not found')) // opencode
.mockRejectedValueOnce(new Error('not found')) // openclaw
.mockResolvedValueOnce({ stdout: '1.0.0', exitCode: 0 } as any); // amp

const agent = await detectBestAgent();
expect(agent?.type).toBe('amp');
});

it('should prefer amp over cursor', async () => {
mockExeca
.mockRejectedValueOnce(new Error('not found')) // claude-code
.mockResolvedValueOnce({ stdout: '1.0.0', exitCode: 0 } as any) // cursor
.mockRejectedValueOnce(new Error('not found')) // codex
.mockRejectedValueOnce(new Error('not found')); // opencode
.mockRejectedValueOnce(new Error('not found')) // opencode
.mockRejectedValueOnce(new Error('not found')) // openclaw
.mockResolvedValueOnce({ stdout: '1.0.0', exitCode: 0 } as any); // amp

const agent = await detectBestAgent();
expect(agent?.type).toBe('cursor');
expect(agent?.type).toBe('amp');
});
});

Expand Down
Loading
Loading