Skip to content
Open
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,11 @@ Titles must be unique.
```bash
ralphy --github owner/repo
ralphy --github owner/repo --github-label "ready"
ralphy --github owner/repo --github-label "task" --github-order 2,3,4,5,6
```

Use `--github-order` to execute issues in a specific order. Without it, issues are processed in the order returned by the GitHub API (newest first). The flag takes comma-separated issue numbers — only those issues will be executed, in the exact order specified.

## Parallel Execution

```bash
Expand Down Expand Up @@ -300,6 +303,7 @@ ralphy --parallel --sandbox
| `--json FILE` | JSON task file |
| `--github REPO` | use GitHub issues |
| `--github-label TAG` | filter issues by label |
| `--github-order 2,3,4` | execute issues in this order |
| `--sync-issue N` | sync PRD progress to GitHub issue #N |
| `--model NAME` | override model for any engine |
| `--sonnet` | shortcut for `--claude --model sonnet` |
Expand Down
10 changes: 10 additions & 0 deletions cli/src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function createProgram(): Command {
.option("--json <file>", "JSON task file")
.option("--github <repo>", "GitHub repo for issues (owner/repo)")
.option("--github-label <label>", "Filter GitHub issues by label")
.option("--github-order <issues>", "Execute GitHub issues in this order (comma-separated issue numbers, e.g. 2,3,4,5,6)")
.option("--sync-issue <number>", "Sync PRD file to GitHub issue body on each iteration")
.option("--no-commit", "Don't auto-commit changes")
.option("--browser", "Enable browser automation (agent-browser)")
Expand Down Expand Up @@ -152,6 +153,15 @@ export function parseArgs(args: string[]): {
prdIsFolder,
githubRepo: opts.github || "",
githubLabel: opts.githubLabel || "",
githubOrder: opts.githubOrder
? (() => {
const nums = opts.githubOrder
.split(",")
.map((n: string) => Number.parseInt(n.trim(), 10))
.filter((n: number) => !Number.isNaN(n));
return nums.length > 0 ? nums : undefined;
})()
: undefined,
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
syncIssue: opts.syncIssue ? Number.parseInt(opts.syncIssue, 10) || undefined : undefined,
autoCommit: opts.commit !== false,
browserEnabled: opts.browser === true ? "true" : opts.browser === false ? "false" : "auto",
Expand Down
1 change: 1 addition & 0 deletions cli/src/cli/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export async function runLoop(options: RuntimeOptions): Promise<void> {
filePath: options.prdFile,
repo: options.githubRepo,
label: options.githubLabel,
order: options.githubOrder,
});
const taskSource = new CachedTaskSource(innerTaskSource);

Expand Down
2 changes: 2 additions & 0 deletions cli/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export interface RuntimeOptions {
githubRepo: string;
/** GitHub issue label filter */
githubLabel: string;
/** Ordered list of GitHub issue numbers to execute in sequence */
githubOrder?: number[];
/** GitHub issue number to sync PRD with on each iteration */
syncIssue?: number;
/** Auto-commit changes */
Expand Down
35 changes: 32 additions & 3 deletions cli/src/tasks/github.ts
Comment thread
idorozin marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ export class GitHubTaskSource implements TaskSource {
private owner: string;
private repo: string;
private label?: string;
private order?: number[];
private cache: GitHubCache | null = null;

constructor(repoPath: string, label?: string) {
constructor(repoPath: string, label?: string, order?: number[]) {
// Parse owner/repo format
const [owner, repo] = repoPath.split("/");
if (!owner || !repo) {
Expand All @@ -37,6 +38,7 @@ export class GitHubTaskSource implements TaskSource {
this.owner = owner;
this.repo = repo;
this.label = label;
this.order = order;

// Use GITHUB_TOKEN from environment
this.octokit = new Octokit({
Expand Down Expand Up @@ -75,13 +77,35 @@ export class GitHubTaskSource implements TaskSource {
per_page: 100,
});

const tasks = issues.map((issue) => ({
let tasks = issues.map((issue) => ({
id: `${issue.number}:${issue.title}`,
title: issue.title,
body: issue.body || undefined,
completed: false,
}));

// Sort by specified order if provided
if (this.order && this.order.length > 0) {
const orderMap = new Map(this.order.map((num, idx) => [num, idx]));
const tagged = tasks.map((t) => ({
task: t,
issueNum: Number.parseInt(t.id.split(":")[0], 10),
}));
const filtered = tagged.filter(({ issueNum }) => orderMap.has(issueNum));

const foundNums = new Set(filtered.map(({ issueNum }) => issueNum));
const missing = this.order.filter((n) => !foundNums.has(n));
if (missing.length > 0) {
console.warn(
`[ralphy] Warning: --github-order specified issues not found among open issues: ${missing.join(", ")}`,
);
}
Comment on lines +96 to +102
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Warning fires as false positive after issues are closed

The missing-issue warning is emitted on every cache refresh, not just the first time. During normal operation, once issue #2 is processed and closed via markComplete(), the cache is invalidated. The next call to fetchOpenIssues() fetches only state: "open" issues, so issue #2 is no longer in the results. The check on line 97 then flags it as missing and emits the warning — even though it was just successfully completed.

This means a user running --github-order 2,3,4,5 will see "Warning: issues not found among open issues: 2" after processing the very first issue, then "...2, 3" after the second, and so on. This is confusing and may cause users to think something went wrong when the workflow is proceeding correctly.

Consider filtering this.order to exclude issues that have already been closed before computing missing. You can cross-reference with the closed-count query, or simply only warn on the first fetch (e.g. when this.cache is null):

// Only warn on the initial fetch, not on subsequent refreshes after issues are closed
const isFirstFetch = !this.cache;
const foundNums = new Set(filtered.map(({ issueNum }) => issueNum));
const missing = this.order.filter((n) => !foundNums.has(n));
if (isFirstFetch && missing.length > 0) {
    console.warn(
        `[ralphy] Warning: --github-order specified issues not found among open issues: ${missing.join(", ")}`,
    );
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/tasks/github.ts
Line: 96-102

Comment:
**Warning fires as false positive after issues are closed**

The missing-issue warning is emitted on every cache refresh, not just the first time. During normal operation, once issue `#2` is processed and closed via `markComplete()`, the cache is invalidated. The next call to `fetchOpenIssues()` fetches only `state: "open"` issues, so issue `#2` is no longer in the results. The check on line 97 then flags it as missing and emits the warning — even though it was just successfully completed.

