From f8e00d0b83cec3bd7a60748e8f866bf3acd2d18b Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 06:10:57 +0000 Subject: [PATCH 1/8] fix: sync MCP server version with package.json Read version dynamically from package.json instead of hardcoding '0.1.0'. Co-Authored-By: Claude Opus 4.6 --- src/mcp/server.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 2ac073b..71212e9 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -1,3 +1,6 @@ +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { @@ -12,6 +15,17 @@ import { getPrompts, handleGetPrompt } from './prompts.js'; import { getResources, handleResourceRead } from './resources.js'; import { getTools, handleToolCall } from './tools.js'; +function getPackageVersion(): string { + try { + const __dirname = dirname(fileURLToPath(import.meta.url)); + const pkgPath = join(__dirname, '..', '..', 'package.json'); + const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); + return pkg.version || '0.1.0'; + } catch { + return '0.1.0'; + } +} + /** * Create and configure the MCP server */ @@ -19,7 +33,7 @@ export function createMcpServer(): Server { const server = new Server( { name: 'ralph-starter', - version: '0.1.0', + version: getPackageVersion(), }, { capabilities: { From 6aa1ae524be5fac9eadb6f1b5aed3c8cd8b89894 Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 06:12:42 +0000 Subject: [PATCH 2/8] feat: add ralph_list_presets and ralph_fetch_spec MCP tools - Add ralph_list_presets tool to discover all 19 workflow presets by category - Add ralph_fetch_spec tool to preview specs from GitHub, Linear, Notion, and Figma without running the full coding loop - Improve all existing tool descriptions with detailed context for LLM clients - ralph_fetch_spec supports Figma modes (spec, tokens, components, content, assets) Co-Authored-By: Claude Opus 4.6 --- src/mcp/tools.ts | 192 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 173 insertions(+), 19 deletions(-) diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index 133ab94..92dae46 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -41,6 +41,31 @@ const toolSchemas = { ralph_validate: z.object({ path: z.string().describe('Project path'), }), + + ralph_list_presets: z.object({ + category: z + .string() + .optional() + .describe('Filter by category (development, debugging, review, documentation, specialized)'), + }), + + ralph_fetch_spec: z.object({ + path: z.string().describe('Project directory path'), + source: z + .enum(['github', 'linear', 'notion', 'figma']) + .describe('Integration source to fetch from'), + identifier: z + .string() + .describe( + 'Source identifier: GitHub repo/issue URL, Linear project name, Notion page URL, or Figma file URL' + ), + mode: z + .string() + .optional() + .describe('Figma-specific mode: spec, tokens, components, content, assets'), + project: z.string().optional().describe('Project or team filter (for Linear/GitHub)'), + label: z.string().optional().describe('Label filter (for GitHub/Linear issues)'), + }), }; /** @@ -51,17 +76,17 @@ export function getTools(): Tool[] { { name: 'ralph_init', description: - 'Initialize Ralph Playbook in a project. Creates AGENTS.md, PROMPT_plan.md, PROMPT_build.md, specs/, and IMPLEMENTATION_PLAN.md.', + 'Initialize Ralph Playbook in a project directory. Creates the scaffolding files needed for autonomous coding: AGENTS.md (agent config), PROMPT_plan.md and PROMPT_build.md (workflow prompts), specs/ directory, and IMPLEMENTATION_PLAN.md. Auto-detects project type (Node.js, Python, Rust, Go) and configures validation commands.', inputSchema: { type: 'object', properties: { path: { type: 'string', - description: 'Project path to initialize', + description: 'Absolute path to the project directory to initialize', }, name: { type: 'string', - description: 'Project name', + description: 'Project name (defaults to directory name)', }, }, required: ['path'], @@ -70,17 +95,17 @@ export function getTools(): Tool[] { { name: 'ralph_plan', description: - 'Create an implementation plan from specs. Analyzes specs/ directory and generates IMPLEMENTATION_PLAN.md.', + 'Create an implementation plan from specification files. Analyzes the specs/ directory using an AI coding agent and generates a structured IMPLEMENTATION_PLAN.md with checkboxed tasks. The plan breaks down the spec into actionable development tasks.', inputSchema: { type: 'object', properties: { path: { type: 'string', - description: 'Project path', + description: 'Project directory path containing specs/ folder', }, auto: { type: 'boolean', - description: 'Run in automated mode (skip permissions)', + description: 'Run in automated mode without interactive prompts', }, }, required: ['path'], @@ -89,41 +114,43 @@ export function getTools(): Tool[] { { name: 'ralph_run', description: - 'Execute an autonomous coding loop. Uses the implementation plan and agents to build the project.', + 'Execute an autonomous AI coding loop that iterates until task completion. The agent reads specs, writes code, runs validation (tests/lint/build), and auto-commits. Supports Claude Code, Cursor, Codex, OpenCode, Copilot, Gemini CLI, Amp, and Openclaw agents. Can fetch tasks from GitHub issues, Linear tickets, Notion pages, or Figma designs. Use workflow presets (see ralph_list_presets) to configure behavior.', inputSchema: { type: 'object', properties: { path: { type: 'string', - description: 'Project path', + description: 'Project directory path', }, task: { type: 'string', - description: 'Task to execute (optional if using Ralph Playbook)', + description: + 'Task description to execute. Optional if using Ralph Playbook (reads from IMPLEMENTATION_PLAN.md)', }, auto: { type: 'boolean', - description: 'Run in automated mode (skip permissions)', + description: 'Run in automated mode — processes all tasks without interactive prompts', }, commit: { type: 'boolean', - description: 'Auto-commit changes after each task', + description: 'Auto-commit changes after each completed task', }, validate: { type: 'boolean', - description: 'Run tests/lint/build validation', + description: 'Run validation commands (tests, lint, build) after each iteration', }, from: { type: 'string', - description: 'Source to fetch spec from (file, url, github, todoist, linear, notion)', + description: + 'Source integration to fetch spec from: file, url, github, linear, notion, figma', }, project: { type: 'string', - description: 'Project/repo name for source integrations', + description: 'Project/repo name filter for GitHub or Linear integrations', }, label: { type: 'string', - description: 'Label filter for source integrations', + description: 'Label filter to select specific issues from GitHub or Linear', }, }, required: ['path'], @@ -132,13 +159,13 @@ export function getTools(): Tool[] { { name: 'ralph_status', description: - 'Check Ralph Playbook status. Shows available files, implementation progress, and agent availability.', + 'Check Ralph Playbook status for a project. Returns available playbook files, implementation plan progress (completed/total tasks), and spec files. Useful for understanding where a project stands before continuing work.', inputSchema: { type: 'object', properties: { path: { type: 'string', - description: 'Project path', + description: 'Project directory path to check', }, }, required: ['path'], @@ -147,18 +174,71 @@ export function getTools(): Tool[] { { name: 'ralph_validate', description: - 'Run validation commands (tests, lint, build). Checks project health and reports issues.', + 'Run all detected validation commands (tests, linting, build) for a project. Auto-detects validation commands from package.json scripts, Makefile targets, and common patterns. Returns pass/fail status with output for each command.', inputSchema: { type: 'object', properties: { path: { type: 'string', - description: 'Project path', + description: 'Project directory path to validate', }, }, required: ['path'], }, }, + { + name: 'ralph_list_presets', + description: + 'List all available workflow presets for ralph-starter. Presets configure the coding loop behavior: iteration limits, validation, auto-commit, and specialized prompts. Categories include Development (feature, TDD, refactor), Debugging (debug, incident-response), Review (code review, PR review, adversarial), Documentation, and Specialized (API design, migration, performance).', + inputSchema: { + type: 'object', + properties: { + category: { + type: 'string', + description: + 'Filter by category: development, debugging, review, documentation, specialized. Returns all if omitted.', + }, + }, + }, + }, + { + name: 'ralph_fetch_spec', + description: + 'Fetch a specification from an external integration without running the coding loop. Returns the raw spec content as markdown. Supports GitHub (issues, PRs), Linear (tickets by project/team), Notion (pages, databases), and Figma (design specs, tokens, components, content, assets). Use this to preview what will be built before committing to a full run.', + inputSchema: { + type: 'object', + properties: { + path: { + type: 'string', + description: 'Project directory path (used as working directory)', + }, + source: { + type: 'string', + description: 'Integration source: github, linear, notion, or figma', + enum: ['github', 'linear', 'notion', 'figma'], + }, + identifier: { + type: 'string', + description: + 'Source identifier — GitHub: repo URL or "owner/repo#123", Linear: project name, Notion: page URL, Figma: file URL', + }, + mode: { + type: 'string', + description: + 'Figma-specific extraction mode: spec (design specs), tokens (design tokens), components (component code), content (text extraction), assets (icons/images)', + }, + project: { + type: 'string', + description: 'Project or team name filter (for Linear and GitHub)', + }, + label: { + type: 'string', + description: 'Label filter for issue selection (for GitHub and Linear)', + }, + }, + required: ['path', 'source', 'identifier'], + }, + }, ]; } @@ -186,6 +266,12 @@ export async function handleToolCall( case 'ralph_validate': return await handleValidate(args); + case 'ralph_list_presets': + return await handleListPresets(args); + + case 'ralph_fetch_spec': + return await handleFetchSpec(args); + default: return { content: [ @@ -344,6 +430,74 @@ async function handleValidate( }; } +async function handleListPresets( + args: Record | undefined +): Promise<{ content: Array<{ type: 'text'; text: string }> }> { + const parsed = toolSchemas.ralph_list_presets.parse(args); + + const { getPresetsByCategory } = await import('../presets/index.js'); + + const allCategories = getPresetsByCategory(); + const filterCategory = parsed.category?.toLowerCase(); + + const result: Record< + string, + Array<{ + name: string; + description: string; + maxIterations: number; + validate: boolean; + commit: boolean; + }> + > = {}; + + for (const [category, presets] of Object.entries(allCategories)) { + if (filterCategory && !category.toLowerCase().includes(filterCategory)) { + continue; + } + result[category] = presets.map((p) => ({ + name: p.name, + description: p.description, + maxIterations: p.maxIterations, + validate: p.validate, + commit: p.commit, + })); + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result, null, 2), + }, + ], + }; +} + +async function handleFetchSpec( + args: Record | undefined +): Promise<{ content: Array<{ type: 'text'; text: string }> }> { + const parsed = toolSchemas.ralph_fetch_spec.parse(args); + + const { fetchFromIntegration } = await import('../integrations/index.js'); + + const options: Record = {}; + if (parsed.mode) options.mode = parsed.mode; + if (parsed.project) options.project = parsed.project; + if (parsed.label) options.label = parsed.label; + + const result = await fetchFromIntegration(parsed.source, parsed.identifier, options); + + return { + content: [ + { + type: 'text', + text: typeof result === 'string' ? result : JSON.stringify(result, null, 2), + }, + ], + }; +} + function formatInitResult(result: InitCoreResult): string { if (result.success) { return `Successfully initialized Ralph Playbook at ${result.path}\n\nFiles created:\n${result.filesCreated.map((f) => `- ${f}`).join('\n')}`; From aaf1eae6ab9253b9d434e1e3b863030edce475e9 Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 06:13:36 +0000 Subject: [PATCH 3/8] feat: add figma_to_code and batch_issues MCP prompts - Add figma_to_code prompt for Figma design-to-code workflow with framework and mode selection (spec, tokens, components, content) - Add batch_issues prompt for processing multiple GitHub/Linear issues automatically with auto mode - Update fetch_and_build prompt to include Figma as a source option - Update fetch_and_build to use ralph_fetch_spec for preview before building Co-Authored-By: Claude Opus 4.6 --- src/mcp/prompts.ts | 130 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 7 deletions(-) diff --git a/src/mcp/prompts.ts b/src/mcp/prompts.ts index a01c84e..235a910 100644 --- a/src/mcp/prompts.ts +++ b/src/mcp/prompts.ts @@ -45,16 +45,17 @@ export function getPrompts(): Prompt[] { }, { name: 'fetch_and_build', - description: 'Fetch a spec from a source and start building', + description: + 'Fetch a spec from an external source (GitHub, Linear, Notion, Figma) and start building', arguments: [ { name: 'source', - description: 'Source to fetch from (url, github, todoist, linear, notion)', + description: 'Source to fetch from (url, github, linear, notion, figma)', required: true, }, { name: 'identifier', - description: 'Source identifier (URL, project name, etc.)', + description: 'Source identifier (URL, project name, issue number, Figma file URL, etc.)', required: true, }, { @@ -64,6 +65,62 @@ export function getPrompts(): Prompt[] { }, ], }, + { + name: 'figma_to_code', + description: + 'Extract a Figma design and build it as code. Supports design specs, tokens, components, and content extraction.', + arguments: [ + { + name: 'figma_url', + description: 'Figma file or frame URL', + required: true, + }, + { + name: 'framework', + description: + 'Target framework: react, vue, svelte, astro, nextjs, nuxt, html (default: react)', + required: false, + }, + { + name: 'mode', + description: + 'Extraction mode: spec (full design spec), tokens (design tokens as CSS/Tailwind), components (component code), content (text/IA extraction)', + required: false, + }, + { + name: 'path', + description: 'Project directory to build in', + required: false, + }, + ], + }, + { + name: 'batch_issues', + description: + 'Process multiple GitHub or Linear issues automatically in sequence. Each issue becomes a task with its own branch, commits, and PR.', + arguments: [ + { + name: 'source', + description: 'Issue source: github or linear', + required: true, + }, + { + name: 'project', + description: 'GitHub repo (owner/repo) or Linear project name', + required: true, + }, + { + name: 'label', + description: 'Filter issues by label (e.g., "good first issue", "bug", "enhancement")', + required: false, + }, + { + name: 'path', + description: 'Project directory path', + required: false, + }, + ], + }, ]; } @@ -150,13 +207,14 @@ Show me where we are!`, type: 'text', text: `Please fetch a spec and build a project from it. -Source: ${args?.source || '(specify source)'} +Source: ${args?.source || '(specify source: github, linear, notion, or figma)'} Identifier: ${args?.identifier || '(specify identifier)'} Path: ${cwd} -1. First, initialize Ralph Playbook at the path if needed -2. Use ralph_run with the --from option to fetch the spec and start building -3. Monitor progress and help resolve any issues +1. First, use ralph_fetch_spec to preview the spec content +2. Initialize Ralph Playbook at the path if needed +3. Use ralph_run with the --from option to fetch the spec and start building +4. Monitor progress and help resolve any issues Let's build it!`, }, @@ -164,6 +222,64 @@ Let's build it!`, ], }; + case 'figma_to_code': + return { + description: 'Convert Figma design to code', + messages: [ + { + role: 'user', + content: { + type: 'text', + text: `Please extract a Figma design and build it as code. + +Figma URL: ${args?.figma_url || '(specify Figma file URL)'} +Framework: ${args?.framework || 'react'} +Mode: ${args?.mode || 'spec'} +Path: ${cwd} + +1. First, use ralph_fetch_spec with source "figma" to extract the design: + - Use mode "${args?.mode || 'spec'}" to get ${args?.mode === 'tokens' ? 'design tokens' : args?.mode === 'components' ? 'component structure' : args?.mode === 'content' ? 'text content and IA' : 'the full design specification'} +2. Review the extracted spec — check colors, typography, spacing, and component structure +3. Initialize Ralph Playbook if needed, then use ralph_run to build the ${args?.framework || 'React'} implementation +4. The agent will iterate until the UI matches the design spec, running validation between iterations + +Let's bring this design to life!`, + }, + }, + ], + }; + + case 'batch_issues': + return { + description: 'Process multiple issues automatically', + messages: [ + { + role: 'user', + content: { + type: 'text', + text: `Please process multiple issues from ${args?.source || '(github or linear)'} automatically. + +Source: ${args?.source || '(specify: github or linear)'} +Project: ${args?.project || '(specify repo or project name)'} +${args?.label ? `Label filter: ${args.label}` : 'Label: (all issues)'} +Path: ${cwd} + +1. First, use ralph_fetch_spec to preview the available issues from ${args?.source || 'the source'} + - Project: ${args?.project || '(specify)'} + ${args?.label ? `- Filter by label: "${args.label}"` : ''} +2. Review the issues and confirm which ones to process +3. Use ralph_run with auto mode enabled to process each issue: + - Each issue gets its own branch + - Code changes are validated and auto-committed + - A PR is created for each completed issue +4. Monitor progress across all issues + +Let's batch process these issues!`, + }, + }, + ], + }; + default: throw new Error(`Unknown prompt: ${name}`); } From 6e41ab16be7dfa6bbb56750b24a968783d8941cc Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 06:14:12 +0000 Subject: [PATCH 4/8] feat: expose activity log as MCP resource Add .ralph/activity.md as a readable MCP resource so Claude Desktop and other MCP clients can access loop execution history, timing data, and cost information. Co-Authored-By: Claude Opus 4.6 --- src/mcp/resources.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/mcp/resources.ts b/src/mcp/resources.ts index 25fa021..dfbd521 100644 --- a/src/mcp/resources.ts +++ b/src/mcp/resources.ts @@ -67,6 +67,18 @@ export async function getResources(): Promise { } } + // Activity log + const activityPath = join(cwd, '.ralph', 'activity.md'); + if (existsSync(activityPath)) { + resources.push({ + uri: 'ralph://project/activity', + name: 'Activity Log', + description: + 'Loop execution history with timing, cost data, and task outcomes (.ralph/activity.md)', + mimeType: 'text/markdown', + }); + } + return resources; } @@ -105,6 +117,10 @@ export async function handleResourceRead(uri: string): Promise<{ filePath = join(cwd, 'PROMPT_plan.md'); break; + case 'activity': + filePath = join(cwd, '.ralph', 'activity.md'); + break; + default: // Handle specs if (resourcePath.startsWith('specs/')) { From 66f3748c2e9e4b94089cad026799470923b41759 Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 13:52:30 +0000 Subject: [PATCH 5/8] feat: add seo category to template filters Add 'seo' to the category filter options in CLI help text and docs, matching the new SEO & AEO templates added to ralph-templates. Co-Authored-By: Claude Opus 4.6 --- docs/docs/cli/template.md | 1 + src/cli.ts | 5 ++++- src/commands/template.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/docs/cli/template.md b/docs/docs/cli/template.md index 45bcc61..70167e8 100644 --- a/docs/docs/cli/template.md +++ b/docs/docs/cli/template.md @@ -55,6 +55,7 @@ Templates are organized into categories: - `devops` - DevOps tools and automation - `mobile` - Mobile app projects - `tools` - CLI tools and utilities +- `seo` - SEO and Answer Engine Optimization toolkits ## Examples diff --git a/src/cli.ts b/src/cli.ts index f410560..95e8ff6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -264,7 +264,10 @@ program program .command('template [action] [args...]') .description('Browse and use project templates from ralph-templates') - .option('--category ', 'Filter by category (web-dev, blockchain, devops, mobile, tools)') + .option( + '--category ', + 'Filter by category (web-dev, blockchain, devops, mobile, tools, seo)' + ) .option('--refresh', 'Force refresh the cache') .option('--auto', 'Skip confirmation prompts') .option('--output-dir ', 'Directory to create the project in') diff --git a/src/commands/template.ts b/src/commands/template.ts index a3e7c88..37be65c 100644 --- a/src/commands/template.ts +++ b/src/commands/template.ts @@ -374,7 +374,7 @@ ${chalk.bold('Commands:')} browse Interactive template browser ${chalk.bold('Options:')} - --category Filter by category (web-dev, blockchain, devops, mobile, tools) + --category Filter by category (web-dev, blockchain, devops, mobile, tools, seo) --refresh Force refresh the cache --auto Skip confirmation prompts --output-dir Directory to create the project in From 790a69781e3971eada12c88396a069161b0c5965 Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 14:41:34 +0000 Subject: [PATCH 6/8] fix: extract shared getPackageVersion and fix handleFetchSpec path - Create src/utils/version.ts with a robust version resolver that walks up parent directories, handles multiple dist depths, and caches the result - Replace duplicate getPackageVersion in cli.ts and mcp/server.ts with the shared import - Fix handleFetchSpec not passing parsed.path to fetchFromIntegration, preserving working-directory context Co-Authored-By: Claude Opus 4.6 --- src/cli.ts | 10 ++-------- src/mcp/server.ts | 15 +-------------- src/mcp/tools.ts | 1 + src/utils/version.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 src/utils/version.ts diff --git a/src/cli.ts b/src/cli.ts index 95e8ff6..5e239e9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,8 +1,5 @@ #!/usr/bin/env node -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; import chalk from 'chalk'; import { Command } from 'commander'; import { authCommand } from './commands/auth.js'; @@ -19,13 +16,10 @@ import { sourceCommand } from './commands/source.js'; import { templateCommand } from './commands/template.js'; import { startMcpServer } from './mcp/server.js'; import { formatPresetsHelp, getPresetNames } from './presets/index.js'; +import { getPackageVersion } from './utils/version.js'; import { runIdeaMode, runWizard } from './wizard/index.js'; -// Read version from package.json dynamically -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')); -const VERSION = packageJson.version; +const VERSION = getPackageVersion(); const program = new Command(); diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 71212e9..ac04994 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -1,6 +1,3 @@ -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { @@ -11,21 +8,11 @@ import { ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; +import { getPackageVersion } from '../utils/version.js'; import { getPrompts, handleGetPrompt } from './prompts.js'; import { getResources, handleResourceRead } from './resources.js'; import { getTools, handleToolCall } from './tools.js'; -function getPackageVersion(): string { - try { - const __dirname = dirname(fileURLToPath(import.meta.url)); - const pkgPath = join(__dirname, '..', '..', 'package.json'); - const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); - return pkg.version || '0.1.0'; - } catch { - return '0.1.0'; - } -} - /** * Create and configure the MCP server */ diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index 92dae46..6721dc4 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -482,6 +482,7 @@ async function handleFetchSpec( const { fetchFromIntegration } = await import('../integrations/index.js'); const options: Record = {}; + if (parsed.path) options.path = parsed.path; if (parsed.mode) options.mode = parsed.mode; if (parsed.project) options.project = parsed.project; if (parsed.label) options.label = parsed.label; diff --git a/src/utils/version.ts b/src/utils/version.ts new file mode 100644 index 0000000..d00cd43 --- /dev/null +++ b/src/utils/version.ts @@ -0,0 +1,40 @@ +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const DEFAULT_VERSION = '0.1.0'; + +let cachedVersion: string | null = null; + +/** + * Resolves the package version by walking up parent directories + * from the compiled dist/ location to find package.json. + */ +export function getPackageVersion(): string { + if (cachedVersion) return cachedVersion; + + const __dirname = dirname(fileURLToPath(import.meta.url)); + + // Walk up from current file to find package.json + // Handles both src/ (dev) and dist/ (built) depths + const candidates = [ + join(__dirname, '..', 'package.json'), // src/utils/ → package.json + join(__dirname, '..', '..', 'package.json'), // dist/utils/ → package.json + join(__dirname, '..', '..', '..', 'package.json'), // dist/src/utils/ → package.json + ]; + + for (const candidate of candidates) { + try { + const pkg = JSON.parse(readFileSync(candidate, 'utf-8')); + if (pkg.version) { + cachedVersion = pkg.version as string; + return cachedVersion; + } + } catch { + // Try next candidate + } + } + + cachedVersion = DEFAULT_VERSION; + return cachedVersion; +} From 82a31a8c37757452da9dfd406fde07b48d314844 Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 15:54:52 +0000 Subject: [PATCH 7/8] fix: address CodeRabbit review feedback on MCP tools and prompts - Fix batch_issues prompt: use ralph_run with auto mode instead of incorrectly referencing ralph_fetch_spec for listing issues - Fix handleListPresets category filter: use strict equality instead of substring match to prevent unintended matches - handleFetchSpec already passes path to fetchFromIntegration (linter fix) Co-Authored-By: Claude Opus 4.6 --- src/mcp/prompts.ts | 10 +++++----- src/mcp/tools.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mcp/prompts.ts b/src/mcp/prompts.ts index 235a910..3fa8aff 100644 --- a/src/mcp/prompts.ts +++ b/src/mcp/prompts.ts @@ -264,15 +264,15 @@ Project: ${args?.project || '(specify repo or project name)'} ${args?.label ? `Label filter: ${args.label}` : 'Label: (all issues)'} Path: ${cwd} -1. First, use ralph_fetch_spec to preview the available issues from ${args?.source || 'the source'} - - Project: ${args?.project || '(specify)'} +1. Use ralph_run with auto mode to batch-process issues from ${args?.source || 'the source'}: + - Set from="${args?.source || '(github or linear)'}" and project="${args?.project || '(specify)'}" ${args?.label ? `- Filter by label: "${args.label}"` : ''} -2. Review the issues and confirm which ones to process -3. Use ralph_run with auto mode enabled to process each issue: + - Enable auto=true, commit=true, and validate=true - Each issue gets its own branch - Code changes are validated and auto-committed - A PR is created for each completed issue -4. Monitor progress across all issues +2. Monitor progress across all issues +3. If an individual issue fails, note the failure and continue to the next one Let's batch process these issues!`, }, diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index 6721dc4..3793a80 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -452,7 +452,7 @@ async function handleListPresets( > = {}; for (const [category, presets] of Object.entries(allCategories)) { - if (filterCategory && !category.toLowerCase().includes(filterCategory)) { + if (filterCategory && category.toLowerCase() !== filterCategory) { continue; } result[category] = presets.map((p) => ({ From 22ba3d1201a3aeede8271e85094b9e6aa2df099e Mon Sep 17 00:00:00 2001 From: ruben-cytonic Date: Fri, 6 Feb 2026 16:18:09 +0000 Subject: [PATCH 8/8] fix: address CodeRabbit review round 2 - Normalize framework casing in figma_to_code prompt (consistent display name) - Guard undefined args in handleListPresets with fallback to empty object - Enforce non-empty path in ralph_fetch_spec schema (.min(1)) and always assign path to options instead of truthiness check Co-Authored-By: Claude Opus 4.6 --- src/mcp/prompts.ts | 9 ++++++--- src/mcp/tools.ts | 7 +++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/mcp/prompts.ts b/src/mcp/prompts.ts index 3fa8aff..277da6e 100644 --- a/src/mcp/prompts.ts +++ b/src/mcp/prompts.ts @@ -222,7 +222,9 @@ Let's build it!`, ], }; - case 'figma_to_code': + case 'figma_to_code': { + const framework = args?.framework || 'react'; + const displayFramework = framework.charAt(0).toUpperCase() + framework.slice(1); return { description: 'Convert Figma design to code', messages: [ @@ -233,14 +235,14 @@ Let's build it!`, text: `Please extract a Figma design and build it as code. Figma URL: ${args?.figma_url || '(specify Figma file URL)'} -Framework: ${args?.framework || 'react'} +Framework: ${framework} Mode: ${args?.mode || 'spec'} Path: ${cwd} 1. First, use ralph_fetch_spec with source "figma" to extract the design: - Use mode "${args?.mode || 'spec'}" to get ${args?.mode === 'tokens' ? 'design tokens' : args?.mode === 'components' ? 'component structure' : args?.mode === 'content' ? 'text content and IA' : 'the full design specification'} 2. Review the extracted spec — check colors, typography, spacing, and component structure -3. Initialize Ralph Playbook if needed, then use ralph_run to build the ${args?.framework || 'React'} implementation +3. Initialize Ralph Playbook if needed, then use ralph_run to build the ${displayFramework} implementation 4. The agent will iterate until the UI matches the design spec, running validation between iterations Let's bring this design to life!`, @@ -248,6 +250,7 @@ Let's bring this design to life!`, }, ], }; + } case 'batch_issues': return { diff --git a/src/mcp/tools.ts b/src/mcp/tools.ts index 3793a80..049a2cc 100644 --- a/src/mcp/tools.ts +++ b/src/mcp/tools.ts @@ -50,7 +50,7 @@ const toolSchemas = { }), ralph_fetch_spec: z.object({ - path: z.string().describe('Project directory path'), + path: z.string().min(1).describe('Project directory path'), source: z .enum(['github', 'linear', 'notion', 'figma']) .describe('Integration source to fetch from'), @@ -433,7 +433,7 @@ async function handleValidate( async function handleListPresets( args: Record | undefined ): Promise<{ content: Array<{ type: 'text'; text: string }> }> { - const parsed = toolSchemas.ralph_list_presets.parse(args); + const parsed = toolSchemas.ralph_list_presets.parse(args ?? {}); const { getPresetsByCategory } = await import('../presets/index.js'); @@ -481,8 +481,7 @@ async function handleFetchSpec( const { fetchFromIntegration } = await import('../integrations/index.js'); - const options: Record = {}; - if (parsed.path) options.path = parsed.path; + const options: Record = { path: parsed.path }; if (parsed.mode) options.mode = parsed.mode; if (parsed.project) options.project = parsed.project; if (parsed.label) options.label = parsed.label;