diff --git a/.changeset/heavy-shoes-report.md b/.changeset/heavy-shoes-report.md new file mode 100644 index 0000000..c4c73a3 --- /dev/null +++ b/.changeset/heavy-shoes-report.md @@ -0,0 +1,5 @@ +--- +"@techatnyu/ralph": patch +--- + +Add a \ralph init`command to scaffold a`.ralph` workspace for coding projects.` diff --git a/apps/tui/README.md b/apps/tui/README.md index ad89ef2..9fb34dd 100644 --- a/apps/tui/README.md +++ b/apps/tui/README.md @@ -8,6 +8,14 @@ In local development, run `bun run dev` from the repo root. Turborepo will run the TUI and daemon together, and the TUI will wait for the foreground daemon instead of spawning a detached background process. +## Scaffold a workspace + +From the `apps/tui` directory, run: + +```bash +bun run src/cli.ts init /path/to/project +``` + ## Advanced daemon control ```bash diff --git a/apps/tui/package.json b/apps/tui/package.json index fff25f7..424433c 100644 --- a/apps/tui/package.json +++ b/apps/tui/package.json @@ -20,11 +20,12 @@ "typescript": "^5" }, "dependencies": { - "@techatnyu/ralphd": "workspace:*", "@crustjs/core": "^0.0.13", + "@crustjs/create": "^0.0.4", "@crustjs/plugins": "^0.0.16", "@opentui/core": "^0.1.77", "@opentui/react": "^0.1.77", + "@techatnyu/ralphd": "workspace:*", "react": "^19.2.4" } } diff --git a/apps/tui/src/cli.ts b/apps/tui/src/cli.ts index a0daf97..5b0f244 100644 --- a/apps/tui/src/cli.ts +++ b/apps/tui/src/cli.ts @@ -1,5 +1,6 @@ import { Crust } from "@crustjs/core"; import { helpPlugin } from "@crustjs/plugins"; +import { bootstrapRalphWorkspace } from "./scaffold"; import { daemon, type JobState, @@ -36,6 +37,22 @@ const cli = new Crust("ralph") .run(async () => { await runTui(); }) + .command("init", (cmd) => + cmd + .meta({ description: "Create a Ralph workspace for a project" }) + .args([ + { + name: "directory", + type: "string", + required: true, + description: "Project directory where .ralph will be created", + }, + ]) + .run(async ({ args }) => { + await bootstrapRalphWorkspace(args.directory); + console.log(`Created Ralph workspace in ${args.directory}/.ralph`); + }), + ) .command("daemon", (daemonCommand) => daemonCommand .meta({ description: "Manage the background daemon" }) diff --git a/apps/tui/src/scaffold.ts b/apps/tui/src/scaffold.ts new file mode 100644 index 0000000..dbd74b9 --- /dev/null +++ b/apps/tui/src/scaffold.ts @@ -0,0 +1,17 @@ +import { scaffold } from "@crustjs/create"; +import { basename, resolve } from "node:path"; + +export async function bootstrapRalphWorkspace( + projectDirectory: string, +): Promise { + const absoluteProjectDirectory = resolve(projectDirectory); + const projectName = basename(absoluteProjectDirectory); + + await scaffold({ + template: new URL("../templates/ralph-workspace", import.meta.url), + dest: resolve(absoluteProjectDirectory, ".ralph"), + context: { + projectName, + }, + }); +} diff --git a/apps/tui/templates/ralph-workspace/PROMPT.md b/apps/tui/templates/ralph-workspace/PROMPT.md new file mode 100644 index 0000000..a53178e --- /dev/null +++ b/apps/tui/templates/ralph-workspace/PROMPT.md @@ -0,0 +1,109 @@ +# Ralph Agent Instructions + +You are an AI coding agent working through a project task by task. Each session, you complete EXACTLY ONE task and hand off to the next iteration. + +## Step 1: Understand Context + +Read these files to understand the current state: + +1. **`SPEC.md`** - Project specification (what you're building) +2. **`prd.json`** - Task list with all tasks and their status +3. **`progress.md`** - Log of completed work and notes from previous iterations + +> **Note**: These files may be located in a `.ralph/` subdirectory (e.g., `.ralph/SPEC.md`, `.ralph/prd.json`, `.ralph/progress.md`). Check both locations. + +## Step 2: Select a Task + +From `prd.json`, select **ONE** task to work on: + +- Choose a task where `passed: false` +- Analyze task descriptions and the current project state to determine the best task to work on next +- Consider logical dependencies (e.g., "add authentication" should come before "add protected routes") +- If unclear, prefer tasks listed earlier in the file + +## Step 3: Complete the Task + +Work through the task: + +1. Follow the `subtasks` array as your implementation guide +2. Write clean, well-structured code +3. **Verify your work** before marking complete: + - Run tests if they exist + - Run type checks if applicable (`tsc --noEmit`, `mypy`, etc.) + - Run linting if configured + - Manually verify the feature works as expected + +## Step 4: Update Progress + +After completing the task, update `progress.md`: + +### Format + +Each entry should be separated by "---" and include: + +- Task: The task description from prd.json +- Completed: What was accomplished +- Files Changed: List of modified files +- Decisions: Key architectural or implementation decisions +- Notes for Next Agent: Context for future iterations + +### Rules + +- **APPEND ONLY**: Never modify or delete previous entries +- Each new entry starts with "---" separator +- Be specific about what was done and why +- Leave helpful context for future iterations + +### Example Entry + +```markdown +--- + +## Task: [Task description from prd.json] + +### Completed + +- [What you accomplished] + +### Files Changed + +- [List of files] + +### Decisions + +- [Any architectural or implementation decisions] + +### Notes for Future Agent + +- [Helpful context for future iterations] +``` + +## Step 5: Update prd.json + +1. Set `passed: true` for the completed task +2. Update `notes` field of **any other tasks** if you discovered relevant context that is helpful for completing the given task + +## Step 6: Commit Changes + +- Create a git commit with a clear, descriptive message. +- Include what was implemented, not just "completed task" + +## Step 7: Signal Completion + +When finished, output this exact string on its own line: + +``` +RALPH_TASK_COMPLETE +``` + +This signals the orchestration script to start the next iteration. + +--- + +## Important Rules + +1. **One task per session** - Do not work on multiple tasks +2. **Verify before marking complete** - Ensure the implementation actually works +3. **Append-only progress** - Never edit previous progress.md entries +4. **Leave context** - Future iterations depend on your notes +5. **Commit your work** - All changes must be committed before signaling completion diff --git a/apps/tui/templates/ralph-workspace/SPEC.md b/apps/tui/templates/ralph-workspace/SPEC.md new file mode 100644 index 0000000..0d24ec2 --- /dev/null +++ b/apps/tui/templates/ralph-workspace/SPEC.md @@ -0,0 +1,2 @@ +# {{projectName}} + \ No newline at end of file diff --git a/apps/tui/templates/ralph-workspace/config b/apps/tui/templates/ralph-workspace/config new file mode 100644 index 0000000..d000974 --- /dev/null +++ b/apps/tui/templates/ralph-workspace/config @@ -0,0 +1,5 @@ +RALPH_AGENT_CMD="opencode run" +RALPH_PROMPT_FILE="PROMPT.md" +RALPH_SPEC_FILE="SPEC.md" +RALPH_PRD_FILE="prd.json" +RALPH_PROGRESS_FILE="progress.md" diff --git a/apps/tui/templates/ralph-workspace/prd.json b/apps/tui/templates/ralph-workspace/prd.json new file mode 100644 index 0000000..e6e3456 --- /dev/null +++ b/apps/tui/templates/ralph-workspace/prd.json @@ -0,0 +1,3 @@ +{ + "tasks": [] +} \ No newline at end of file diff --git a/apps/tui/templates/ralph-workspace/progress.md b/apps/tui/templates/ralph-workspace/progress.md new file mode 100644 index 0000000..480cbc4 --- /dev/null +++ b/apps/tui/templates/ralph-workspace/progress.md @@ -0,0 +1 @@ +# Progress Log \ No newline at end of file diff --git a/bun.lock b/bun.lock index d2a906f..a2f9b01 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,9 @@ "workspaces": { "": { "name": "ralph", + "dependencies": { + "@crustjs/create": "^0.0.4", + }, "devDependencies": { "@biomejs/biome": "2.3.14", "@changesets/changelog-git": "^0.2.1", @@ -46,6 +49,7 @@ "version": "0.0.1", "dependencies": { "@crustjs/core": "^0.0.13", + "@crustjs/create": "^0.0.4", "@crustjs/plugins": "^0.0.16", "@opentui/core": "^0.1.77", "@opentui/react": "^0.1.77", @@ -180,6 +184,8 @@ "@crustjs/core": ["@crustjs/core@0.0.13", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-kAjSL68kjEdO68uGQVWn6RQwaFFx17skogokyTF3UutF7WZepMhzQ7Zhw4Q5joKrVZJjCcSkRkDYzb6bye/Rgg=="], + "@crustjs/create": ["@crustjs/create@0.0.4", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-vGbJKCc0zQQdSjfYo4gMYplGvfh1UNRupcyGTR9Rg4U8ej0UpmVLkoSz7b4wcC7XBuLs3pmYaLYZhT/k24SWNQ=="], + "@crustjs/plugins": ["@crustjs/plugins@0.0.16", "", { "dependencies": { "@crustjs/style": "0.0.5" }, "peerDependencies": { "@crustjs/core": "0.0.13", "typescript": "^5" } }, "sha512-Y3MuwpHxPEutEqBzpR8sLuG3z61BRPDiWDOQppw1M2xaEI/RTTfuYRVfPF58NGrcKSRFFDn8vfty+rUdkacnwg=="], "@crustjs/style": ["@crustjs/style@0.0.5", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-KZhxi2iCMYEMihMDmOrHkC4gl5INtogR9AEfbOkjwot74cmNQ2vTUfiZsLmWzBQ23nhPQ0vBwZBNFN+IpH+F0w=="], @@ -584,7 +590,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], + "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], @@ -666,7 +672,7 @@ "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], - "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], + "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],