This means a user running `--github-order 2,3,4,5` will see "Warning: issues not found among open issues: 2" after processing the very first issue, then "...2, 3" after the second, and so on. This is confusing and may cause users to think something went wrong when the workflow is proceeding correctly.

Consider filtering `this.order` to exclude issues that have already been closed before computing `missing`. You can cross-reference with the closed-count query, or simply only warn on the first fetch (e.g. when `this.cache` is null):

```ts
// Only warn on the initial fetch, not on subsequent refreshes after issues are closed
const isFirstFetch = !this.cache;
const foundNums = new Set(filtered.map(({ issueNum }) => issueNum));
const missing = this.order.filter((n) => !foundNums.has(n));
if (isFirstFetch && missing.length > 0) {
    console.warn(
        `[ralphy] Warning: --github-order specified issues not found among open issues: ${missing.join(", ")}`,
    );
}
```

How can I resolve this? If you propose a fix, please make it concise.


tasks = filtered
.sort((a, b) => orderMap.get(a.issueNum)! - orderMap.get(b.issueNum)!)
.map(({ task }) => task);
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

// Update cache (preserve closed count if we have it)
this.cache = {
openIssues: tasks,
Expand Down Expand Up @@ -139,7 +163,12 @@ export class GitHubTaskSource implements TaskSource {
per_page: 100,
});

const closedCount = issues.length;
// When order is specified, only count closed issues that are in the order list
let closedCount = issues.length;
if (this.order && this.order.length > 0) {
const orderSet = new Set(this.order);
closedCount = issues.filter((issue) => orderSet.has(issue.number)).length;
}

// Update cache with closed count
if (this.cache) {
Expand Down
4 changes: 3 additions & 1 deletion cli/src/tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface TaskSourceOptions {
repo?: string;
/** Label filter for GitHub source */
label?: string;
/** Ordered list of issue numbers to execute in sequence */
order?: number[];
}

/**
Expand Down Expand Up @@ -56,7 +58,7 @@ export function createTaskSource(options: TaskSourceOptions): TaskSource {
if (!options.repo) {
throw new Error("repo is required for github task source");
}
return new GitHubTaskSource(options.repo, options.label);
return new GitHubTaskSource(options.repo, options.label, options.order);

default:
throw new Error(`Unknown task source type: ${options.type}`);
Expand Down