@rotorsoft/gent 1.24.0 → 1.25.0
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-P5MZOU4B.js → chunk-ENNNKNLI.js} +33 -3
- package/dist/chunk-ENNNKNLI.js.map +1 -0
- package/dist/{chunk-YS7HWP4W.js → chunk-MRF5FZO6.js} +2 -2
- package/dist/{chunk-OIHSXI5X.js → chunk-PHZJIEY6.js} +2 -2
- package/dist/github-remote-YD46C4M7.js +9 -0
- package/dist/index.js +83 -106
- package/dist/index.js.map +1 -1
- package/dist/setup-labels-W4Z3EJ43.js +9 -0
- package/package.json +1 -1
- package/dist/chunk-P5MZOU4B.js.map +0 -1
- package/dist/github-remote-G6UKRDUB.js +0 -9
- package/dist/setup-labels-FZEN5TKM.js +0 -9
- /package/dist/{chunk-YS7HWP4W.js.map → chunk-MRF5FZO6.js.map} +0 -0
- /package/dist/{chunk-OIHSXI5X.js.map → chunk-PHZJIEY6.js.map} +0 -0
- /package/dist/{github-remote-G6UKRDUB.js.map → github-remote-YD46C4M7.js.map} +0 -0
- /package/dist/{setup-labels-FZEN5TKM.js.map → setup-labels-W4Z3EJ43.js.map} +0 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/utils/logger.ts
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
var stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
6
|
+
var visibleLength = (str) => stripAnsi(str).length;
|
|
5
7
|
var logger = {
|
|
6
8
|
info: (message) => console.log(chalk.blue("\u2139"), message),
|
|
7
9
|
success: (message) => console.log(chalk.green("\u2713"), message),
|
|
@@ -15,10 +17,38 @@ var logger = {
|
|
|
15
17
|
dim: (message) => console.log(chalk.dim(message)),
|
|
16
18
|
bold: (message) => console.log(chalk.bold(message)),
|
|
17
19
|
highlight: (message) => console.log(chalk.cyan(message)),
|
|
20
|
+
/**
|
|
21
|
+
* Display a bordered table with key-value pairs.
|
|
22
|
+
* Used for operation summaries before AI invocation.
|
|
23
|
+
*/
|
|
24
|
+
table: (title, entries) => {
|
|
25
|
+
const validEntries = entries.filter((e) => e.value);
|
|
26
|
+
if (validEntries.length === 0) return;
|
|
27
|
+
const keyWidth = Math.max(...validEntries.map((e) => e.key.length));
|
|
28
|
+
const valueWidth = Math.max(
|
|
29
|
+
...validEntries.map((e) => visibleLength(e.value))
|
|
30
|
+
);
|
|
31
|
+
const innerWidth = Math.max(title.length, keyWidth + valueWidth + 3);
|
|
32
|
+
const totalWidth = innerWidth + 4;
|
|
33
|
+
const padVisible = (str, len) => {
|
|
34
|
+
const visible = visibleLength(str);
|
|
35
|
+
return str + " ".repeat(Math.max(0, len - visible));
|
|
36
|
+
};
|
|
37
|
+
console.log(chalk.dim("\u250C" + "\u2500".repeat(totalWidth - 2) + "\u2510"));
|
|
38
|
+
console.log(
|
|
39
|
+
`${chalk.dim("\u2502")} ${chalk.bold.cyan(title.padEnd(innerWidth))} ${chalk.dim("\u2502")}`
|
|
40
|
+
);
|
|
41
|
+
console.log(chalk.dim("\u251C" + "\u2500".repeat(totalWidth - 2) + "\u2524"));
|
|
42
|
+
for (const { key, value } of validEntries) {
|
|
43
|
+
const row = chalk.dim(key.padEnd(keyWidth)) + " " + value;
|
|
44
|
+
console.log(
|
|
45
|
+
`${chalk.dim("\u2502")} ${padVisible(row, innerWidth)} ${chalk.dim("\u2502")}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
console.log(chalk.dim("\u2514" + "\u2500".repeat(totalWidth - 2) + "\u2518"));
|
|
49
|
+
},
|
|
18
50
|
box: (title, content) => {
|
|
19
51
|
const lines = content.split("\n");
|
|
20
|
-
const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
21
|
-
const visibleLength = (str) => stripAnsi(str).length;
|
|
22
52
|
const maxLen = Math.max(title.length, ...lines.map((l) => visibleLength(l))) + 4;
|
|
23
53
|
const border = "\u2500".repeat(maxLen);
|
|
24
54
|
const padVisible = (str, len) => {
|
|
@@ -853,4 +883,4 @@ export {
|
|
|
853
883
|
remoteBranchExists,
|
|
854
884
|
fetchAndCheckout
|
|
855
885
|
};
|
|
856
|
-
//# sourceMappingURL=chunk-
|
|
886
|
+
//# sourceMappingURL=chunk-ENNNKNLI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/lib/config.ts","../src/utils/validators.ts","../src/lib/github.ts","../src/lib/git.ts"],"sourcesContent":["import chalk from \"chalk\";\n\n// eslint-disable-next-line no-control-regex\nconst stripAnsi = (str: string) => str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\nconst visibleLength = (str: string) => stripAnsi(str).length;\n\nexport interface TableEntry {\n key: string;\n value: string;\n}\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 /**\n * Display a bordered table with key-value pairs.\n * Used for operation summaries before AI invocation.\n */\n table: (title: string, entries: TableEntry[]) => {\n // Filter out entries with empty values\n const validEntries = entries.filter((e) => e.value);\n if (validEntries.length === 0) return;\n\n // Calculate column widths\n const keyWidth = Math.max(...validEntries.map((e) => e.key.length));\n const valueWidth = Math.max(\n ...validEntries.map((e) => visibleLength(e.value))\n );\n const innerWidth = Math.max(title.length, keyWidth + valueWidth + 3); // 3 = \" : \"\n const totalWidth = innerWidth + 4; // 4 = \"│ \" + \" │\"\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 // Render\n console.log(chalk.dim(\"┌\" + \"─\".repeat(totalWidth - 2) + \"┐\"));\n console.log(\n `${chalk.dim(\"│\")} ${chalk.bold.cyan(title.padEnd(innerWidth))} ${chalk.dim(\"│\")}`\n );\n console.log(chalk.dim(\"├\" + \"─\".repeat(totalWidth - 2) + \"┤\"));\n for (const { key, value } of validEntries) {\n const row = chalk.dim(key.padEnd(keyWidth)) + \" \" + value;\n console.log(\n `${chalk.dim(\"│\")} ${padVisible(row, innerWidth)} ${chalk.dim(\"│\")}`\n );\n }\n console.log(chalk.dim(\"└\" + \"─\".repeat(totalWidth - 2) + \"┘\"));\n },\n\n box: (title: string, content: string) => {\n const lines = content.split(\"\\n\");\n // Calculate visible length (strips ANSI codes) for proper alignment\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 { 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","import { execa } from \"execa\";\nimport type { GentConfig, AIProvider } from \"../types/index.js\";\nimport { configExists } from \"../lib/config.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 checkInitialized(cwd?: string): boolean {\n return configExists(cwd);\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 { execa } from \"execa\";\nimport type {\n GitHubIssue,\n GitHubLabel,\n GitHubReviewData,\n} from \"../types/index.js\";\n\nconst WORKFLOW_LABELS = [\n \"ai-ready\",\n \"ai-in-progress\",\n \"ai-completed\",\n \"ai-blocked\",\n];\n\nexport async function checkLabelsExist(): Promise<boolean> {\n try {\n const { stdout } = await execa(\"gh\", [\n \"label\",\n \"list\",\n \"--json\",\n \"name\",\n \"--limit\",\n \"200\",\n ]);\n const labels: { name: string }[] = JSON.parse(stdout);\n const names = new Set(labels.map((l) => l.name));\n return WORKFLOW_LABELS.every((wl) => names.has(wl));\n } catch {\n return false;\n }\n}\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 { configExists } from \"./config.js\";\nimport { checkLabelsExist } from \"./github.js\";\n\nexport interface RepoSetupState {\n gitInitialized: boolean;\n gentInitialized: boolean;\n hasRemote: boolean;\n hasLabels: boolean;\n}\n\nexport async function getRepoSetupState(): Promise<RepoSetupState> {\n // Step 1: Check git init\n let gitInitialized = false;\n try {\n await execa(\"git\", [\"rev-parse\", \"--git-dir\"]);\n gitInitialized = true;\n } catch {\n return { gitInitialized: false, gentInitialized: false, hasRemote: false, hasLabels: false };\n }\n\n // Step 2: Check gent config\n const gentInitialized = configExists();\n\n // Step 3: Check remote\n let hasRemote = false;\n try {\n const { stdout } = await execa(\"git\", [\"config\", \"--get\", \"remote.origin.url\"]);\n hasRemote = stdout.trim().length > 0;\n } catch {\n // No remote\n }\n if (!hasRemote) {\n return { gitInitialized, gentInitialized, hasRemote: false, hasLabels: false };\n }\n\n // Step 4: Check labels\n const hasLabels = await checkLabelsExist().catch(() => false);\n\n return { gitInitialized, gentInitialized, hasRemote, hasLabels };\n}\n\nexport async function getCurrentBranch(): Promise<string> {\n const { stdout } = await execa(\"git\", [\"branch\", \"--show-current\"]);\n return stdout.trim();\n}\n\nexport async function isOnMainBranch(): Promise<boolean> {\n const branch = await getCurrentBranch();\n return branch === \"main\" || branch === \"master\";\n}\n\nexport async function getDefaultBranch(): Promise<string> {\n try {\n const { stdout } = await execa(\"git\", [\n \"symbolic-ref\",\n \"refs/remotes/origin/HEAD\",\n ]);\n return stdout.trim().replace(\"refs/remotes/origin/\", \"\");\n } catch {\n // Fallback to checking if main or master exists\n try {\n await execa(\"git\", [\"rev-parse\", \"--verify\", \"main\"]);\n return \"main\";\n } catch {\n return \"master\";\n }\n }\n}\n\nexport async function branchExists(name: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"rev-parse\", \"--verify\", name]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function createBranch(name: string, from?: string): Promise<void> {\n if (from) {\n await execa(\"git\", [\"checkout\", \"-b\", name, from]);\n } else {\n await execa(\"git\", [\"checkout\", \"-b\", name]);\n }\n}\n\nexport async function checkoutBranch(name: string): Promise<void> {\n await execa(\"git\", [\"checkout\", name]);\n}\n\nexport async function hasUncommittedChanges(): Promise<boolean> {\n const { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"]);\n return stdout.trim().length > 0;\n}\n\nexport async function getUnpushedCommits(): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git\", [\"log\", \"@{u}..HEAD\", \"--oneline\"]);\n return stdout.trim().length > 0;\n } catch {\n // No upstream set\n return true;\n }\n}\n\nexport async function pushBranch(branch?: string): Promise<void> {\n const branchName = branch || (await getCurrentBranch());\n await execa(\"git\", [\"push\", \"-u\", \"origin\", branchName]);\n}\n\nexport async function getAuthorInitials(): Promise<string> {\n // Try git config user.initials first\n try {\n const { stdout } = await execa(\"git\", [\"config\", \"user.initials\"]);\n if (stdout.trim()) {\n return stdout.trim();\n }\n } catch {\n // Not set, continue\n }\n\n // Fall back to deriving from user.name\n try {\n const { stdout } = await execa(\"git\", [\"config\", \"user.name\"]);\n const name = stdout.trim();\n if (name) {\n // Extract initials from name (e.g., \"John Doe\" -> \"jd\")\n const parts = name.split(/\\s+/);\n return parts.map((p) => p[0]?.toLowerCase() || \"\").join(\"\");\n }\n } catch {\n // Not set\n }\n\n return \"dev\";\n}\n\nexport async function getRepoInfo(): Promise<{\n owner: string;\n repo: string;\n} | null> {\n try {\n const { stdout } = await execa(\"git\", [\n \"config\",\n \"--get\",\n \"remote.origin.url\",\n ]);\n const url = stdout.trim();\n\n // Handle SSH format: git@github.com:owner/repo.git\n const sshMatch = url.match(/git@github\\.com:([^/]+)\\/([^.]+)/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2] };\n }\n\n // Handle HTTPS format: https://github.com/owner/repo.git\n const httpsMatch = url.match(/github\\.com\\/([^/]+)\\/([^.]+)/);\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2] };\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\nexport async function getCommitsSinceBase(\n base: string = \"main\"\n): Promise<string[]> {\n try {\n const { stdout } = await execa(\"git\", [\n \"log\",\n `${base}..HEAD`,\n \"--pretty=format:%s\",\n ]);\n return stdout.trim().split(\"\\n\").filter(Boolean);\n } catch {\n return [];\n }\n}\n\nexport async function getDiffSummary(base: string = \"main\"): Promise<string> {\n try {\n const { stdout } = await execa(\"git\", [\"diff\", `${base}...HEAD`, \"--stat\"]);\n return stdout.trim();\n } catch {\n return \"\";\n }\n}\n\nexport async function getCurrentCommitSha(): Promise<string> {\n const { stdout } = await execa(\"git\", [\"rev-parse\", \"HEAD\"]);\n return stdout.trim();\n}\n\nexport async function hasNewCommits(beforeSha: string): Promise<boolean> {\n const currentSha = await getCurrentCommitSha();\n return currentSha !== beforeSha;\n}\n\nexport async function getLastCommitTimestamp(): Promise<string> {\n const { stdout } = await execa(\"git\", [\"log\", \"-1\", \"--format=%cI\"]);\n return stdout.trim();\n}\n\nexport async function listLocalBranches(): Promise<string[]> {\n const { stdout } = await execa(\"git\", [\n \"branch\",\n \"--format=%(refname:short)\",\n ]);\n return stdout.trim().split(\"\\n\").filter(Boolean);\n}\n\nexport async function remoteBranchExists(name: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"ls-remote\", \"--exit-code\", \"--heads\", \"origin\", name]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function fetchAndCheckout(name: string): Promise<void> {\n await execa(\"git\", [\"fetch\", \"origin\", `${name}:${name}`]);\n await execa(\"git\", [\"checkout\", name]);\n}\n"],"mappings":"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","names":["execa","execa","execa"]}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getRepoInfo,
|
|
7
7
|
loadConfig,
|
|
8
8
|
logger
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ENNNKNLI.js";
|
|
10
10
|
|
|
11
11
|
// src/utils/spinner.ts
|
|
12
12
|
import ora from "ora";
|
|
@@ -280,4 +280,4 @@ export {
|
|
|
280
280
|
sortByPriority,
|
|
281
281
|
setupLabelsCommand
|
|
282
282
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
283
|
+
//# sourceMappingURL=chunk-MRF5FZO6.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
checkGhAuth,
|
|
4
4
|
getCurrentBranch,
|
|
5
5
|
logger
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ENNNKNLI.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/github-remote.ts
|
|
9
9
|
import { execa } from "execa";
|
|
@@ -40,4 +40,4 @@ async function githubRemoteCommand() {
|
|
|
40
40
|
export {
|
|
41
41
|
githubRemoteCommand
|
|
42
42
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
43
|
+
//# sourceMappingURL=chunk-PHZJIEY6.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
githubRemoteCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PHZJIEY6.js";
|
|
5
5
|
import {
|
|
6
6
|
aiSpinnerText,
|
|
7
7
|
buildIssueLabels,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
setupLabelsCommand,
|
|
12
12
|
sortByPriority,
|
|
13
13
|
withSpinner
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-MRF5FZO6.js";
|
|
15
15
|
import {
|
|
16
16
|
addIssueComment,
|
|
17
17
|
addPrComment,
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
sanitizeSlug,
|
|
64
64
|
setRuntimeProvider,
|
|
65
65
|
updateIssueLabels
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-ENNNKNLI.js";
|
|
67
67
|
|
|
68
68
|
// src/index.ts
|
|
69
69
|
import { Command } from "commander";
|
|
@@ -285,7 +285,7 @@ async function initCommand(options) {
|
|
|
285
285
|
}
|
|
286
286
|
]);
|
|
287
287
|
if (createRemote) {
|
|
288
|
-
const { githubRemoteCommand: githubRemoteCommand2 } = await import("./github-remote-
|
|
288
|
+
const { githubRemoteCommand: githubRemoteCommand2 } = await import("./github-remote-YD46C4M7.js");
|
|
289
289
|
const success = await githubRemoteCommand2();
|
|
290
290
|
if (success) {
|
|
291
291
|
const { setupLabels } = await inquirer.prompt([
|
|
@@ -297,7 +297,7 @@ async function initCommand(options) {
|
|
|
297
297
|
}
|
|
298
298
|
]);
|
|
299
299
|
if (setupLabels) {
|
|
300
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-
|
|
300
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-W4Z3EJ43.js");
|
|
301
301
|
await setupLabelsCommand2();
|
|
302
302
|
}
|
|
303
303
|
}
|
|
@@ -321,7 +321,7 @@ async function initCommand(options) {
|
|
|
321
321
|
}
|
|
322
322
|
]);
|
|
323
323
|
if (setupLabels) {
|
|
324
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-
|
|
324
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-W4Z3EJ43.js");
|
|
325
325
|
await setupLabelsCommand2();
|
|
326
326
|
}
|
|
327
327
|
}
|
|
@@ -334,6 +334,7 @@ import chalk from "chalk";
|
|
|
334
334
|
// src/lib/ai-provider.ts
|
|
335
335
|
import { spawn } from "child_process";
|
|
336
336
|
import { execa as execa2 } from "execa";
|
|
337
|
+
var AI_DEFAULT_TIMEOUT_MS = 3e4;
|
|
337
338
|
async function getOtherAvailableProviders(currentProvider) {
|
|
338
339
|
const allProviders = ["claude", "gemini", "codex"];
|
|
339
340
|
const others = allProviders.filter((p) => p !== currentProvider);
|
|
@@ -479,7 +480,8 @@ async function invokeClaudeInternal(options) {
|
|
|
479
480
|
child.on("error", reject);
|
|
480
481
|
});
|
|
481
482
|
} else {
|
|
482
|
-
const
|
|
483
|
+
const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
|
|
484
|
+
const { stdout } = await execa2("claude", args, { timeout });
|
|
483
485
|
return stdout;
|
|
484
486
|
}
|
|
485
487
|
}
|
|
@@ -523,7 +525,8 @@ async function invokeGeminiInternal(options) {
|
|
|
523
525
|
child.on("error", reject);
|
|
524
526
|
});
|
|
525
527
|
} else {
|
|
526
|
-
const
|
|
528
|
+
const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
|
|
529
|
+
const { stdout } = await execa2("gemini", args, { timeout });
|
|
527
530
|
return stdout;
|
|
528
531
|
}
|
|
529
532
|
}
|
|
@@ -566,7 +569,8 @@ async function invokeCodexInternal(options) {
|
|
|
566
569
|
child.on("error", reject);
|
|
567
570
|
});
|
|
568
571
|
} else {
|
|
569
|
-
const
|
|
572
|
+
const timeout = options.timeout ?? AI_DEFAULT_TIMEOUT_MS;
|
|
573
|
+
const { stdout } = await execa2("codex", args, { timeout });
|
|
570
574
|
return stdout;
|
|
571
575
|
}
|
|
572
576
|
}
|
|
@@ -694,21 +698,27 @@ ${issue ? `Closes #${issue.number}` : ""}
|
|
|
694
698
|
|
|
695
699
|
Only output the PR description, nothing else.`;
|
|
696
700
|
}
|
|
697
|
-
function
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
${
|
|
701
|
+
function buildCommitPrompt(issueNumber, issueTitle, config) {
|
|
702
|
+
const provider = config.ai.provider;
|
|
703
|
+
const providerName = getProviderDisplayName(provider);
|
|
704
|
+
const providerEmail = getProviderEmail(provider);
|
|
705
|
+
const issueContext = issueNumber ? `Related Issue: #${issueNumber}${issueTitle ? ` - ${issueTitle}` : ""}` : "No linked issue";
|
|
706
|
+
return `Create a git commit for the staged changes.
|
|
707
|
+
|
|
708
|
+
Context: ${issueContext}
|
|
705
709
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
-
|
|
711
|
-
-
|
|
710
|
+
Steps:
|
|
711
|
+
1. Run \`git diff --cached --stat\` to see what files changed
|
|
712
|
+
2. Run \`git diff --cached\` to see the actual changes
|
|
713
|
+
3. Generate a commit message following these rules:
|
|
714
|
+
- Use conventional commit format: <type>: <short description>
|
|
715
|
+
- Types: feat, fix, refactor, chore, docs, test, style, perf
|
|
716
|
+
- Keep the first line under 72 characters
|
|
717
|
+
- Add a blank line, then: Co-Authored-By: ${providerName} <${providerEmail}>
|
|
718
|
+
4. Run \`git commit -m "<your message>"\` to create the commit
|
|
719
|
+
5. Exit when done
|
|
720
|
+
|
|
721
|
+
Do not ask for confirmation - just create the commit.`;
|
|
712
722
|
}
|
|
713
723
|
function parseTicketMeta(output) {
|
|
714
724
|
const metaMatch = output.match(
|
|
@@ -1397,11 +1407,10 @@ async function runCommand(issueNumberArg, options) {
|
|
|
1397
1407
|
}
|
|
1398
1408
|
}
|
|
1399
1409
|
logger.newline();
|
|
1400
|
-
logger.
|
|
1401
|
-
"Issue
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
);
|
|
1410
|
+
logger.table("Run Summary", [
|
|
1411
|
+
{ key: "Issue", value: `${colors.issue(`#${issue.number}`)} ${issue.title}` },
|
|
1412
|
+
{ key: "Labels", value: issue.labels.map((l) => colors.label(l)).join(", ") }
|
|
1413
|
+
]);
|
|
1405
1414
|
logger.newline();
|
|
1406
1415
|
const type = extractTypeFromLabels(issue.labels);
|
|
1407
1416
|
const branchName = await generateBranchName(
|
|
@@ -1659,8 +1668,6 @@ async function prCommand(options) {
|
|
|
1659
1668
|
}
|
|
1660
1669
|
const currentBranch = await getCurrentBranch();
|
|
1661
1670
|
const baseBranch = await getDefaultBranch();
|
|
1662
|
-
logger.info(`Branch: ${colors.branch(currentBranch)}`);
|
|
1663
|
-
logger.info(`Base: ${colors.branch(baseBranch)}`);
|
|
1664
1671
|
const hasUnpushed = await getUnpushedCommits();
|
|
1665
1672
|
if (hasUnpushed) {
|
|
1666
1673
|
await withSpinner("Pushing branch...", async () => {
|
|
@@ -1673,9 +1680,6 @@ async function prCommand(options) {
|
|
|
1673
1680
|
if (issueNumber) {
|
|
1674
1681
|
try {
|
|
1675
1682
|
issue = await getIssue(issueNumber);
|
|
1676
|
-
logger.info(
|
|
1677
|
-
`Linked issue: ${colors.issue(`#${issueNumber}`)} - ${issue.title}`
|
|
1678
|
-
);
|
|
1679
1683
|
} catch {
|
|
1680
1684
|
logger.warning(`Could not fetch issue #${issueNumber}`);
|
|
1681
1685
|
}
|
|
@@ -1688,23 +1692,19 @@ async function prCommand(options) {
|
|
|
1688
1692
|
logger.error("No commits found since base branch.");
|
|
1689
1693
|
return;
|
|
1690
1694
|
}
|
|
1691
|
-
logger.info(`Commits: ${commits.length}`);
|
|
1692
|
-
logger.newline();
|
|
1693
1695
|
const shouldCaptureVideo = options.video !== false && config.video.enabled;
|
|
1694
1696
|
let captureVideoInstructions = "";
|
|
1697
|
+
let uiChangesDetected = false;
|
|
1698
|
+
let playwrightAvailable = false;
|
|
1695
1699
|
if (shouldCaptureVideo) {
|
|
1696
1700
|
const changedFiles = await getChangedFiles(baseBranch);
|
|
1697
|
-
|
|
1701
|
+
uiChangesDetected = hasUIChanges(changedFiles);
|
|
1698
1702
|
if (uiChangesDetected) {
|
|
1699
|
-
|
|
1700
|
-
const playwrightAvailable = await isPlaywrightAvailable();
|
|
1703
|
+
playwrightAvailable = await isPlaywrightAvailable();
|
|
1701
1704
|
if (!playwrightAvailable) {
|
|
1702
1705
|
logger.warning("Playwright not available. Skipping video capture.");
|
|
1703
1706
|
logger.dim("Install Playwright with: npm install -D playwright");
|
|
1704
1707
|
} else {
|
|
1705
|
-
logger.info(
|
|
1706
|
-
"Playwright available - AI will capture demo video via MCP"
|
|
1707
|
-
);
|
|
1708
1708
|
captureVideoInstructions = `
|
|
1709
1709
|
|
|
1710
1710
|
IMPORTANT: This PR contains UI changes. Use the Playwright MCP plugin to:
|
|
@@ -1716,6 +1716,18 @@ IMPORTANT: This PR contains UI changes. Use the Playwright MCP plugin to:
|
|
|
1716
1716
|
}
|
|
1717
1717
|
}
|
|
1718
1718
|
}
|
|
1719
|
+
logger.table("PR Summary", [
|
|
1720
|
+
{ key: "Branch", value: colors.branch(currentBranch) },
|
|
1721
|
+
{ key: "Base", value: colors.branch(baseBranch) },
|
|
1722
|
+
{
|
|
1723
|
+
key: "Issue",
|
|
1724
|
+
value: issue ? `${colors.issue(`#${issueNumber}`)} ${issue.title}` : colors.label("(none)")
|
|
1725
|
+
},
|
|
1726
|
+
{ key: "Commits", value: String(commits.length) },
|
|
1727
|
+
...uiChangesDetected ? [{ key: "UI Changes", value: "detected" }] : [],
|
|
1728
|
+
...playwrightAvailable ? [{ key: "Video", value: "Playwright MCP available" }] : []
|
|
1729
|
+
]);
|
|
1730
|
+
logger.newline();
|
|
1719
1731
|
const prompt = buildPrPrompt(issue, commits, diffSummary) + captureVideoInstructions;
|
|
1720
1732
|
let prBody;
|
|
1721
1733
|
let usedProvider = currentProvider;
|
|
@@ -2206,7 +2218,7 @@ import { homedir } from "os";
|
|
|
2206
2218
|
// package.json
|
|
2207
2219
|
var package_default = {
|
|
2208
2220
|
name: "@rotorsoft/gent",
|
|
2209
|
-
version: "1.
|
|
2221
|
+
version: "1.25.0",
|
|
2210
2222
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
2211
2223
|
keywords: [
|
|
2212
2224
|
"cli",
|
|
@@ -3629,7 +3641,6 @@ function showStatus(title, message, dashboardLines) {
|
|
|
3629
3641
|
}
|
|
3630
3642
|
|
|
3631
3643
|
// src/commands/tui.ts
|
|
3632
|
-
var CANCEL = /* @__PURE__ */ Symbol("cancel");
|
|
3633
3644
|
async function waitForKey(validKeys) {
|
|
3634
3645
|
return new Promise((resolve) => {
|
|
3635
3646
|
const { stdin } = process;
|
|
@@ -3746,13 +3757,6 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3746
3757
|
return false;
|
|
3747
3758
|
}
|
|
3748
3759
|
await execa4("git", ["add", "-A"]);
|
|
3749
|
-
const { stdout: diffStat } = await execa4("git", [
|
|
3750
|
-
"diff",
|
|
3751
|
-
"--cached",
|
|
3752
|
-
"--stat"
|
|
3753
|
-
]);
|
|
3754
|
-
const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
|
|
3755
|
-
const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
|
|
3756
3760
|
const issueNumber = state.issue?.number ?? null;
|
|
3757
3761
|
const issueTitle = state.issue?.title ?? null;
|
|
3758
3762
|
const provider = state.config.ai.provider;
|
|
@@ -3769,75 +3773,48 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3769
3773
|
await execa4("git", ["reset", "HEAD"]);
|
|
3770
3774
|
return false;
|
|
3771
3775
|
}
|
|
3772
|
-
let message;
|
|
3773
3776
|
if (mode === "manual") {
|
|
3774
3777
|
const input = await showInput({
|
|
3775
3778
|
title: "Commit Message",
|
|
3776
3779
|
label: "Enter commit message:",
|
|
3777
3780
|
dashboardLines
|
|
3778
3781
|
});
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
issueTitle,
|
|
3786
|
-
state,
|
|
3787
|
-
dashboardLines
|
|
3788
|
-
);
|
|
3789
|
-
}
|
|
3790
|
-
if (message === CANCEL) {
|
|
3791
|
-
await execa4("git", ["reset", "HEAD"]);
|
|
3792
|
-
return false;
|
|
3793
|
-
}
|
|
3794
|
-
const confirmed = await showConfirm({
|
|
3795
|
-
title: "Commit",
|
|
3796
|
-
message: `Message: ${message.length > 50 ? message.slice(0, 50) + "\u2026" : message}`,
|
|
3797
|
-
dashboardLines
|
|
3798
|
-
});
|
|
3799
|
-
if (!confirmed) {
|
|
3800
|
-
await execa4("git", ["reset", "HEAD"]);
|
|
3801
|
-
return false;
|
|
3802
|
-
}
|
|
3803
|
-
const providerEmail = getProviderEmail(provider);
|
|
3804
|
-
const fullMessage = `${message}
|
|
3782
|
+
if (!input) {
|
|
3783
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3784
|
+
return false;
|
|
3785
|
+
}
|
|
3786
|
+
const providerEmail = getProviderEmail(provider);
|
|
3787
|
+
const fullMessage = `${input}
|
|
3805
3788
|
|
|
3806
3789
|
Co-Authored-By: ${providerName} <${providerEmail}>`;
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3790
|
+
showStatus("Committing", "Committing changes...", dashboardLines);
|
|
3791
|
+
await execa4("git", ["commit", "-m", fullMessage]);
|
|
3792
|
+
return true;
|
|
3793
|
+
} else {
|
|
3794
|
+
const prompt = buildCommitPrompt(issueNumber, issueTitle, state.config);
|
|
3795
|
+
clearScreen();
|
|
3796
|
+
logger.table("Commit Summary", [
|
|
3797
|
+
{
|
|
3798
|
+
key: "Issue",
|
|
3799
|
+
value: issueNumber ? `${colors.issue(`#${issueNumber}`)} ${issueTitle ?? ""}` : "No linked issue"
|
|
3800
|
+
},
|
|
3801
|
+
{ key: "Provider", value: colors.provider(providerName) }
|
|
3802
|
+
]);
|
|
3803
|
+
logger.newline();
|
|
3804
|
+
try {
|
|
3805
|
+
const { result } = await invokeAIInteractive(prompt, state.config);
|
|
3806
|
+
await result;
|
|
3807
|
+
return true;
|
|
3808
|
+
} catch (error) {
|
|
3809
|
+
logger.error(`${providerName} commit failed: ${error}`);
|
|
3810
|
+
return false;
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3810
3813
|
} catch (error) {
|
|
3811
3814
|
logger.error(`Commit failed: ${error}`);
|
|
3812
3815
|
return false;
|
|
3813
3816
|
}
|
|
3814
3817
|
}
|
|
3815
|
-
async function generateCommitMessage(diffContent, issueNumber, issueTitle, state, dashboardLines) {
|
|
3816
|
-
try {
|
|
3817
|
-
const prompt = buildCommitMessagePrompt(
|
|
3818
|
-
diffContent,
|
|
3819
|
-
issueNumber,
|
|
3820
|
-
issueTitle
|
|
3821
|
-
);
|
|
3822
|
-
const result = await invokeAI({ prompt, streamOutput: false }, state.config);
|
|
3823
|
-
let message = result.output.trim().split("\n")[0].trim();
|
|
3824
|
-
for (const q of ['"', "'", "`"]) {
|
|
3825
|
-
if (message.startsWith(q) && message.endsWith(q)) {
|
|
3826
|
-
message = message.slice(1, -1);
|
|
3827
|
-
break;
|
|
3828
|
-
}
|
|
3829
|
-
}
|
|
3830
|
-
message = message.replace(/^```\w*\s*/, "").replace(/\s*```$/, "");
|
|
3831
|
-
return message;
|
|
3832
|
-
} catch {
|
|
3833
|
-
const input = await showInput({
|
|
3834
|
-
title: "Commit Message",
|
|
3835
|
-
label: "AI generation failed. Enter commit message:",
|
|
3836
|
-
dashboardLines
|
|
3837
|
-
});
|
|
3838
|
-
return input || CANCEL;
|
|
3839
|
-
}
|
|
3840
|
-
}
|
|
3841
3818
|
async function handleRun(state) {
|
|
3842
3819
|
if (!state.issue) {
|
|
3843
3820
|
logger.error("No linked issue found");
|