@rotorsoft/gent 1.19.0 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{chunk-JHIW57FY.js → chunk-O6JNGCPQ.js} +6 -1
- package/dist/chunk-O6JNGCPQ.js.map +1 -0
- package/dist/index.js +100 -54
- package/dist/index.js.map +1 -1
- package/dist/{setup-labels-LB7X6A3K.js → setup-labels-MIGOHCYM.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-JHIW57FY.js.map +0 -1
- /package/dist/{setup-labels-LB7X6A3K.js.map → setup-labels-MIGOHCYM.js.map} +0 -0
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
AI-powered GitHub workflow tool - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs from an interactive dashboard.
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
https://github.com/user-attachments/assets/c18fe01a-9695-4e8a-bf27-bb54f27247ef
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
## Overview
|
|
6
10
|
|
|
7
11
|
`gent` provides an interactive dashboard that integrates AI with GitHub to automate your development workflow. Just run `gent` to launch:
|
|
@@ -56,6 +56,9 @@ var colors = {
|
|
|
56
56
|
|
|
57
57
|
// src/utils/spinner.ts
|
|
58
58
|
import ora from "ora";
|
|
59
|
+
function aiSpinnerText(provider, action) {
|
|
60
|
+
return `Waiting for ${provider} to ${action}...`;
|
|
61
|
+
}
|
|
59
62
|
function createSpinner(text) {
|
|
60
63
|
return ora({
|
|
61
64
|
text,
|
|
@@ -896,6 +899,8 @@ export {
|
|
|
896
899
|
loadAgentInstructions,
|
|
897
900
|
configExists,
|
|
898
901
|
generateDefaultConfig,
|
|
902
|
+
aiSpinnerText,
|
|
903
|
+
createSpinner,
|
|
899
904
|
withSpinner,
|
|
900
905
|
getWorkflowLabels,
|
|
901
906
|
buildIssueLabels,
|
|
@@ -917,4 +922,4 @@ export {
|
|
|
917
922
|
listOpenPrs,
|
|
918
923
|
setupLabelsCommand
|
|
919
924
|
};
|
|
920
|
-
//# sourceMappingURL=chunk-
|
|
925
|
+
//# sourceMappingURL=chunk-O6JNGCPQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/utils/spinner.ts","../src/lib/config.ts","../src/types/index.ts","../src/lib/labels.ts","../src/lib/github.ts","../src/utils/validators.ts","../src/commands/setup-labels.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nexport const logger = {\n info: (message: string) => console.log(chalk.blue(\"ℹ\"), message),\n success: (message: string) => console.log(chalk.green(\"✓\"), message),\n warning: (message: string) => console.log(chalk.yellow(\"⚠\"), message),\n error: (message: string) => console.log(chalk.red(\"✗\"), message),\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray(\"⋯\"), message);\n }\n },\n dim: (message: string) => console.log(chalk.dim(message)),\n bold: (message: string) => console.log(chalk.bold(message)),\n highlight: (message: string) => console.log(chalk.cyan(message)),\n\n box: (title: string, content: string) => {\n const lines = content.split(\"\\n\");\n // Calculate visible length (strips ANSI codes) for proper alignment\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n const visibleLength = (str: string) => stripAnsi(str).length;\n const maxLen =\n Math.max(title.length, ...lines.map((l) => visibleLength(l))) + 4;\n const border = \"─\".repeat(maxLen);\n\n // Pad string to target length accounting for ANSI codes\n const padVisible = (str: string, len: number) => {\n const visible = visibleLength(str);\n return str + \" \".repeat(Math.max(0, len - visible));\n };\n\n console.log(chalk.dim(`┌${border}┐`));\n console.log(\n `${chalk.dim(\"│\")} ${chalk.bold(title.padEnd(maxLen - 2))} ${chalk.dim(\"│\")}`\n );\n console.log(chalk.dim(`├${border}┤`));\n for (const line of lines) {\n console.log(\n `${chalk.dim(\"│\")} ${padVisible(line, maxLen - 2)} ${chalk.dim(\"│\")}`\n );\n }\n console.log(chalk.dim(`└${border}┘`));\n },\n\n list: (items: string[], bullet = \"•\") => {\n for (const item of items) {\n console.log(chalk.dim(bullet), item);\n }\n },\n\n newline: () => console.log(),\n};\n\nexport const colors = {\n issue: chalk.cyan,\n branch: chalk.magenta,\n label: chalk.yellow,\n file: chalk.green,\n command: chalk.blue,\n url: chalk.underline.blue,\n provider: chalk.cyan.bold,\n};\n","import ora, { Ora } from \"ora\";\n\nexport function aiSpinnerText(provider: string, action: string): string {\n return `Waiting for ${provider} to ${action}...`;\n}\n\nexport function createSpinner(text: string): Ora {\n return ora({\n text,\n spinner: \"dots\",\n });\n}\n\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>\n): Promise<T> {\n const spinner = createSpinner(text);\n spinner.start();\n\n try {\n const result = await fn();\n spinner.succeed();\n return result;\n } catch (error) {\n spinner.fail();\n throw error;\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { GentConfig, AIProvider } from \"../types/index.js\";\n\n// Module-level variable to hold runtime provider override (e.g. from TUI)\nlet runtimeProvider: AIProvider | null = null;\n\nexport function setRuntimeProvider(provider: AIProvider): void {\n runtimeProvider = provider;\n}\n\n/**\n * Helper to resolve the active provider based on precedence:\n * 1. CLI options (explicit flag)\n * 2. Runtime override (in-memory state)\n * 3. Environment variable (GENT_AI_PROVIDER)\n * 4. Configuration (file)\n * 5. Default\n */\nexport function resolveProvider(\n options: { provider?: AIProvider } | undefined,\n config: GentConfig\n): AIProvider {\n return options?.provider ?? config.ai.provider;\n}\n\nconst DEFAULT_CONFIG: GentConfig = {\n version: 1,\n github: {\n labels: {\n workflow: {\n ready: \"ai-ready\",\n in_progress: \"ai-in-progress\",\n completed: \"ai-completed\",\n blocked: \"ai-blocked\",\n },\n types: [\"feature\", \"fix\", \"refactor\", \"chore\", \"docs\", \"test\"],\n priorities: [\"critical\", \"high\", \"medium\", \"low\"],\n risks: [\"low\", \"medium\", \"high\"],\n areas: [\"ui\", \"api\", \"database\", \"workers\", \"shared\", \"testing\", \"infra\"],\n },\n },\n branch: {\n pattern: \"{author}/{type}-{issue}-{slug}\",\n author_source: \"git\",\n author_env_var: \"GENT_AUTHOR\",\n },\n progress: {\n file: \"progress.txt\",\n archive_threshold: 500,\n archive_dir: \".gent/archive\",\n },\n claude: {\n permission_mode: \"acceptEdits\",\n agent_file: \"AGENT.md\",\n },\n gemini: {\n sandbox_mode: \"on\",\n agent_file: \"AGENT.md\",\n },\n codex: {\n agent_file: \"AGENT.md\",\n },\n ai: {\n provider: \"claude\",\n auto_fallback: true,\n },\n video: {\n enabled: true,\n max_duration: 30,\n width: 1280,\n height: 720,\n },\n validation: [\"npm run typecheck\", \"npm run lint\", \"npm run test\"],\n};\n\nexport function getConfigPath(cwd: string = process.cwd()): string {\n return join(cwd, \".gent.yml\");\n}\n\nexport function getAgentPath(cwd: string = process.cwd()): string | null {\n const config = loadConfig(cwd);\n // Use claude.agent_file for backward compatibility\n const agentPath = join(cwd, config.claude.agent_file);\n return existsSync(agentPath) ? agentPath : null;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): GentConfig {\n const configPath = getConfigPath(cwd);\n\n if (!existsSync(configPath)) {\n return DEFAULT_CONFIG;\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const userConfig = parseYaml(content) as Partial<GentConfig>;\n\n return mergeConfig(DEFAULT_CONFIG, userConfig);\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport function loadAgentInstructions(\n cwd: string = process.cwd()\n): string | null {\n const agentPath = getAgentPath(cwd);\n\n if (!agentPath) {\n return null;\n }\n\n try {\n return readFileSync(agentPath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport function configExists(cwd: string = process.cwd()): boolean {\n return existsSync(getConfigPath(cwd));\n}\n\nfunction mergeConfig(\n defaults: GentConfig,\n user: Partial<GentConfig>\n): GentConfig {\n // Support GENT_AI_PROVIDER environment variable override\n const envProvider = process.env.GENT_AI_PROVIDER as\n | \"claude\"\n | \"gemini\"\n | \"codex\"\n | undefined;\n\n // Runtime override takes precedence over env var\n const effectiveProvider = runtimeProvider ?? envProvider;\n\n return {\n version: user.version ?? defaults.version,\n github: {\n labels: {\n workflow: {\n ...defaults.github.labels.workflow,\n ...user.github?.labels?.workflow,\n },\n types: user.github?.labels?.types ?? defaults.github.labels.types,\n priorities:\n user.github?.labels?.priorities ?? defaults.github.labels.priorities,\n risks: user.github?.labels?.risks ?? defaults.github.labels.risks,\n areas: user.github?.labels?.areas ?? defaults.github.labels.areas,\n },\n },\n branch: {\n ...defaults.branch,\n ...user.branch,\n },\n progress: {\n ...defaults.progress,\n ...user.progress,\n },\n claude: {\n ...defaults.claude,\n ...user.claude,\n },\n gemini: {\n ...defaults.gemini,\n ...user.gemini,\n },\n codex: {\n ...defaults.codex,\n ...user.codex,\n },\n ai: {\n ...defaults.ai,\n ...user.ai,\n // Runtime/Env takes precedence\n ...(effectiveProvider && { provider: effectiveProvider }),\n },\n video: {\n ...defaults.video,\n ...user.video,\n },\n validation: user.validation ?? defaults.validation,\n };\n}\n\nexport function updateConfigProvider(\n provider: AIProvider,\n cwd: string = process.cwd()\n): void {\n const configPath = getConfigPath(cwd);\n if (!existsSync(configPath)) {\n writeFileSync(configPath, generateDefaultConfig(provider), \"utf-8\");\n return;\n }\n const content = readFileSync(configPath, \"utf-8\");\n const updated = content.replace(\n /^(\\s*provider:\\s*)\"[^\"]*\"/m,\n `$1\"${provider}\"`\n );\n writeFileSync(configPath, updated, \"utf-8\");\n}\n\nexport function generateDefaultConfig(provider: AIProvider = \"claude\"): string {\n return `# Gent Configuration\n# See https://github.com/rotorsoft/gent for documentation\nversion: 1\n\n# GitHub settings\ngithub:\n labels:\n workflow:\n ready: \"ai-ready\"\n in_progress: \"ai-in-progress\"\n completed: \"ai-completed\"\n blocked: \"ai-blocked\"\n types:\n - feature\n - fix\n - refactor\n - chore\n - docs\n - test\n priorities:\n - critical\n - high\n - medium\n - low\n risks:\n - low\n - medium\n - high\n areas:\n - ui\n - api\n - database\n - workers\n - shared\n - testing\n - infra\n\n# Branch naming convention\nbranch:\n pattern: \"{author}/{type}-{issue}-{slug}\"\n author_source: \"git\" # git | env | prompt\n author_env_var: \"GENT_AUTHOR\"\n\n# Progress tracking\nprogress:\n file: \"progress.txt\"\n archive_threshold: 500\n archive_dir: \".gent/archive\"\n\n# Claude settings\nclaude:\n permission_mode: \"acceptEdits\"\n agent_file: \"AGENT.md\"\n\n# Gemini settings\ngemini:\n sandbox_mode: \"on\"\n agent_file: \"AGENT.md\"\n\n# Codex settings\ncodex:\n agent_file: \"AGENT.md\"\n\n# AI provider settings\nai:\n provider: \"${provider}\" # claude | gemini | codex\n # fallback_provider: \"gemini\" # optional fallback when rate limited\n auto_fallback: true # automatically switch to fallback on rate limit\n\n# Video capture for UI changes (requires Playwright)\nvideo:\n enabled: true # set to false to disable video capture for PRs with UI changes\n max_duration: 30 # maximum video duration in seconds\n width: 1280 # video width\n height: 720 # video height\n\n# Validation commands (run before commit)\nvalidation:\n - \"npm run typecheck\"\n - \"npm run lint\"\n - \"npm run test\"\n`;\n}\n","export type AIProvider = \"claude\" | \"gemini\" | \"codex\";\n\nexport interface GentConfig {\n version: number;\n github: GitHubConfig;\n branch: BranchConfig;\n progress: ProgressConfig;\n claude: ClaudeConfig;\n gemini: GeminiConfig;\n codex: CodexConfig;\n ai: AIConfig;\n video: VideoConfig;\n validation: string[];\n}\n\nexport interface VideoConfig {\n enabled: boolean;\n max_duration: number; // seconds\n width: number;\n height: number;\n}\n\nexport interface AIConfig {\n provider: AIProvider;\n fallback_provider?: AIProvider;\n auto_fallback: boolean;\n}\n\nexport interface GitHubConfig {\n labels: {\n workflow: WorkflowLabels;\n types: string[];\n priorities: string[];\n risks: string[];\n areas: string[];\n };\n}\n\nexport interface WorkflowLabels {\n ready: string;\n in_progress: string;\n completed: string;\n blocked: string;\n}\n\nexport interface BranchConfig {\n pattern: string;\n author_source: \"git\" | \"env\" | \"prompt\";\n author_env_var: string;\n}\n\nexport interface ProgressConfig {\n file: string;\n archive_threshold: number;\n archive_dir: string;\n}\n\nexport interface ClaudeConfig {\n permission_mode: string;\n agent_file: string;\n}\n\nexport interface GeminiConfig {\n sandbox_mode: string;\n agent_file: string;\n}\n\nexport interface CodexConfig {\n agent_file: string;\n}\n\nexport interface GitHubIssue {\n number: number;\n title: string;\n body: string;\n labels: string[];\n state: \"open\" | \"closed\";\n assignee?: string;\n url: string;\n}\n\nexport interface GitHubLabel {\n name: string;\n color: string;\n description?: string;\n}\n\nexport interface GitHubReviewComment {\n id?: number;\n author: string;\n body: string;\n path?: string;\n line?: number | null;\n createdAt?: string;\n}\n\nexport interface GitHubReview {\n author: string;\n body: string;\n state: string;\n submittedAt?: string;\n}\n\nexport interface GitHubReviewThread {\n isResolved?: boolean | null;\n isOutdated?: boolean;\n path?: string;\n line?: number | null;\n comments: GitHubReviewComment[];\n}\n\nexport interface GitHubPRComment {\n id?: string;\n author: string;\n body: string;\n createdAt?: string;\n}\n\nexport interface GitHubReviewData {\n reviews: GitHubReview[];\n reviewThreads: GitHubReviewThread[];\n comments: GitHubPRComment[];\n}\n\nexport interface ProgressEntry {\n date: string;\n type: string;\n description: string;\n issue?: number;\n decisions: string[];\n files: string[];\n tests: string[];\n concerns: string[];\n followUp: string[];\n commit?: string;\n}\n\nexport interface BranchInfo {\n name: string;\n author: string;\n type: string;\n issueNumber: number;\n slug: string;\n}\n\nexport const DEFAULT_LABELS: Record<string, GitHubLabel[]> = {\n workflow: [\n {\n name: \"ai-ready\",\n color: \"0E8A16\",\n description: \"Issue ready for AI implementation\",\n },\n {\n name: \"ai-in-progress\",\n color: \"FFA500\",\n description: \"AI currently working on this\",\n },\n {\n name: \"ai-completed\",\n color: \"1D76DB\",\n description: \"AI done, needs human review\",\n },\n {\n name: \"ai-blocked\",\n color: \"D93F0B\",\n description: \"AI couldn't complete, needs help\",\n },\n ],\n priority: [\n {\n name: \"priority:critical\",\n color: \"B60205\",\n description: \"Blocking production\",\n },\n {\n name: \"priority:high\",\n color: \"D93F0B\",\n description: \"Important features/bugs\",\n },\n {\n name: \"priority:medium\",\n color: \"FBCA04\",\n description: \"Nice-to-have improvements\",\n },\n { name: \"priority:low\", color: \"0E8A16\", description: \"Minor tweaks\" },\n ],\n risk: [\n {\n name: \"risk:low\",\n color: \"C2E0C6\",\n description: \"UI changes, tests, non-critical\",\n },\n {\n name: \"risk:medium\",\n color: \"FEF2C0\",\n description: \"API changes, new features\",\n },\n {\n name: \"risk:high\",\n color: \"F9D0C4\",\n description: \"Migrations, auth, security\",\n },\n ],\n type: [\n { name: \"type:feature\", color: \"1D76DB\", description: \"New feature\" },\n { name: \"type:fix\", color: \"D73A4A\", description: \"Bug fix\" },\n {\n name: \"type:refactor\",\n color: \"5319E7\",\n description: \"Code improvement\",\n },\n { name: \"type:chore\", color: \"FEF2C0\", description: \"Maintenance\" },\n { name: \"type:docs\", color: \"0075CA\", description: \"Documentation\" },\n { name: \"type:test\", color: \"D4C5F9\", description: \"Testing\" },\n ],\n area: [\n { name: \"area:ui\", color: \"C5DEF5\", description: \"User interface\" },\n { name: \"area:api\", color: \"D4C5F9\", description: \"API/Backend\" },\n { name: \"area:database\", color: \"FEF2C0\", description: \"Database/Models\" },\n {\n name: \"area:workers\",\n color: \"F9D0C4\",\n description: \"Background workers\",\n },\n { name: \"area:shared\", color: \"C2E0C6\", description: \"Shared libraries\" },\n {\n name: \"area:testing\",\n color: \"E99695\",\n description: \"Test infrastructure\",\n },\n {\n name: \"area:infra\",\n color: \"BFD4F2\",\n description: \"Infrastructure/DevOps\",\n },\n ],\n};\n","import type { GentConfig, GitHubLabel } from \"../types/index.js\";\nimport { DEFAULT_LABELS } from \"../types/index.js\";\n\nexport function getAllLabels(config: GentConfig): GitHubLabel[] {\n const labels: GitHubLabel[] = [];\n\n // Workflow labels\n labels.push(...DEFAULT_LABELS.workflow);\n\n // Priority labels\n for (const priority of config.github.labels.priorities) {\n const defaultLabel = DEFAULT_LABELS.priority.find(\n (l) => l.name === `priority:${priority}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `priority:${priority}`,\n color: \"FBCA04\",\n description: `Priority: ${priority}`,\n });\n }\n }\n\n // Risk labels\n for (const risk of config.github.labels.risks) {\n const defaultLabel = DEFAULT_LABELS.risk.find(\n (l) => l.name === `risk:${risk}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `risk:${risk}`,\n color: \"FEF2C0\",\n description: `Risk: ${risk}`,\n });\n }\n }\n\n // Type labels\n for (const type of config.github.labels.types) {\n const defaultLabel = DEFAULT_LABELS.type.find(\n (l) => l.name === `type:${type}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `type:${type}`,\n color: \"1D76DB\",\n description: `Type: ${type}`,\n });\n }\n }\n\n // Area labels\n for (const area of config.github.labels.areas) {\n const defaultLabel = DEFAULT_LABELS.area.find(\n (l) => l.name === `area:${area}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `area:${area}`,\n color: \"C5DEF5\",\n description: `Area: ${area}`,\n });\n }\n }\n\n return labels;\n}\n\nexport function getWorkflowLabels(config: GentConfig): {\n ready: string;\n inProgress: string;\n completed: string;\n blocked: string;\n} {\n return {\n ready: config.github.labels.workflow.ready,\n inProgress: config.github.labels.workflow.in_progress,\n completed: config.github.labels.workflow.completed,\n blocked: config.github.labels.workflow.blocked,\n };\n}\n\nexport function buildIssueLabels(meta: {\n type: string;\n priority: string;\n risk: string;\n area: string;\n}): string[] {\n return [\n \"ai-ready\",\n `type:${meta.type}`,\n `priority:${meta.priority}`,\n `risk:${meta.risk}`,\n `area:${meta.area}`,\n ];\n}\n\nexport function extractTypeFromLabels(labels: string[]): string {\n for (const label of labels) {\n if (label.startsWith(\"type:\")) {\n return label.replace(\"type:\", \"\");\n }\n }\n return \"feature\";\n}\n\nexport function extractPriorityFromLabels(labels: string[]): string {\n for (const label of labels) {\n if (label.startsWith(\"priority:\")) {\n return label.replace(\"priority:\", \"\");\n }\n }\n return \"medium\";\n}\n\nexport function hasWorkflowLabel(\n labels: string[],\n workflowLabel: string\n): boolean {\n return labels.includes(workflowLabel);\n}\n\nexport function sortByPriority(issues: { labels: string[] }[]): void {\n const priorityOrder = [\"critical\", \"high\", \"medium\", \"low\"];\n\n issues.sort((a, b) => {\n const aPriority = extractPriorityFromLabels(a.labels);\n const bPriority = extractPriorityFromLabels(b.labels);\n return priorityOrder.indexOf(aPriority) - priorityOrder.indexOf(bPriority);\n });\n}\n","import { execa } from \"execa\";\nimport type {\n GitHubIssue,\n GitHubLabel,\n GitHubReviewData,\n} from \"../types/index.js\";\n\nexport async function getIssue(issueNumber: number): Promise<GitHubIssue> {\n const { stdout } = await execa(\"gh\", [\n \"issue\",\n \"view\",\n String(issueNumber),\n \"--json\",\n \"number,title,body,labels,state,assignees,url\",\n ]);\n\n const data = JSON.parse(stdout);\n return {\n number: data.number,\n title: data.title,\n body: data.body || \"\",\n labels: data.labels.map((l: { name: string }) => l.name),\n state: data.state.toLowerCase(),\n assignee: data.assignees?.[0]?.login,\n url: data.url,\n };\n}\n\nexport async function listIssues(options: {\n labels?: string[];\n state?: \"open\" | \"closed\" | \"all\";\n limit?: number;\n}): Promise<GitHubIssue[]> {\n const args = [\n \"issue\",\n \"list\",\n \"--json\",\n \"number,title,body,labels,state,url\",\n ];\n\n if (options.labels?.length) {\n args.push(\"--label\", options.labels.join(\",\"));\n }\n\n if (options.state) {\n args.push(\"--state\", options.state);\n }\n\n args.push(\"--limit\", String(options.limit || 50));\n\n const { stdout } = await execa(\"gh\", args);\n const data = JSON.parse(stdout);\n\n return data.map(\n (d: {\n number: number;\n title: string;\n body: string;\n labels: { name: string }[];\n state: string;\n url: string;\n }) => ({\n number: d.number,\n title: d.title,\n body: d.body || \"\",\n labels: d.labels.map((l) => l.name),\n state: d.state.toLowerCase() as \"open\" | \"closed\",\n url: d.url,\n })\n );\n}\n\nexport async function createIssue(options: {\n title: string;\n body: string;\n labels?: string[];\n}): Promise<number> {\n const args = [\n \"issue\",\n \"create\",\n \"--title\",\n options.title,\n \"--body\",\n options.body,\n ];\n\n if (options.labels?.length) {\n args.push(\"--label\", options.labels.join(\",\"));\n }\n\n const { stdout } = await execa(\"gh\", args);\n\n // Extract issue number from URL\n const match = stdout.match(/\\/issues\\/(\\d+)/);\n if (!match) {\n throw new Error(\"Failed to extract issue number from gh output\");\n }\n\n return parseInt(match[1], 10);\n}\n\nexport async function updateIssueLabels(\n issueNumber: number,\n options: {\n add?: string[];\n remove?: string[];\n }\n): Promise<void> {\n const promises: Promise<unknown>[] = [];\n\n if (options.add?.length) {\n promises.push(\n execa(\"gh\", [\n \"issue\",\n \"edit\",\n String(issueNumber),\n \"--add-label\",\n options.add.join(\",\"),\n ])\n );\n }\n\n if (options.remove?.length) {\n promises.push(\n execa(\"gh\", [\n \"issue\",\n \"edit\",\n String(issueNumber),\n \"--remove-label\",\n options.remove.join(\",\"),\n ])\n );\n }\n\n await Promise.all(promises);\n}\n\nexport async function addIssueComment(\n issueNumber: number,\n body: string\n): Promise<void> {\n await execa(\"gh\", [\"issue\", \"comment\", String(issueNumber), \"--body\", body]);\n}\n\nexport async function assignIssue(\n issueNumber: number,\n assignee: string\n): Promise<void> {\n await execa(\"gh\", [\n \"issue\",\n \"edit\",\n String(issueNumber),\n \"--add-assignee\",\n assignee,\n ]);\n}\n\nexport async function createLabel(label: GitHubLabel): Promise<void> {\n try {\n await execa(\"gh\", [\n \"label\",\n \"create\",\n label.name,\n \"--color\",\n label.color,\n \"--description\",\n label.description || \"\",\n \"--force\",\n ]);\n } catch {\n // Label might already exist, ignore error\n }\n}\n\nexport async function createPullRequest(options: {\n title: string;\n body: string;\n base?: string;\n draft?: boolean;\n}): Promise<string> {\n const args = [\n \"pr\",\n \"create\",\n \"--title\",\n options.title,\n \"--body\",\n options.body,\n \"--assignee\",\n \"@me\",\n ];\n\n if (options.base) {\n args.push(\"--base\", options.base);\n }\n\n if (options.draft) {\n args.push(\"--draft\");\n }\n\n const { stdout } = await execa(\"gh\", args);\n return stdout.trim();\n}\n\nexport interface PrBasicInfo {\n number: number;\n url: string;\n}\n\nexport interface PrStatusInfo {\n number: number;\n title: string;\n url: string;\n state: \"open\" | \"closed\" | \"merged\";\n reviewDecision: string | null;\n isDraft: boolean;\n}\n\nexport async function getPrForBranch(): Promise<PrBasicInfo | null> {\n try {\n const { stdout } = await execa(\"gh\", [\n \"pr\",\n \"view\",\n \"--json\",\n \"number,url\",\n ]);\n const data = JSON.parse(stdout);\n return { number: data.number, url: data.url };\n } catch {\n return null;\n }\n}\n\nexport async function getPrStatus(): Promise<PrStatusInfo | null> {\n try {\n const { stdout } = await execa(\"gh\", [\n \"pr\",\n \"view\",\n \"--json\",\n \"number,title,url,state,reviewDecision,isDraft\",\n ]);\n const data = JSON.parse(stdout);\n // gh pr view returns state as OPEN, CLOSED, or MERGED (uppercase)\n const state = (data.state?.toLowerCase() ?? \"open\") as\n | \"open\"\n | \"closed\"\n | \"merged\";\n return {\n number: data.number,\n title: data.title ?? \"\",\n url: data.url,\n state,\n reviewDecision: data.reviewDecision ?? null,\n isDraft: data.isDraft ?? false,\n };\n } catch {\n return null;\n }\n}\n\nexport async function getPrReviewData(\n prNumber?: number\n): Promise<GitHubReviewData> {\n // Fetch reviews and comments using gh pr view (both are supported JSON fields)\n const prArgs = [\"pr\", \"view\"];\n if (prNumber) {\n prArgs.push(String(prNumber));\n }\n prArgs.push(\"--json\", \"reviews,comments\");\n\n const { stdout: prStdout } = await execa(\"gh\", prArgs);\n const prData = JSON.parse(prStdout);\n\n // Fetch review threads using GraphQL API (not available via gh pr view --json)\n // First get repo owner and name since GraphQL doesn't support {owner}/{repo} placeholders\n let reviewThreads: Array<{\n isResolved?: boolean | null;\n isOutdated?: boolean;\n path?: string;\n line?: number | null;\n comments: Array<{\n author: string;\n body: string;\n path?: string;\n line?: number | null;\n createdAt?: string;\n }>;\n }> = [];\n\n try {\n const { stdout: repoStdout } = await execa(\"gh\", [\n \"repo\",\n \"view\",\n \"--json\",\n \"owner,name\",\n ]);\n const repoData = JSON.parse(repoStdout);\n const owner = repoData.owner?.login ?? repoData.owner;\n const repo = repoData.name;\n\n const graphqlQuery = `query { repository(owner: \"${owner}\", name: \"${repo}\") { pullRequest(number: ${prNumber}) { reviewThreads(first: 100) { nodes { isResolved isOutdated path line comments(first: 100) { nodes { databaseId author { login } body path line createdAt } } } } } } }`;\n\n const { stdout: graphqlStdout } = await execa(\"gh\", [\n \"api\",\n \"graphql\",\n \"-f\",\n `query=${graphqlQuery}`,\n ]);\n const graphqlData = JSON.parse(graphqlStdout);\n const prNode = graphqlData.data?.repository?.pullRequest;\n const threadNodes = prNode?.reviewThreads?.nodes ?? [];\n\n reviewThreads = threadNodes.map(\n (thread: {\n isResolved?: boolean | null;\n isOutdated?: boolean;\n path?: string;\n line?: number | null;\n comments?: {\n nodes?: Array<{\n databaseId?: number;\n author?: { login?: string };\n body?: string;\n path?: string;\n line?: number | null;\n createdAt?: string;\n }>;\n };\n }) => ({\n isResolved: thread.isResolved ?? null,\n isOutdated: thread.isOutdated ?? false,\n path: thread.path,\n line: thread.line ?? null,\n comments: (thread.comments?.nodes ?? []).map((comment) => ({\n id: comment.databaseId,\n author: comment.author?.login ?? \"unknown\",\n body: comment.body ?? \"\",\n path: comment.path ?? thread.path,\n line: comment.line ?? thread.line ?? null,\n createdAt: comment.createdAt,\n })),\n })\n );\n } catch {\n // If GraphQL fails (e.g., no permissions), continue with empty threads\n reviewThreads = [];\n }\n\n return {\n reviews: (prData.reviews ?? []).map(\n (review: {\n author?: { login?: string };\n body?: string;\n state?: string;\n submittedAt?: string;\n }) => ({\n author: review.author?.login ?? \"unknown\",\n body: review.body ?? \"\",\n state: review.state ?? \"UNKNOWN\",\n submittedAt: review.submittedAt,\n })\n ),\n reviewThreads,\n comments: (prData.comments ?? []).map(\n (comment: {\n id?: string;\n author?: { login?: string };\n body?: string;\n createdAt?: string;\n }) => ({\n id: comment.id,\n author: comment.author?.login ?? \"unknown\",\n body: comment.body ?? \"\",\n createdAt: comment.createdAt,\n })\n ),\n };\n}\n\nexport async function getCurrentUser(): Promise<string> {\n const { stdout } = await execa(\"gh\", [\"api\", \"user\", \"--jq\", \".login\"]);\n return stdout.trim();\n}\n\nexport async function replyToReviewComment(\n prNumber: number,\n commentId: number,\n body: string\n): Promise<void> {\n await execa(\"gh\", [\n \"api\",\n `repos/{owner}/{repo}/pulls/${prNumber}/comments/${commentId}/replies`,\n \"-f\",\n `body=${body}`,\n ]);\n}\n\nexport async function addPrComment(\n prNumber: number,\n body: string\n): Promise<void> {\n await execa(\"gh\", [\"pr\", \"comment\", String(prNumber), \"--body\", body]);\n}\n\nexport interface OpenPr {\n number: number;\n title: string;\n headRefName: string;\n url: string;\n}\n\nexport async function listOpenPrs(limit: number = 30): Promise<OpenPr[]> {\n const { stdout } = await execa(\"gh\", [\n \"pr\",\n \"list\",\n \"--state\",\n \"open\",\n \"--json\",\n \"number,title,headRefName,url\",\n \"--limit\",\n String(limit),\n ]);\n const data = JSON.parse(stdout);\n return data.map(\n (d: {\n number: number;\n title: string;\n headRefName: string;\n url: string;\n }) => ({\n number: d.number,\n title: d.title,\n headRefName: d.headRefName,\n url: d.url,\n })\n );\n}\n","import { execa } from \"execa\";\nimport type { GentConfig, AIProvider } from \"../types/index.js\";\n\nexport async function checkGhCli(): Promise<boolean> {\n try {\n await execa(\"gh\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkGhAuth(): Promise<boolean> {\n try {\n await execa(\"gh\", [\"auth\", \"status\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkClaudeCli(): Promise<boolean> {\n try {\n await execa(\"claude\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkGeminiCli(): Promise<boolean> {\n try {\n await execa(\"gemini\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkCodexCLI(): Promise<boolean> {\n try {\n await execa(\"codex\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function checkAIProvider(provider: AIProvider): Promise<boolean> {\n switch (provider) {\n case \"claude\":\n return checkClaudeCli();\n case \"gemini\":\n return checkGeminiCli();\n case \"codex\":\n return checkCodexCLI();\n }\n}\n\nexport async function checkGitRepo(): Promise<boolean> {\n try {\n await execa(\"git\", [\"rev-parse\", \"--git-dir\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function validatePrerequisites(config?: GentConfig): Promise<{\n valid: boolean;\n missing: string[];\n}> {\n const checks = [\n { name: \"gh CLI\", check: checkGhCli },\n { name: \"gh auth\", check: checkGhAuth },\n { name: \"git repository\", check: checkGitRepo },\n ];\n\n const getProviderName = (provider: AIProvider) => {\n switch (provider) {\n case \"claude\":\n return \"claude CLI\";\n case \"gemini\":\n return \"gemini CLI\";\n case \"codex\":\n return \"codex CLI\";\n }\n };\n\n // Add AI provider check based on config\n if (config) {\n const provider = config.ai.provider;\n checks.push({\n name: getProviderName(provider),\n check: () => checkAIProvider(provider),\n });\n\n // Also check fallback if configured\n if (config.ai.fallback_provider) {\n const fallback = config.ai.fallback_provider;\n checks.push({\n name: `${getProviderName(fallback)} (fallback)`,\n check: () => checkAIProvider(fallback),\n });\n }\n } else {\n // Default to checking claude for backward compatibility\n checks.push({ name: \"claude CLI\", check: checkClaudeCli });\n }\n\n const missing: string[] = [];\n\n for (const { name, check } of checks) {\n const passed = await check();\n if (!passed) {\n missing.push(name);\n }\n }\n\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\nexport function isValidIssueNumber(value: string): boolean {\n const num = parseInt(value, 10);\n return !isNaN(num) && num > 0;\n}\n\nexport function sanitizeSlug(title: string, maxLength = 40): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, maxLength);\n}\n","import { logger, colors } from \"../utils/logger.js\";\nimport { withSpinner } from \"../utils/spinner.js\";\nimport { loadConfig } from \"../lib/config.js\";\nimport { getAllLabels } from \"../lib/labels.js\";\nimport { createLabel } from \"../lib/github.js\";\nimport { checkGhAuth } from \"../utils/validators.js\";\n\nexport async function setupLabelsCommand(): Promise<void> {\n logger.bold(\"Setting up GitHub labels...\");\n logger.newline();\n\n // Check gh auth\n const isAuthed = await checkGhAuth();\n if (!isAuthed) {\n logger.error(\"Not authenticated with GitHub. Run 'gh auth login' first.\");\n process.exit(1);\n }\n\n const config = loadConfig();\n const labels = getAllLabels(config);\n\n logger.info(`Creating ${labels.length} labels...`);\n logger.newline();\n\n let created = 0;\n let failed = 0;\n\n for (const label of labels) {\n try {\n await withSpinner(`Creating ${colors.label(label.name)}`, async () => {\n await createLabel(label);\n });\n created++;\n } catch (error) {\n logger.error(`Failed to create ${label.name}: ${error}`);\n failed++;\n }\n }\n\n logger.newline();\n logger.success(`Created ${created} labels`);\n if (failed > 0) {\n logger.warning(`Failed to create ${failed} labels`);\n }\n\n logger.newline();\n logger.info(\"Labels are ready. You can now create AI-ready issues.\");\n}\n"],"mappings":"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","names":["execa","execa"]}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addIssueComment,
|
|
4
4
|
addPrComment,
|
|
5
|
+
aiSpinnerText,
|
|
5
6
|
assignIssue,
|
|
6
7
|
buildIssueLabels,
|
|
7
8
|
checkAIProvider,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
configExists,
|
|
14
15
|
createIssue,
|
|
15
16
|
createPullRequest,
|
|
17
|
+
createSpinner,
|
|
16
18
|
extractTypeFromLabels,
|
|
17
19
|
generateDefaultConfig,
|
|
18
20
|
getConfigPath,
|
|
@@ -36,7 +38,7 @@ import {
|
|
|
36
38
|
sortByPriority,
|
|
37
39
|
updateIssueLabels,
|
|
38
40
|
withSpinner
|
|
39
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-O6JNGCPQ.js";
|
|
40
42
|
|
|
41
43
|
// src/index.ts
|
|
42
44
|
import { Command } from "commander";
|
|
@@ -200,7 +202,7 @@ async function initCommand(options) {
|
|
|
200
202
|
}
|
|
201
203
|
]);
|
|
202
204
|
if (setupLabels) {
|
|
203
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-
|
|
205
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-MIGOHCYM.js");
|
|
204
206
|
await setupLabelsCommand2();
|
|
205
207
|
}
|
|
206
208
|
}
|
|
@@ -332,7 +334,12 @@ async function invokeClaudeInternal(options) {
|
|
|
332
334
|
stdio: ["inherit", "pipe", "pipe"]
|
|
333
335
|
});
|
|
334
336
|
let output = "";
|
|
337
|
+
let firstData = true;
|
|
335
338
|
child.stdout.on("data", (chunk) => {
|
|
339
|
+
if (firstData) {
|
|
340
|
+
firstData = false;
|
|
341
|
+
options.onFirstData?.();
|
|
342
|
+
}
|
|
336
343
|
const text = chunk.toString();
|
|
337
344
|
output += text;
|
|
338
345
|
process.stdout.write(text);
|
|
@@ -371,7 +378,12 @@ async function invokeGeminiInternal(options) {
|
|
|
371
378
|
stdio: ["inherit", "pipe", "pipe"]
|
|
372
379
|
});
|
|
373
380
|
let output = "";
|
|
381
|
+
let firstData = true;
|
|
374
382
|
child.stdout.on("data", (chunk) => {
|
|
383
|
+
if (firstData) {
|
|
384
|
+
firstData = false;
|
|
385
|
+
options.onFirstData?.();
|
|
386
|
+
}
|
|
375
387
|
const text = chunk.toString();
|
|
376
388
|
output += text;
|
|
377
389
|
process.stdout.write(text);
|
|
@@ -409,7 +421,12 @@ async function invokeCodexInternal(options) {
|
|
|
409
421
|
stdio: ["inherit", "pipe", "pipe"]
|
|
410
422
|
});
|
|
411
423
|
let output = "";
|
|
424
|
+
let firstData = true;
|
|
412
425
|
child.stdout.on("data", (chunk) => {
|
|
426
|
+
if (firstData) {
|
|
427
|
+
firstData = false;
|
|
428
|
+
options.onFirstData?.();
|
|
429
|
+
}
|
|
413
430
|
const text = chunk.toString();
|
|
414
431
|
output += text;
|
|
415
432
|
process.stdout.write(text);
|
|
@@ -628,8 +645,6 @@ function generateFallbackTitle(description) {
|
|
|
628
645
|
|
|
629
646
|
// src/commands/create.ts
|
|
630
647
|
async function createCommand(description, options) {
|
|
631
|
-
logger.bold("Creating AI-enhanced ticket...");
|
|
632
|
-
logger.newline();
|
|
633
648
|
const config = loadConfig();
|
|
634
649
|
let currentProvider = resolveProvider(options, config);
|
|
635
650
|
const [ghAuth, aiOk] = await Promise.all([
|
|
@@ -650,33 +665,24 @@ async function createCommand(description, options) {
|
|
|
650
665
|
let aiOutput;
|
|
651
666
|
let additionalHints = null;
|
|
652
667
|
while (true) {
|
|
653
|
-
const providerName = getProviderDisplayName(currentProvider);
|
|
654
668
|
const prompt = buildTicketPrompt(
|
|
655
669
|
description,
|
|
656
670
|
agentInstructions,
|
|
657
671
|
additionalHints
|
|
658
672
|
);
|
|
659
673
|
try {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
`\u250C\u2500 Generating ticket with ${providerName}... \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`
|
|
663
|
-
)
|
|
664
|
-
);
|
|
665
|
-
logger.newline();
|
|
674
|
+
const spinner = createSpinner(aiSpinnerText(getProviderDisplayName(currentProvider), "generate ticket"));
|
|
675
|
+
spinner.start();
|
|
666
676
|
const result = await invokeAI(
|
|
667
|
-
{ prompt, streamOutput: true },
|
|
677
|
+
{ prompt, streamOutput: true, onFirstData: () => spinner.stop() },
|
|
668
678
|
config,
|
|
669
679
|
currentProvider
|
|
670
680
|
);
|
|
681
|
+
spinner.stop();
|
|
671
682
|
aiOutput = result.output;
|
|
672
683
|
logger.newline();
|
|
673
|
-
console.log(
|
|
674
|
-
chalk.dim(
|
|
675
|
-
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
676
|
-
)
|
|
677
|
-
);
|
|
678
|
-
logger.newline();
|
|
679
684
|
} catch (error) {
|
|
685
|
+
const providerName = getProviderDisplayName(currentProvider);
|
|
680
686
|
if (error && typeof error === "object" && "rateLimited" in error) {
|
|
681
687
|
logger.warning(`${providerName} is rate limited.`);
|
|
682
688
|
const others = await getOtherAvailableProviders(currentProvider);
|
|
@@ -722,7 +728,7 @@ async function createCommand(description, options) {
|
|
|
722
728
|
const issueBody = extractIssueBody(aiOutput) + `
|
|
723
729
|
|
|
724
730
|
---
|
|
725
|
-
*Created with ${
|
|
731
|
+
*Created with ${getProviderDisplayName(currentProvider)} by [gent](https://github.com/Rotorsoft/gent)*`;
|
|
726
732
|
let title;
|
|
727
733
|
if (options.title) {
|
|
728
734
|
title = options.title;
|
|
@@ -1350,8 +1356,6 @@ async function offerCreateBranch(config, issueNumber, title) {
|
|
|
1350
1356
|
// src/commands/run.ts
|
|
1351
1357
|
import inquirer4 from "inquirer";
|
|
1352
1358
|
async function runCommand(issueNumberArg, options) {
|
|
1353
|
-
logger.bold("Running AI implementation workflow...");
|
|
1354
|
-
logger.newline();
|
|
1355
1359
|
const config = loadConfig();
|
|
1356
1360
|
const provider = options.provider ?? config.ai.provider;
|
|
1357
1361
|
const providerName = getProviderDisplayName(provider);
|
|
@@ -1504,12 +1508,8 @@ Labels: ${issue.labels.join(", ")}`
|
|
|
1504
1508
|
config
|
|
1505
1509
|
);
|
|
1506
1510
|
logger.newline();
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
);
|
|
1510
|
-
logger.dim(`${providerName} will implement the feature and create a commit.`);
|
|
1511
|
-
logger.dim("Review the changes before pushing.");
|
|
1512
|
-
logger.newline();
|
|
1511
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "implement ticket"));
|
|
1512
|
+
spinner.start();
|
|
1513
1513
|
const beforeSha = await getCurrentCommitSha();
|
|
1514
1514
|
let wasCancelled = false;
|
|
1515
1515
|
const handleSignal = () => {
|
|
@@ -1517,6 +1517,7 @@ Labels: ${issue.labels.join(", ")}`
|
|
|
1517
1517
|
};
|
|
1518
1518
|
process.on("SIGINT", handleSignal);
|
|
1519
1519
|
process.on("SIGTERM", handleSignal);
|
|
1520
|
+
spinner.stop();
|
|
1520
1521
|
let aiExitCode;
|
|
1521
1522
|
let usedProvider = provider;
|
|
1522
1523
|
try {
|
|
@@ -1661,8 +1662,6 @@ async function getChangedFiles(baseBranch = "main") {
|
|
|
1661
1662
|
// src/commands/pr.ts
|
|
1662
1663
|
import inquirer5 from "inquirer";
|
|
1663
1664
|
async function prCommand(options) {
|
|
1664
|
-
logger.bold("Creating AI-enhanced pull request...");
|
|
1665
|
-
logger.newline();
|
|
1666
1665
|
const config = loadConfig();
|
|
1667
1666
|
let currentProvider = options.provider ?? config.ai.provider;
|
|
1668
1667
|
const [ghAuth, aiOk] = await Promise.all([
|
|
@@ -1755,15 +1754,14 @@ IMPORTANT: This PR contains UI changes. Use the Playwright MCP plugin to:
|
|
|
1755
1754
|
while (true) {
|
|
1756
1755
|
const providerName = getProviderDisplayName(usedProvider);
|
|
1757
1756
|
try {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
);
|
|
1761
|
-
logger.newline();
|
|
1757
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "generate PR"));
|
|
1758
|
+
spinner.start();
|
|
1762
1759
|
const result = await invokeAI(
|
|
1763
|
-
{ prompt, streamOutput: true },
|
|
1760
|
+
{ prompt, streamOutput: true, onFirstData: () => spinner.stop() },
|
|
1764
1761
|
config,
|
|
1765
1762
|
usedProvider
|
|
1766
1763
|
);
|
|
1764
|
+
spinner.stop();
|
|
1767
1765
|
prBody = result.output;
|
|
1768
1766
|
logger.newline();
|
|
1769
1767
|
break;
|
|
@@ -2054,8 +2052,6 @@ function truncateComment(body, maxLength = 200) {
|
|
|
2054
2052
|
|
|
2055
2053
|
// src/commands/fix.ts
|
|
2056
2054
|
async function fixCommand(options) {
|
|
2057
|
-
logger.bold("Applying PR review feedback with AI...");
|
|
2058
|
-
logger.newline();
|
|
2059
2055
|
const config = loadConfig();
|
|
2060
2056
|
const provider = options.provider ?? config.ai.provider;
|
|
2061
2057
|
const providerName = getProviderDisplayName(provider);
|
|
@@ -2148,9 +2144,8 @@ async function fixCommand(options) {
|
|
|
2148
2144
|
${summary}`
|
|
2149
2145
|
);
|
|
2150
2146
|
logger.newline();
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
logger.newline();
|
|
2147
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "apply fixes"));
|
|
2148
|
+
spinner.start();
|
|
2154
2149
|
const beforeSha = await getCurrentCommitSha();
|
|
2155
2150
|
let wasCancelled = false;
|
|
2156
2151
|
const handleSignal = () => {
|
|
@@ -2158,6 +2153,7 @@ ${summary}`
|
|
|
2158
2153
|
};
|
|
2159
2154
|
process.on("SIGINT", handleSignal);
|
|
2160
2155
|
process.on("SIGTERM", handleSignal);
|
|
2156
|
+
spinner.stop();
|
|
2161
2157
|
let aiExitCode;
|
|
2162
2158
|
try {
|
|
2163
2159
|
const { result } = await invokeAIInteractive(
|
|
@@ -2242,7 +2238,7 @@ import { homedir } from "os";
|
|
|
2242
2238
|
// package.json
|
|
2243
2239
|
var package_default = {
|
|
2244
2240
|
name: "@rotorsoft/gent",
|
|
2245
|
-
version: "1.
|
|
2241
|
+
version: "1.20.1",
|
|
2246
2242
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
2247
2243
|
keywords: [
|
|
2248
2244
|
"cli",
|
|
@@ -2664,7 +2660,7 @@ async function statusCommand() {
|
|
|
2664
2660
|
}
|
|
2665
2661
|
|
|
2666
2662
|
// src/commands/tui.ts
|
|
2667
|
-
import { execa as
|
|
2663
|
+
import { execa as execa5 } from "execa";
|
|
2668
2664
|
|
|
2669
2665
|
// src/tui/state.ts
|
|
2670
2666
|
var envCache = null;
|
|
@@ -2821,7 +2817,11 @@ function getAvailableActions(state) {
|
|
|
2821
2817
|
actions.push({ id: "run", label: "run", shortcut: "r" });
|
|
2822
2818
|
}
|
|
2823
2819
|
}
|
|
2824
|
-
|
|
2820
|
+
if (state.hasValidRemote) {
|
|
2821
|
+
actions.push({ id: "list", label: "list", shortcut: "l" });
|
|
2822
|
+
} else {
|
|
2823
|
+
actions.push({ id: "github-remote", label: "github", shortcut: "g" });
|
|
2824
|
+
}
|
|
2825
2825
|
actions.push({ id: "refresh", label: "refresh", shortcut: "f" });
|
|
2826
2826
|
actions.push({ id: "switch-provider", label: "ai", shortcut: "a" });
|
|
2827
2827
|
actions.push({ id: "quit", label: "quit", shortcut: "q" });
|
|
@@ -3114,7 +3114,7 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
|
3114
3114
|
out(
|
|
3115
3115
|
row(
|
|
3116
3116
|
chalk3.yellow(
|
|
3117
|
-
"
|
|
3117
|
+
"Press [g] to create a GitHub repo and push"
|
|
3118
3118
|
),
|
|
3119
3119
|
w
|
|
3120
3120
|
)
|
|
@@ -3632,6 +3632,38 @@ function showStatus(title, message, dashboardLines) {
|
|
|
3632
3632
|
renderOverlay(dashboardLines, lines, w);
|
|
3633
3633
|
}
|
|
3634
3634
|
|
|
3635
|
+
// src/commands/github-remote.ts
|
|
3636
|
+
import { execa as execa4 } from "execa";
|
|
3637
|
+
async function githubRemoteCommand() {
|
|
3638
|
+
const isAuthenticated = await checkGhAuth();
|
|
3639
|
+
if (!isAuthenticated) {
|
|
3640
|
+
logger.error("GitHub CLI is not authenticated. Run: gh auth login");
|
|
3641
|
+
return false;
|
|
3642
|
+
}
|
|
3643
|
+
try {
|
|
3644
|
+
try {
|
|
3645
|
+
const { stdout } = await execa4("git", [
|
|
3646
|
+
"config",
|
|
3647
|
+
"--get",
|
|
3648
|
+
"remote.origin.url"
|
|
3649
|
+
]);
|
|
3650
|
+
if (stdout.trim()) {
|
|
3651
|
+
logger.error("Remote origin already exists: " + stdout.trim());
|
|
3652
|
+
return false;
|
|
3653
|
+
}
|
|
3654
|
+
} catch {
|
|
3655
|
+
}
|
|
3656
|
+
logger.info("Creating GitHub repository...");
|
|
3657
|
+
await execa4("gh", ["repo", "create", "--source=.", "--push", "--private"]);
|
|
3658
|
+
const branch = await getCurrentBranch();
|
|
3659
|
+
logger.success(`Repository created and ${branch} pushed to GitHub`);
|
|
3660
|
+
return true;
|
|
3661
|
+
} catch (error) {
|
|
3662
|
+
logger.error(`Failed to create remote: ${error}`);
|
|
3663
|
+
return false;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3635
3667
|
// src/commands/tui.ts
|
|
3636
3668
|
var CANCEL = /* @__PURE__ */ Symbol("cancel");
|
|
3637
3669
|
async function waitForKey(validKeys) {
|
|
@@ -3701,6 +3733,17 @@ async function executeAction(actionId, state, dashboardLines) {
|
|
|
3701
3733
|
await handleRun(state);
|
|
3702
3734
|
return CONTINUE;
|
|
3703
3735
|
}
|
|
3736
|
+
case "github-remote": {
|
|
3737
|
+
const confirmed = await showConfirm({
|
|
3738
|
+
title: "Push to GitHub",
|
|
3739
|
+
message: "Create a private GitHub repo and push?",
|
|
3740
|
+
dashboardLines
|
|
3741
|
+
});
|
|
3742
|
+
if (!confirmed) return SKIP_REFRESH;
|
|
3743
|
+
showStatus("Pushing", "Creating GitHub repo and pushing...", dashboardLines);
|
|
3744
|
+
await githubRemoteCommand();
|
|
3745
|
+
return CONTINUE;
|
|
3746
|
+
}
|
|
3704
3747
|
case "switch-provider":
|
|
3705
3748
|
await handleSwitchProvider(state, dashboardLines);
|
|
3706
3749
|
return SKIP_REFRESH;
|
|
@@ -3712,19 +3755,19 @@ async function executeAction(actionId, state, dashboardLines) {
|
|
|
3712
3755
|
}
|
|
3713
3756
|
async function handleCommit(state, dashboardLines) {
|
|
3714
3757
|
try {
|
|
3715
|
-
const { stdout: status } = await
|
|
3758
|
+
const { stdout: status } = await execa5("git", ["status", "--short"]);
|
|
3716
3759
|
if (!status.trim()) {
|
|
3717
3760
|
showStatus("Commit", "No changes to commit", dashboardLines);
|
|
3718
3761
|
await new Promise((r) => setTimeout(r, 1500));
|
|
3719
3762
|
return false;
|
|
3720
3763
|
}
|
|
3721
|
-
await
|
|
3722
|
-
const { stdout: diffStat } = await
|
|
3764
|
+
await execa5("git", ["add", "-A"]);
|
|
3765
|
+
const { stdout: diffStat } = await execa5("git", [
|
|
3723
3766
|
"diff",
|
|
3724
3767
|
"--cached",
|
|
3725
3768
|
"--stat"
|
|
3726
3769
|
]);
|
|
3727
|
-
const { stdout: diffPatch } = await
|
|
3770
|
+
const { stdout: diffPatch } = await execa5("git", ["diff", "--cached"]);
|
|
3728
3771
|
const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
|
|
3729
3772
|
const issueNumber = state.issue?.number ?? null;
|
|
3730
3773
|
const issueTitle = state.issue?.title ?? null;
|
|
@@ -3739,7 +3782,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3739
3782
|
dashboardLines
|
|
3740
3783
|
});
|
|
3741
3784
|
if (!mode) {
|
|
3742
|
-
await
|
|
3785
|
+
await execa5("git", ["reset", "HEAD"]);
|
|
3743
3786
|
return false;
|
|
3744
3787
|
}
|
|
3745
3788
|
let message;
|
|
@@ -3751,7 +3794,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3751
3794
|
});
|
|
3752
3795
|
message = input || CANCEL;
|
|
3753
3796
|
} else {
|
|
3754
|
-
showStatus("Generating",
|
|
3797
|
+
showStatus("Generating", aiSpinnerText(providerName, "generate commit message"), dashboardLines);
|
|
3755
3798
|
message = await generateCommitMessage(
|
|
3756
3799
|
diffContent,
|
|
3757
3800
|
issueNumber,
|
|
@@ -3761,7 +3804,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3761
3804
|
);
|
|
3762
3805
|
}
|
|
3763
3806
|
if (message === CANCEL) {
|
|
3764
|
-
await
|
|
3807
|
+
await execa5("git", ["reset", "HEAD"]);
|
|
3765
3808
|
return false;
|
|
3766
3809
|
}
|
|
3767
3810
|
const confirmed = await showConfirm({
|
|
@@ -3770,7 +3813,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3770
3813
|
dashboardLines
|
|
3771
3814
|
});
|
|
3772
3815
|
if (!confirmed) {
|
|
3773
|
-
await
|
|
3816
|
+
await execa5("git", ["reset", "HEAD"]);
|
|
3774
3817
|
return false;
|
|
3775
3818
|
}
|
|
3776
3819
|
const providerEmail = getProviderEmail(provider);
|
|
@@ -3778,7 +3821,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3778
3821
|
|
|
3779
3822
|
Co-Authored-By: ${providerName} <${providerEmail}>`;
|
|
3780
3823
|
showStatus("Committing", "Committing changes...", dashboardLines);
|
|
3781
|
-
await
|
|
3824
|
+
await execa5("git", ["commit", "-m", fullMessage]);
|
|
3782
3825
|
return true;
|
|
3783
3826
|
} catch (error) {
|
|
3784
3827
|
logger.error(`Commit failed: ${error}`);
|
|
@@ -3858,10 +3901,10 @@ ${feedbackLines}`);
|
|
|
3858
3901
|
}
|
|
3859
3902
|
async function handlePush(dashboardLines) {
|
|
3860
3903
|
try {
|
|
3861
|
-
const { stdout: branch } = await
|
|
3904
|
+
const { stdout: branch } = await execa5("git", ["branch", "--show-current"]);
|
|
3862
3905
|
const branchName = branch.trim();
|
|
3863
3906
|
showStatus("Pushing", `Pushing ${branchName} to remote...`, dashboardLines);
|
|
3864
|
-
await
|
|
3907
|
+
await execa5("git", ["push", "-u", "origin", branchName]);
|
|
3865
3908
|
} catch (error) {
|
|
3866
3909
|
logger.error(`Push failed: ${error}`);
|
|
3867
3910
|
}
|
|
@@ -4176,6 +4219,9 @@ program.command("fix").description("Apply PR review feedback using AI").option(
|
|
|
4176
4219
|
program.command("status").description("Show current workflow status").action(async () => {
|
|
4177
4220
|
await statusCommand();
|
|
4178
4221
|
});
|
|
4222
|
+
program.command("github-remote").description("Create a GitHub repository and push local repo").action(async () => {
|
|
4223
|
+
await githubRemoteCommand();
|
|
4224
|
+
});
|
|
4179
4225
|
program.command("ui").description("Launch interactive dashboard").action(async () => {
|
|
4180
4226
|
await tuiCommand();
|
|
4181
4227
|
});
|