@rotorsoft/gent 1.8.0 → 1.9.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/dist/{chunk-XQJWUAG6.js → chunk-ZLI7ZCC6.js} +66 -1
- package/dist/chunk-ZLI7ZCC6.js.map +1 -0
- package/dist/index.js +326 -4
- package/dist/index.js.map +1 -1
- package/dist/{setup-labels-WXNPB6OD.js → setup-labels-GN6ID47E.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-XQJWUAG6.js.map +0 -1
- /package/dist/{setup-labels-WXNPB6OD.js.map → setup-labels-GN6ID47E.js.map} +0 -0
|
@@ -612,10 +612,72 @@ async function getPrForBranch() {
|
|
|
612
612
|
return null;
|
|
613
613
|
}
|
|
614
614
|
}
|
|
615
|
+
async function getPrReviewData(prNumber) {
|
|
616
|
+
const prArgs = ["pr", "view"];
|
|
617
|
+
if (prNumber) {
|
|
618
|
+
prArgs.push(String(prNumber));
|
|
619
|
+
}
|
|
620
|
+
prArgs.push("--json", "reviews,comments");
|
|
621
|
+
const { stdout: prStdout } = await execa("gh", prArgs);
|
|
622
|
+
const prData = JSON.parse(prStdout);
|
|
623
|
+
let reviewThreads = [];
|
|
624
|
+
try {
|
|
625
|
+
const { stdout: repoStdout } = await execa("gh", ["repo", "view", "--json", "owner,name"]);
|
|
626
|
+
const repoData = JSON.parse(repoStdout);
|
|
627
|
+
const owner = repoData.owner?.login ?? repoData.owner;
|
|
628
|
+
const repo = repoData.name;
|
|
629
|
+
const graphqlQuery = `query { repository(owner: "${owner}", name: "${repo}") { pullRequest(number: ${prNumber}) { reviewThreads(first: 100) { nodes { isResolved path line comments(first: 100) { nodes { databaseId author { login } body path line createdAt } } } } } } }`;
|
|
630
|
+
const { stdout: graphqlStdout } = await execa("gh", ["api", "graphql", "-f", `query=${graphqlQuery}`]);
|
|
631
|
+
const graphqlData = JSON.parse(graphqlStdout);
|
|
632
|
+
const prNode = graphqlData.data?.repository?.pullRequest;
|
|
633
|
+
const threadNodes = prNode?.reviewThreads?.nodes ?? [];
|
|
634
|
+
reviewThreads = threadNodes.map((thread) => ({
|
|
635
|
+
isResolved: thread.isResolved ?? null,
|
|
636
|
+
path: thread.path,
|
|
637
|
+
line: thread.line ?? null,
|
|
638
|
+
comments: (thread.comments?.nodes ?? []).map((comment) => ({
|
|
639
|
+
id: comment.databaseId,
|
|
640
|
+
author: comment.author?.login ?? "unknown",
|
|
641
|
+
body: comment.body ?? "",
|
|
642
|
+
path: comment.path ?? thread.path,
|
|
643
|
+
line: comment.line ?? thread.line ?? null,
|
|
644
|
+
createdAt: comment.createdAt
|
|
645
|
+
}))
|
|
646
|
+
}));
|
|
647
|
+
} catch {
|
|
648
|
+
reviewThreads = [];
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
reviews: (prData.reviews ?? []).map((review) => ({
|
|
652
|
+
author: review.author?.login ?? "unknown",
|
|
653
|
+
body: review.body ?? "",
|
|
654
|
+
state: review.state ?? "UNKNOWN",
|
|
655
|
+
submittedAt: review.submittedAt
|
|
656
|
+
})),
|
|
657
|
+
reviewThreads,
|
|
658
|
+
comments: (prData.comments ?? []).map((comment) => ({
|
|
659
|
+
id: comment.id,
|
|
660
|
+
author: comment.author?.login ?? "unknown",
|
|
661
|
+
body: comment.body ?? "",
|
|
662
|
+
createdAt: comment.createdAt
|
|
663
|
+
}))
|
|
664
|
+
};
|
|
665
|
+
}
|
|
615
666
|
async function getCurrentUser() {
|
|
616
667
|
const { stdout } = await execa("gh", ["api", "user", "--jq", ".login"]);
|
|
617
668
|
return stdout.trim();
|
|
618
669
|
}
|
|
670
|
+
async function replyToReviewComment(prNumber, commentId, body) {
|
|
671
|
+
await execa("gh", [
|
|
672
|
+
"api",
|
|
673
|
+
`repos/{owner}/{repo}/pulls/${prNumber}/comments/${commentId}/replies`,
|
|
674
|
+
"-f",
|
|
675
|
+
`body=${body}`
|
|
676
|
+
]);
|
|
677
|
+
}
|
|
678
|
+
async function addPrComment(prNumber, body) {
|
|
679
|
+
await execa("gh", ["pr", "comment", String(prNumber), "--body", body]);
|
|
680
|
+
}
|
|
619
681
|
|
|
620
682
|
// src/utils/validators.ts
|
|
621
683
|
import { execa as execa2 } from "execa";
|
|
@@ -741,7 +803,10 @@ export {
|
|
|
741
803
|
assignIssue,
|
|
742
804
|
createPullRequest,
|
|
743
805
|
getPrForBranch,
|
|
806
|
+
getPrReviewData,
|
|
744
807
|
getCurrentUser,
|
|
808
|
+
replyToReviewComment,
|
|
809
|
+
addPrComment,
|
|
745
810
|
setupLabelsCommand
|
|
746
811
|
};
|
|
747
|
-
//# sourceMappingURL=chunk-
|
|
812
|
+
//# sourceMappingURL=chunk-ZLI7ZCC6.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 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 } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { GentConfig, AIProvider } from \"../types/index.js\";\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 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(cwd: string = process.cwd()): 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 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 // Environment variable takes precedence\n ...(envProvider && { provider: envProvider }),\n },\n validation: user.validation ?? defaults.validation,\n };\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# 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 validation: string[];\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 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 { name: \"area:testing\", color: \"E99695\", description: \"Test infrastructure\" },\n { name: \"area:infra\", color: \"BFD4F2\", description: \"Infrastructure/DevOps\" },\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 { GitHubIssue, GitHubLabel, GitHubReviewData } 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 = [\"issue\", \"list\", \"--json\", \"number,title,body,labels,state,url\"];\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 = [\"issue\", \"create\", \"--title\", options.title, \"--body\", options.body];\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 async function getPrForBranch(): Promise<{\n number: number;\n url: string;\n} | 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 getPrReviewData(prNumber?: number): 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 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\", [\"repo\", \"view\", \"--json\", \"owner,name\"]);\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 path line comments(first: 100) { nodes { databaseId author { login } body path line createdAt } } } } } } }`;\n\n const { stdout: graphqlStdout } = await execa(\"gh\", [\"api\", \"graphql\", \"-f\", `query=${graphqlQuery}`]);\n const graphqlData = JSON.parse(graphqlStdout);\n const prNode = graphqlData.data?.repository?.pullRequest;\n const threadNodes = prNode?.reviewThreads?.nodes ?? [];\n\n reviewThreads = threadNodes.map((thread: {\n isResolved?: boolean | null;\n path?: string;\n line?: number | null;\n comments?: { 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 isResolved: thread.isResolved ?? null,\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 } catch {\n // If GraphQL fails (e.g., no permissions), continue with empty threads\n reviewThreads = [];\n }\n\n return {\n reviews: (prData.reviews ?? []).map((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 reviewThreads,\n comments: (prData.comments ?? []).map((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\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(prNumber: number, body: string): Promise<void> {\n await execa(\"gh\", [\"pr\", \"comment\", String(prNumber), \"--body\", body]);\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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
addIssueComment,
|
|
4
|
+
addPrComment,
|
|
4
5
|
assignIssue,
|
|
5
6
|
buildIssueLabels,
|
|
6
7
|
checkAIProvider,
|
|
@@ -19,18 +20,20 @@ import {
|
|
|
19
20
|
getCurrentUser,
|
|
20
21
|
getIssue,
|
|
21
22
|
getPrForBranch,
|
|
23
|
+
getPrReviewData,
|
|
22
24
|
getWorkflowLabels,
|
|
23
25
|
isValidIssueNumber,
|
|
24
26
|
listIssues,
|
|
25
27
|
loadAgentInstructions,
|
|
26
28
|
loadConfig,
|
|
27
29
|
logger,
|
|
30
|
+
replyToReviewComment,
|
|
28
31
|
sanitizeSlug,
|
|
29
32
|
setupLabelsCommand,
|
|
30
33
|
sortByPriority,
|
|
31
34
|
updateIssueLabels,
|
|
32
35
|
withSpinner
|
|
33
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ZLI7ZCC6.js";
|
|
34
37
|
|
|
35
38
|
// src/index.ts
|
|
36
39
|
import { Command } from "commander";
|
|
@@ -191,7 +194,7 @@ async function initCommand(options) {
|
|
|
191
194
|
}
|
|
192
195
|
]);
|
|
193
196
|
if (setupLabels) {
|
|
194
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-
|
|
197
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-GN6ID47E.js");
|
|
195
198
|
await setupLabelsCommand2();
|
|
196
199
|
}
|
|
197
200
|
}
|
|
@@ -467,7 +470,7 @@ META:type=<type>,priority=<priority>,risk=<risk>,area=<area>
|
|
|
467
470
|
Example: META:type=feature,priority=high,risk=low,area=ui`;
|
|
468
471
|
return basePrompt;
|
|
469
472
|
}
|
|
470
|
-
function buildImplementationPrompt(issue, agentInstructions, progressContent, config) {
|
|
473
|
+
function buildImplementationPrompt(issue, agentInstructions, progressContent, config, reviewFeedback = null) {
|
|
471
474
|
const providerName = getProviderDisplayName(config.ai.provider);
|
|
472
475
|
const providerEmail = getProviderEmail(config.ai.provider);
|
|
473
476
|
return `GitHub Issue #${issue.number}: ${issue.title}
|
|
@@ -481,6 +484,10 @@ ${agentInstructions}
|
|
|
481
484
|
${progressContent ? `## Previous Progress
|
|
482
485
|
${progressContent}
|
|
483
486
|
|
|
487
|
+
` : ""}
|
|
488
|
+
${reviewFeedback ? `## Review Feedback
|
|
489
|
+
${reviewFeedback}
|
|
490
|
+
|
|
484
491
|
` : ""}
|
|
485
492
|
|
|
486
493
|
## Your Task
|
|
@@ -960,6 +967,10 @@ async function hasNewCommits(beforeSha) {
|
|
|
960
967
|
const currentSha = await getCurrentCommitSha();
|
|
961
968
|
return currentSha !== beforeSha;
|
|
962
969
|
}
|
|
970
|
+
async function getLastCommitTimestamp() {
|
|
971
|
+
const { stdout } = await execa2("git", ["log", "-1", "--format=%cI"]);
|
|
972
|
+
return stdout.trim();
|
|
973
|
+
}
|
|
963
974
|
|
|
964
975
|
// src/lib/branch.ts
|
|
965
976
|
async function generateBranchName(config, issueNumber, issueTitle, type) {
|
|
@@ -1434,6 +1445,314 @@ function generateFallbackBody(issue, commits) {
|
|
|
1434
1445
|
return body;
|
|
1435
1446
|
}
|
|
1436
1447
|
|
|
1448
|
+
// src/commands/fix.ts
|
|
1449
|
+
import inquirer5 from "inquirer";
|
|
1450
|
+
|
|
1451
|
+
// src/lib/review-feedback.ts
|
|
1452
|
+
var ACTIONABLE_KEYWORDS = [
|
|
1453
|
+
"todo",
|
|
1454
|
+
"fix",
|
|
1455
|
+
"should",
|
|
1456
|
+
"must",
|
|
1457
|
+
"needs",
|
|
1458
|
+
"please",
|
|
1459
|
+
"consider",
|
|
1460
|
+
"can you",
|
|
1461
|
+
"change",
|
|
1462
|
+
"update",
|
|
1463
|
+
"remove",
|
|
1464
|
+
"add"
|
|
1465
|
+
];
|
|
1466
|
+
var TRIVIAL_COMMENTS = ["lgtm", "looks good", "approved"];
|
|
1467
|
+
function summarizeReviewFeedback(data, options) {
|
|
1468
|
+
const items = extractReviewFeedbackItems(data, options);
|
|
1469
|
+
return {
|
|
1470
|
+
items,
|
|
1471
|
+
summary: items.length > 0 ? formatReviewFeedbackSummary(items) : ""
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
function isAfterTimestamp(itemTimestamp, afterTimestamp) {
|
|
1475
|
+
if (!afterTimestamp || !itemTimestamp) {
|
|
1476
|
+
return true;
|
|
1477
|
+
}
|
|
1478
|
+
return new Date(itemTimestamp) > new Date(afterTimestamp);
|
|
1479
|
+
}
|
|
1480
|
+
function extractReviewFeedbackItems(data, options) {
|
|
1481
|
+
const items = [];
|
|
1482
|
+
const afterTimestamp = options?.afterTimestamp;
|
|
1483
|
+
for (const review of data.reviews) {
|
|
1484
|
+
const body = review.body?.trim() ?? "";
|
|
1485
|
+
if (!body || isTrivialComment(body)) {
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (!isAfterTimestamp(review.submittedAt, afterTimestamp)) {
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1491
|
+
const isChangesRequested = review.state === "CHANGES_REQUESTED";
|
|
1492
|
+
const actionable = isChangesRequested || isActionableText(body);
|
|
1493
|
+
if (!actionable) {
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
items.push({
|
|
1497
|
+
source: "review",
|
|
1498
|
+
author: review.author,
|
|
1499
|
+
body,
|
|
1500
|
+
state: review.state
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
for (const thread of data.reviewThreads) {
|
|
1504
|
+
const isUnresolved = thread.isResolved === false || thread.isResolved === void 0 || thread.isResolved === null;
|
|
1505
|
+
if (!isUnresolved) {
|
|
1506
|
+
const hasRecentComments = (thread.comments ?? []).some(
|
|
1507
|
+
(c) => isAfterTimestamp(c.createdAt, afterTimestamp)
|
|
1508
|
+
);
|
|
1509
|
+
if (!hasRecentComments) {
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
if (!isActionableThread(thread)) {
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
const comments = thread.comments ?? [];
|
|
1517
|
+
const latestComment = findLatestMeaningfulComment(comments);
|
|
1518
|
+
if (!latestComment) {
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
items.push({
|
|
1522
|
+
source: "thread",
|
|
1523
|
+
author: latestComment.author,
|
|
1524
|
+
body: latestComment.body,
|
|
1525
|
+
path: thread.path ?? latestComment.path,
|
|
1526
|
+
line: thread.line ?? latestComment.line ?? null,
|
|
1527
|
+
commentId: latestComment.id
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
for (const comment of data.comments ?? []) {
|
|
1531
|
+
const body = comment.body?.trim() ?? "";
|
|
1532
|
+
if (!body || isTrivialComment(body)) {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
if (!isAfterTimestamp(comment.createdAt, afterTimestamp)) {
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (!isActionableText(body)) {
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
items.push({
|
|
1542
|
+
source: "comment",
|
|
1543
|
+
author: comment.author,
|
|
1544
|
+
body,
|
|
1545
|
+
commentId: comment.id
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
return items;
|
|
1549
|
+
}
|
|
1550
|
+
function formatReviewFeedbackSummary(items) {
|
|
1551
|
+
return items.map((item) => {
|
|
1552
|
+
const location = formatLocation(item);
|
|
1553
|
+
const stateLabel = item.state ? formatState(item.state) : null;
|
|
1554
|
+
const author = item.author ? `@${item.author}` : "Reviewer";
|
|
1555
|
+
const body = truncateComment(item.body);
|
|
1556
|
+
let header;
|
|
1557
|
+
if (item.source === "review") {
|
|
1558
|
+
header = stateLabel ? `Review (${stateLabel})` : "Review";
|
|
1559
|
+
} else if (item.source === "comment") {
|
|
1560
|
+
header = "Comment";
|
|
1561
|
+
} else {
|
|
1562
|
+
header = location;
|
|
1563
|
+
}
|
|
1564
|
+
return `- [${header}] ${author}: ${body}`;
|
|
1565
|
+
}).join("\n");
|
|
1566
|
+
}
|
|
1567
|
+
function isActionableThread(thread) {
|
|
1568
|
+
if (thread.isResolved === false || thread.isResolved === void 0 || thread.isResolved === null) {
|
|
1569
|
+
return true;
|
|
1570
|
+
}
|
|
1571
|
+
return (thread.comments ?? []).some((comment) => isActionableText(comment.body));
|
|
1572
|
+
}
|
|
1573
|
+
function isActionableText(text) {
|
|
1574
|
+
const normalized = text.toLowerCase();
|
|
1575
|
+
return ACTIONABLE_KEYWORDS.some((keyword) => normalized.includes(keyword));
|
|
1576
|
+
}
|
|
1577
|
+
function isTrivialComment(text) {
|
|
1578
|
+
const normalized = text.trim().toLowerCase();
|
|
1579
|
+
return TRIVIAL_COMMENTS.some((entry) => normalized === entry);
|
|
1580
|
+
}
|
|
1581
|
+
function findLatestMeaningfulComment(comments) {
|
|
1582
|
+
for (let i = comments.length - 1; i >= 0; i -= 1) {
|
|
1583
|
+
const body = comments[i].body?.trim() ?? "";
|
|
1584
|
+
if (body && !isTrivialComment(body)) {
|
|
1585
|
+
return comments[i];
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
function formatLocation(item) {
|
|
1591
|
+
if (item.path && item.line) {
|
|
1592
|
+
return `${item.path}:${item.line}`;
|
|
1593
|
+
}
|
|
1594
|
+
if (item.path) {
|
|
1595
|
+
return item.path;
|
|
1596
|
+
}
|
|
1597
|
+
return "Thread";
|
|
1598
|
+
}
|
|
1599
|
+
function formatState(state) {
|
|
1600
|
+
return state.replace(/_/g, " ").toLowerCase();
|
|
1601
|
+
}
|
|
1602
|
+
function truncateComment(body, maxLength = 200) {
|
|
1603
|
+
const normalized = body.replace(/\s+/g, " ").trim();
|
|
1604
|
+
if (normalized.length <= maxLength) {
|
|
1605
|
+
return normalized;
|
|
1606
|
+
}
|
|
1607
|
+
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// src/commands/fix.ts
|
|
1611
|
+
async function fixCommand(options) {
|
|
1612
|
+
logger.bold("Applying PR review feedback with AI...");
|
|
1613
|
+
logger.newline();
|
|
1614
|
+
const config = loadConfig();
|
|
1615
|
+
const provider = options.provider ?? config.ai.provider;
|
|
1616
|
+
const providerName = getProviderDisplayName(provider);
|
|
1617
|
+
const [ghAuth, aiOk] = await Promise.all([
|
|
1618
|
+
checkGhAuth(),
|
|
1619
|
+
checkAIProvider(provider)
|
|
1620
|
+
]);
|
|
1621
|
+
if (!ghAuth) {
|
|
1622
|
+
logger.error("Not authenticated with GitHub. Run 'gh auth login' first.");
|
|
1623
|
+
process.exit(1);
|
|
1624
|
+
}
|
|
1625
|
+
if (!aiOk) {
|
|
1626
|
+
logger.error(`${providerName} CLI not found. Please install ${provider} CLI first.`);
|
|
1627
|
+
process.exit(1);
|
|
1628
|
+
}
|
|
1629
|
+
if (await isOnMainBranch()) {
|
|
1630
|
+
logger.error("Cannot apply fixes from main/master branch. Switch to the PR branch first.");
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
const hasChanges = await hasUncommittedChanges();
|
|
1634
|
+
if (hasChanges) {
|
|
1635
|
+
logger.warning("You have uncommitted changes.");
|
|
1636
|
+
const { proceed } = await inquirer5.prompt([
|
|
1637
|
+
{
|
|
1638
|
+
type: "confirm",
|
|
1639
|
+
name: "proceed",
|
|
1640
|
+
message: "Continue anyway?",
|
|
1641
|
+
default: false
|
|
1642
|
+
}
|
|
1643
|
+
]);
|
|
1644
|
+
if (!proceed) {
|
|
1645
|
+
logger.info("Aborting. Please commit or stash your changes first.");
|
|
1646
|
+
process.exit(0);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
const pr = await withSpinner("Resolving pull request...", async () => {
|
|
1650
|
+
return getPrForBranch();
|
|
1651
|
+
});
|
|
1652
|
+
if (!pr) {
|
|
1653
|
+
logger.error("No pull request found for the current branch. Create one with 'gent pr' first.");
|
|
1654
|
+
process.exit(1);
|
|
1655
|
+
}
|
|
1656
|
+
const lastCommitTimestamp = await getLastCommitTimestamp();
|
|
1657
|
+
const reviewData = await withSpinner("Fetching review feedback...", async () => {
|
|
1658
|
+
return getPrReviewData(pr.number);
|
|
1659
|
+
});
|
|
1660
|
+
const totalComments = countReviewComments(reviewData);
|
|
1661
|
+
if (totalComments === 0) {
|
|
1662
|
+
logger.error(`No review comments found for PR #${pr.number}.`);
|
|
1663
|
+
process.exit(1);
|
|
1664
|
+
}
|
|
1665
|
+
const { items, summary } = summarizeReviewFeedback(reviewData, { afterTimestamp: lastCommitTimestamp });
|
|
1666
|
+
if (items.length === 0 || !summary) {
|
|
1667
|
+
logger.error("No new actionable review feedback found since your last commit.");
|
|
1668
|
+
process.exit(1);
|
|
1669
|
+
}
|
|
1670
|
+
logger.newline();
|
|
1671
|
+
logger.box("Review Feedback Summary", summary);
|
|
1672
|
+
logger.newline();
|
|
1673
|
+
const currentBranch = await getCurrentBranch();
|
|
1674
|
+
const issueNumber = extractIssueNumber(currentBranch);
|
|
1675
|
+
if (!issueNumber) {
|
|
1676
|
+
logger.error("Could not determine issue number from branch name.");
|
|
1677
|
+
process.exit(1);
|
|
1678
|
+
}
|
|
1679
|
+
const issue = await withSpinner("Fetching linked issue...", async () => {
|
|
1680
|
+
return getIssue(issueNumber);
|
|
1681
|
+
});
|
|
1682
|
+
const agentInstructions = loadAgentInstructions();
|
|
1683
|
+
const progressContent = readProgress(config);
|
|
1684
|
+
const prompt = buildImplementationPrompt(issue, agentInstructions, progressContent, config, summary);
|
|
1685
|
+
logger.newline();
|
|
1686
|
+
logger.info(`Starting ${colors.provider(providerName)} fix session...`);
|
|
1687
|
+
logger.dim("Review feedback will be appended to the implementation prompt.");
|
|
1688
|
+
logger.newline();
|
|
1689
|
+
const beforeSha = await getCurrentCommitSha();
|
|
1690
|
+
let wasCancelled = false;
|
|
1691
|
+
const handleSignal = () => {
|
|
1692
|
+
wasCancelled = true;
|
|
1693
|
+
};
|
|
1694
|
+
process.on("SIGINT", handleSignal);
|
|
1695
|
+
process.on("SIGTERM", handleSignal);
|
|
1696
|
+
let aiExitCode;
|
|
1697
|
+
try {
|
|
1698
|
+
const { result } = await invokeAIInteractive(prompt, config, options.provider);
|
|
1699
|
+
aiExitCode = result.exitCode ?? void 0;
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
if (error && typeof error === "object" && "exitCode" in error) {
|
|
1702
|
+
aiExitCode = error.exitCode;
|
|
1703
|
+
}
|
|
1704
|
+
logger.error(`${providerName} session failed: ${error}`);
|
|
1705
|
+
} finally {
|
|
1706
|
+
process.off("SIGINT", handleSignal);
|
|
1707
|
+
process.off("SIGTERM", handleSignal);
|
|
1708
|
+
}
|
|
1709
|
+
logger.newline();
|
|
1710
|
+
if (wasCancelled) {
|
|
1711
|
+
logger.warning("Operation was cancelled. No changes were recorded.");
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
const commitsCreated = await hasNewCommits(beforeSha);
|
|
1715
|
+
if (commitsCreated) {
|
|
1716
|
+
logger.success(`${providerName} session completed with new commits.`);
|
|
1717
|
+
await replyToFeedbackItems(pr.number, items);
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
const isRateLimited = aiExitCode === 2;
|
|
1721
|
+
if (isRateLimited) {
|
|
1722
|
+
logger.warning(`${providerName} session ended due to rate limits. No commits were created.`);
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
logger.warning(`${providerName} session completed but no commits were created.`);
|
|
1726
|
+
}
|
|
1727
|
+
function countReviewComments(data) {
|
|
1728
|
+
const reviewBodies = data.reviews.filter((review) => review.body?.trim()).length;
|
|
1729
|
+
const threadBodies = data.reviewThreads.reduce((count, thread) => {
|
|
1730
|
+
const threadCount = (thread.comments ?? []).filter((comment) => comment.body?.trim()).length;
|
|
1731
|
+
return count + threadCount;
|
|
1732
|
+
}, 0);
|
|
1733
|
+
const prComments = (data.comments ?? []).filter((comment) => comment.body?.trim()).length;
|
|
1734
|
+
return reviewBodies + threadBodies + prComments;
|
|
1735
|
+
}
|
|
1736
|
+
async function replyToFeedbackItems(prNumber, items) {
|
|
1737
|
+
const replyBody = "Addressed in latest commit.";
|
|
1738
|
+
let repliedCount = 0;
|
|
1739
|
+
for (const item of items) {
|
|
1740
|
+
try {
|
|
1741
|
+
if (item.source === "thread" && typeof item.commentId === "number") {
|
|
1742
|
+
await replyToReviewComment(prNumber, item.commentId, replyBody);
|
|
1743
|
+
repliedCount++;
|
|
1744
|
+
} else if (item.source === "comment" && item.commentId) {
|
|
1745
|
+
await addPrComment(prNumber, `@${item.author} ${replyBody}`);
|
|
1746
|
+
repliedCount++;
|
|
1747
|
+
}
|
|
1748
|
+
} catch {
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
if (repliedCount > 0) {
|
|
1752
|
+
logger.dim(`Replied to ${repliedCount} feedback item${repliedCount > 1 ? "s" : ""}.`);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1437
1756
|
// src/lib/version.ts
|
|
1438
1757
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
1439
1758
|
import { join as join3 } from "path";
|
|
@@ -1442,7 +1761,7 @@ import { homedir } from "os";
|
|
|
1442
1761
|
// package.json
|
|
1443
1762
|
var package_default = {
|
|
1444
1763
|
name: "@rotorsoft/gent",
|
|
1445
|
-
version: "1.
|
|
1764
|
+
version: "1.9.1",
|
|
1446
1765
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
1447
1766
|
keywords: [
|
|
1448
1767
|
"cli",
|
|
@@ -1811,6 +2130,9 @@ program.command("run [issue-number]").description("Run AI to implement a GitHub
|
|
|
1811
2130
|
program.command("pr").description("Create an AI-enhanced pull request").option("-d, --draft", "Create as draft PR").option("-p, --provider <provider>", "AI provider to use (claude, gemini, or codex)").action(async (options) => {
|
|
1812
2131
|
await prCommand({ draft: options.draft, provider: options.provider });
|
|
1813
2132
|
});
|
|
2133
|
+
program.command("fix").description("Apply PR review feedback using AI").option("-p, --provider <provider>", "AI provider to use (claude, gemini, or codex)").action(async (options) => {
|
|
2134
|
+
await fixCommand({ provider: options.provider });
|
|
2135
|
+
});
|
|
1814
2136
|
program.command("status").description("Show current workflow status").action(async () => {
|
|
1815
2137
|
await statusCommand();
|
|
1816
2138
|
});
|