feat: interactive GitHub issues wizard (ralph-starter github)#270
feat: interactive GitHub issues wizard (ralph-starter github)#270rubenmarcus wants to merge 5 commits intomainfrom
Conversation
…or-url) Shared utilities for integration wizards: - ensureCredentials(): check for existing auth, prompt for API key if missing - askBrowseOrUrl(): common "browse or paste URL" prompt - askForUrl(): URL input with domain validation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New `githubCommand()` that guides users through: 1. Authentication check (gh CLI or token prompt) 2. Browse repos/issues or paste a URL 3. Optional label filtering 4. Multi-select issues (checkbox) 5. Delegates to runCommand() for each selected issue Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register `ralph-starter github` as top-level command in cli.ts (same pattern as `ralph-starter figma`) - Add wizard fallback in run.ts: `--from github` without --project/--issue redirects to the interactive github wizard instead of erroring Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Interactive Wizard" section to docs/docs/sources/github.md - Add github/linear/notion wizard commands to README commands table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Issue Linking ReminderThis PR doesn't appear to have a linked issue. Consider linking to:
Using If this PR doesn't need an issue, you can ignore this message. |
✔️ Bundle Size Analysis
Bundle breakdown |
🔗 Docs PreviewPreview URL: https://feat-github-integration-wiza.ralph-starter-docs.pages.dev This preview was deployed from the latest commit on this PR. |
Greptile SummaryThis PR introduces a Key findings:
The core wizard flow is sound and the fallback in run.ts is well-placed. However, the style violations, circular import, browse-mode auth gap, and unimplemented command documentation should be addressed. Confidence Score: 3/5
Sequence DiagramsequenceDiagram
actor User
participant CLI as cli.ts
participant GH as github.ts (githubCommand)
participant Shared as shared.ts
participant GhCLI as gh CLI
participant Run as run.ts (runCommand)
User->>CLI: ralph-starter github [options]
CLI->>GH: githubCommand(options)
GH->>Shared: ensureCredentials('github', ...)
Shared->>GhCLI: gh auth status
alt gh CLI authenticated
GhCLI-->>Shared: ok → '__cli_auth__'
else no gh CLI / not authed
Shared->>Shared: check env var / sources.json
alt no token found
Shared->>User: prompt for token (masked)
User-->>Shared: token
Shared->>Shared: setSourceCredential → sources.json
end
end
Shared-->>GH: credential
GH->>Shared: askBrowseOrUrl('GitHub')
Shared-->>GH: 'browse' | 'url'
alt URL mode
GH->>Shared: askForUrl('GitHub', /github\.com/)
Shared-->>GH: url string
GH->>GH: parseGitHubUrl(url)
GH->>Run: runCommand(undefined, {from:'github', project, issue, ...})
else Browse mode
GH->>GhCLI: gh repo list --json
GhCLI-->>GH: repos[]
GH->>User: select a repository
GH->>GhCLI: gh label list -R owner/repo
GhCLI-->>GH: labels[]
GH->>User: (optional) filter by label
GH->>GhCLI: gh issue list -R owner/repo [--label x]
GhCLI-->>GH: issues[]
GH->>User: checkbox multi-select issues
loop for each selected issue
GH->>Run: runCommand(undefined, {from:'github', project, issue, ...})
end
end
alt run.ts --from github (no --project/--issue)
Run->>GH: githubCommand(options) [wizard fallback]
end
Last reviewed commit: b3e759b |
src/commands/github.ts
Outdated
| export interface GitHubWizardOptions { | ||
| commit?: boolean; | ||
| push?: boolean; | ||
| pr?: boolean; | ||
| validate?: boolean; | ||
| maxIterations?: number; | ||
| agent?: string; | ||
| } | ||
|
|
||
| interface GitHubRepo { | ||
| name: string; | ||
| owner: { login: string }; | ||
| description: string; | ||
| } | ||
|
|
||
| interface GitHubIssue { | ||
| number: number; | ||
| title: string; | ||
| labels: Array<{ name: string }>; | ||
| } | ||
|
|
||
| interface GitHubLabel { | ||
| name: string; |
There was a problem hiding this comment.
Use type instead of interface for simple data structures.
Per the project's TypeScript conventions, type should be used by default for DTOs and simple data structures that don't use inheritance or extension. GitHubWizardOptions, GitHubRepo, GitHubIssue, and GitHubLabel are all plain data structures with no extension. The same applies to CredentialOptions in src/integrations/wizards/shared.ts:12.
| export interface GitHubWizardOptions { | |
| commit?: boolean; | |
| push?: boolean; | |
| pr?: boolean; | |
| validate?: boolean; | |
| maxIterations?: number; | |
| agent?: string; | |
| } | |
| interface GitHubRepo { | |
| name: string; | |
| owner: { login: string }; | |
| description: string; | |
| } | |
| interface GitHubIssue { | |
| number: number; | |
| title: string; | |
| labels: Array<{ name: string }>; | |
| } | |
| interface GitHubLabel { | |
| name: string; | |
| export type GitHubWizardOptions = { | |
| commit?: boolean; | |
| push?: boolean; | |
| pr?: boolean; | |
| validate?: boolean; | |
| maxIterations?: number; | |
| agent?: string; | |
| } | |
| type GitHubRepo = { | |
| name: string; | |
| owner: { login: string }; | |
| description: string; | |
| } | |
| type GitHubIssue = { | |
| number: number; | |
| title: string; | |
| labels: Array<{ name: string }>; | |
| } | |
| type GitHubLabel = { | |
| name: string; | |
| } |
Also update src/integrations/wizards/shared.ts:12:
export type CredentialOptions = {
credKey: string;
consoleUrl: string;
envVar: string;
checkCliAuth?: () => Promise<boolean>;
}Rule Used: Use type by default in TypeScript unless you spe... (source)
Learnt From
cytonic-network/ai-frontend#48
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/github.ts
Line: 16-38
Comment:
Use `type` instead of `interface` for simple data structures.
Per the project's TypeScript conventions, `type` should be used by default for DTOs and simple data structures that don't use inheritance or extension. `GitHubWizardOptions`, `GitHubRepo`, `GitHubIssue`, and `GitHubLabel` are all plain data structures with no extension. The same applies to `CredentialOptions` in `src/integrations/wizards/shared.ts:12`.
```suggestion
export type GitHubWizardOptions = {
commit?: boolean;
push?: boolean;
pr?: boolean;
validate?: boolean;
maxIterations?: number;
agent?: string;
}
type GitHubRepo = {
name: string;
owner: { login: string };
description: string;
}
type GitHubIssue = {
number: number;
title: string;
labels: Array<{ name: string }>;
}
type GitHubLabel = {
name: string;
}
```
Also update `src/integrations/wizards/shared.ts:12`:
```ts
export type CredentialOptions = {
credKey: string;
consoleUrl: string;
envVar: string;
checkCliAuth?: () => Promise<boolean>;
}
```
**Rule Used:** Use `type` by default in TypeScript unless you spe... ([source](https://app.greptile.com/review/custom-context?memory=c862f053-5655-4b41-be69-c840e3c9f280))
**Learnt From**
[cytonic-network/ai-frontend#48](https://github.com/cytonic-network/ai-frontend/pull/48)
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| // Browse mode | ||
| // Step 3: Fetch and select repository | ||
| console.log(chalk.dim(' Fetching your repositories...')); | ||
| let repos: GitHubRepo[]; | ||
| try { | ||
| repos = await fetchReposViaCli(); | ||
| } catch (err) { | ||
| console.log(chalk.red(' Failed to fetch repositories. Check your authentication.')); | ||
| console.log(chalk.dim(` Error: ${err instanceof Error ? err.message : String(err)}`)); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Browse mode unconditionally shells out to gh CLI, but ensureCredentials can succeed with a plain token.
ensureCredentials can succeed via a plain token (env var or ~/.ralph-starter/sources.json) when gh CLI is not installed or not authenticated. However, fetchReposViaCli() unconditionally shells out to gh repo list, so browse mode will always fail for token-only users with a confusing error message ("Check your authentication") even though their credentials are valid.
Consider either:
- Disabling the "Browse" option in
askBrowseOrUrlwhenghCLI is unavailable (only offering URL mode). - Implementing a REST API fallback using the token for fetching repos/issues.
As a quick guard, you could check isGhCliAvailable() before offering browse mode:
const ghAvailable = await isGhCliAvailable();
const mode = ghAvailable ? await askBrowseOrUrl('GitHub') : 'url';
if (!ghAvailable) {
console.log(chalk.dim(' (gh CLI not available — using URL mode)'));
}Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/github.ts
Line: 179-189
Comment:
Browse mode unconditionally shells out to `gh` CLI, but `ensureCredentials` can succeed with a plain token.
`ensureCredentials` can succeed via a plain token (env var or `~/.ralph-starter/sources.json`) when `gh` CLI is not installed or not authenticated. However, `fetchReposViaCli()` unconditionally shells out to `gh repo list`, so browse mode will always fail for token-only users with a confusing error message ("Check your authentication") even though their credentials are valid.
Consider either:
1. Disabling the "Browse" option in `askBrowseOrUrl` when `gh` CLI is unavailable (only offering URL mode).
2. Implementing a REST API fallback using the token for fetching repos/issues.
As a quick guard, you could check `isGhCliAvailable()` before offering browse mode:
```ts
const ghAvailable = await isGhCliAvailable();
const mode = ghAvailable ? await askBrowseOrUrl('GitHub') : 'url';
if (!ghAvailable) {
console.log(chalk.dim(' (gh CLI not available — using URL mode)'));
}
```
How can I resolve this? If you propose a fix, please make it concise.| } from '../utils/sanitize.js'; | ||
| import { ensureSharp } from '../utils/sharp.js'; | ||
| import { showWelcome } from '../wizard/ui.js'; | ||
| import { githubCommand } from './github.js'; |
There was a problem hiding this comment.
Circular dependency between run.ts and github.ts.
run.ts imports githubCommand from github.ts (line 37), while github.ts imports runCommand from run.ts (line 14). This creates a circular module dependency:
run.ts → github.ts → run.ts
In Node.js ESM this typically resolves at runtime because the imports are function references that are only evaluated when called (not at import time). However, circular dependencies are a code smell, can confuse bundlers/tree-shakers, and make the dependency graph harder to reason about.
A cleaner approach would be to introduce a thin shared types/options file (e.g., src/commands/types.ts) that both files can import from, breaking the cycle. Alternatively, the wizard fallback logic in run.ts could be moved to a separate orchestration layer that imports both commands without creating a cycle.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/run.ts
Line: 37
Comment:
Circular dependency between `run.ts` and `github.ts`.
`run.ts` imports `githubCommand` from `github.ts` (line 37), while `github.ts` imports `runCommand` from `run.ts` (line 14). This creates a circular module dependency:
```
run.ts → github.ts → run.ts
```
In Node.js ESM this typically resolves at runtime because the imports are function references that are only evaluated when called (not at import time). However, circular dependencies are a code smell, can confuse bundlers/tree-shakers, and make the dependency graph harder to reason about.
A cleaner approach would be to introduce a thin shared types/options file (e.g., `src/commands/types.ts`) that both files can import from, breaking the cycle. Alternatively, the wizard fallback logic in `run.ts` could be moved to a separate orchestration layer that imports both commands without creating a cycle.
How can I resolve this? If you propose a fix, please make it concise.| | `ralph-starter github` | Interactive GitHub issues wizard | | ||
| | `ralph-starter linear` | Interactive Linear issues wizard | | ||
| | `ralph-starter notion` | Interactive Notion pages wizard | |
There was a problem hiding this comment.
Documenting unimplemented commands in the README.
The ralph-starter linear and ralph-starter notion commands are listed in the commands table but are not implemented in this PR — neither the command handlers nor CLI registrations exist yet. Publishing documentation for commands that don't work yet will confuse users who try running them.
Consider either removing these rows until the commands are implemented, or annotating them as "coming soon":
| | `ralph-starter github` | Interactive GitHub issues wizard | | |
| | `ralph-starter linear` | Interactive Linear issues wizard | | |
| | `ralph-starter notion` | Interactive Notion pages wizard | | |
| | `ralph-starter linear` | Interactive Linear issues wizard _(coming soon)_ | | |
| | `ralph-starter notion` | Interactive Notion pages wizard _(coming soon)_ | |
Prompt To Fix With AI
This is a comment left during a code review.
Path: README.md
Line: 459-461
Comment:
Documenting unimplemented commands in the README.
The `ralph-starter linear` and `ralph-starter notion` commands are listed in the commands table but are not implemented in this PR — neither the command handlers nor CLI registrations exist yet. Publishing documentation for commands that don't work yet will confuse users who try running them.
Consider either removing these rows until the commands are implemented, or annotating them as "coming soon":
```suggestion
| `ralph-starter linear` | Interactive Linear issues wizard _(coming soon)_ |
| `ralph-starter notion` | Interactive Notion pages wizard _(coming soon)_ |
```
How can I resolve this? If you propose a fix, please make it concise.- Anchor GitHub URL regex patterns with ^https?:// (CodeQL fix) - Change interface to type for plain data structures (Greptile) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
ralph-starter githubcommand — interactive wizard to browse repos, filter by label, multi-select issues, and start buildingsrc/integrations/wizards/shared.ts— shared utilities (credential prompting, browse-or-URL choice) reused by upcoming Linear and Notion wizardsralph-starter run --from github(without--project/--issue) now redirects to the wizard instead of erroringralph-starter figmaFeatures
ghCLI auth or prompts for token (saves to~/.ralph-starter/sources.json)github.comURL (auto-detects repo + issue number)--commit,--push,--pr,--validate,--agent,--max-iterationsTest plan
ralph-starter githublaunches wizardralph-starter run --from github(no flags) redirects to wizardralph-starter run --from github --project owner/repo --issue 1still works (no regression)pnpm buildcompiles (pre-existing type errors in visual-validation.ts are unrelated)🤖 Generated with Claude Code