@rotorsoft/gent 1.20.0 → 1.21.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/README.md +4 -0
- package/dist/{chunk-JHIW57FY.js → chunk-75KVN7ZC.js} +41 -8
- package/dist/chunk-75KVN7ZC.js.map +1 -0
- package/dist/index.js +116 -51
- package/dist/index.js.map +1 -1
- package/dist/{setup-labels-LB7X6A3K.js → setup-labels-STWAFV2E.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-JHIW57FY.js.map +0 -1
- /package/dist/{setup-labels-LB7X6A3K.js.map → setup-labels-STWAFV2E.js.map} +0 -0
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
AI-powered GitHub workflow tool - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs from an interactive dashboard.
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
https://github.com/user-attachments/assets/c18fe01a-9695-4e8a-bf27-bb54f27247ef
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
## Overview
|
|
6
10
|
|
|
7
11
|
`gent` provides an interactive dashboard that integrates AI with GitHub to automate your development workflow. Just run `gent` to launch:
|
|
@@ -56,6 +56,9 @@ var colors = {
|
|
|
56
56
|
|
|
57
57
|
// src/utils/spinner.ts
|
|
58
58
|
import ora from "ora";
|
|
59
|
+
function aiSpinnerText(provider, action) {
|
|
60
|
+
return `Waiting for ${provider} to ${action}...`;
|
|
61
|
+
}
|
|
59
62
|
function createSpinner(text) {
|
|
60
63
|
return ora({
|
|
61
64
|
text,
|
|
@@ -505,6 +508,29 @@ function sortByPriority(issues) {
|
|
|
505
508
|
|
|
506
509
|
// src/lib/github.ts
|
|
507
510
|
import { execa } from "execa";
|
|
511
|
+
var WORKFLOW_LABELS = [
|
|
512
|
+
"ai-ready",
|
|
513
|
+
"ai-in-progress",
|
|
514
|
+
"ai-completed",
|
|
515
|
+
"ai-blocked"
|
|
516
|
+
];
|
|
517
|
+
async function checkLabelsExist() {
|
|
518
|
+
try {
|
|
519
|
+
const { stdout } = await execa("gh", [
|
|
520
|
+
"label",
|
|
521
|
+
"list",
|
|
522
|
+
"--json",
|
|
523
|
+
"name",
|
|
524
|
+
"--limit",
|
|
525
|
+
"200"
|
|
526
|
+
]);
|
|
527
|
+
const labels = JSON.parse(stdout);
|
|
528
|
+
const names = new Set(labels.map((l) => l.name));
|
|
529
|
+
return WORKFLOW_LABELS.every((wl) => names.has(wl));
|
|
530
|
+
} catch {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
508
534
|
async function getIssue(issueNumber) {
|
|
509
535
|
const { stdout } = await execa("gh", [
|
|
510
536
|
"issue",
|
|
@@ -836,6 +862,9 @@ async function checkGitRepo() {
|
|
|
836
862
|
return false;
|
|
837
863
|
}
|
|
838
864
|
}
|
|
865
|
+
function checkInitialized(cwd) {
|
|
866
|
+
return configExists(cwd);
|
|
867
|
+
}
|
|
839
868
|
function isValidIssueNumber(value) {
|
|
840
869
|
const num = parseInt(value, 10);
|
|
841
870
|
return !isNaN(num) && num > 0;
|
|
@@ -882,13 +911,6 @@ async function setupLabelsCommand() {
|
|
|
882
911
|
export {
|
|
883
912
|
logger,
|
|
884
913
|
colors,
|
|
885
|
-
checkGhAuth,
|
|
886
|
-
checkClaudeCli,
|
|
887
|
-
checkGeminiCli,
|
|
888
|
-
checkAIProvider,
|
|
889
|
-
checkGitRepo,
|
|
890
|
-
isValidIssueNumber,
|
|
891
|
-
sanitizeSlug,
|
|
892
914
|
setRuntimeProvider,
|
|
893
915
|
resolveProvider,
|
|
894
916
|
getConfigPath,
|
|
@@ -896,11 +918,22 @@ export {
|
|
|
896
918
|
loadAgentInstructions,
|
|
897
919
|
configExists,
|
|
898
920
|
generateDefaultConfig,
|
|
921
|
+
checkGhAuth,
|
|
922
|
+
checkClaudeCli,
|
|
923
|
+
checkGeminiCli,
|
|
924
|
+
checkAIProvider,
|
|
925
|
+
checkGitRepo,
|
|
926
|
+
checkInitialized,
|
|
927
|
+
isValidIssueNumber,
|
|
928
|
+
sanitizeSlug,
|
|
929
|
+
aiSpinnerText,
|
|
930
|
+
createSpinner,
|
|
899
931
|
withSpinner,
|
|
900
932
|
getWorkflowLabels,
|
|
901
933
|
buildIssueLabels,
|
|
902
934
|
extractTypeFromLabels,
|
|
903
935
|
sortByPriority,
|
|
936
|
+
checkLabelsExist,
|
|
904
937
|
getIssue,
|
|
905
938
|
listIssues,
|
|
906
939
|
createIssue,
|
|
@@ -917,4 +950,4 @@ export {
|
|
|
917
950
|
listOpenPrs,
|
|
918
951
|
setupLabelsCommand
|
|
919
952
|
};
|
|
920
|
-
//# sourceMappingURL=chunk-
|
|
953
|
+
//# sourceMappingURL=chunk-75KVN7ZC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/utils/spinner.ts","../src/lib/config.ts","../src/types/index.ts","../src/lib/labels.ts","../src/lib/github.ts","../src/utils/validators.ts","../src/commands/setup-labels.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nexport const logger = {\n info: (message: string) => console.log(chalk.blue(\"ℹ\"), message),\n success: (message: string) => console.log(chalk.green(\"✓\"), message),\n warning: (message: string) => console.log(chalk.yellow(\"⚠\"), message),\n error: (message: string) => console.log(chalk.red(\"✗\"), message),\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray(\"⋯\"), message);\n }\n },\n dim: (message: string) => console.log(chalk.dim(message)),\n bold: (message: string) => console.log(chalk.bold(message)),\n highlight: (message: string) => console.log(chalk.cyan(message)),\n\n box: (title: string, content: string) => {\n const lines = content.split(\"\\n\");\n // Calculate visible length (strips ANSI codes) for proper alignment\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n const visibleLength = (str: string) => stripAnsi(str).length;\n const maxLen =\n Math.max(title.length, ...lines.map((l) => visibleLength(l))) + 4;\n const border = \"─\".repeat(maxLen);\n\n // Pad string to target length accounting for ANSI codes\n const padVisible = (str: string, len: number) => {\n const visible = visibleLength(str);\n return str + \" \".repeat(Math.max(0, len - visible));\n };\n\n console.log(chalk.dim(`┌${border}┐`));\n console.log(\n `${chalk.dim(\"│\")} ${chalk.bold(title.padEnd(maxLen - 2))} ${chalk.dim(\"│\")}`\n );\n console.log(chalk.dim(`├${border}┤`));\n for (const line of lines) {\n console.log(\n `${chalk.dim(\"│\")} ${padVisible(line, maxLen - 2)} ${chalk.dim(\"│\")}`\n );\n }\n console.log(chalk.dim(`└${border}┘`));\n },\n\n list: (items: string[], bullet = \"•\") => {\n for (const item of items) {\n console.log(chalk.dim(bullet), item);\n }\n },\n\n newline: () => console.log(),\n};\n\nexport const colors = {\n issue: chalk.cyan,\n branch: chalk.magenta,\n label: chalk.yellow,\n file: chalk.green,\n command: chalk.blue,\n url: chalk.underline.blue,\n provider: chalk.cyan.bold,\n};\n","import ora, { Ora } from \"ora\";\n\nexport function aiSpinnerText(provider: string, action: string): string {\n return `Waiting for ${provider} to ${action}...`;\n}\n\nexport function createSpinner(text: string): Ora {\n return ora({\n text,\n spinner: \"dots\",\n });\n}\n\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>\n): Promise<T> {\n const spinner = createSpinner(text);\n spinner.start();\n\n try {\n const result = await fn();\n spinner.succeed();\n return result;\n } catch (error) {\n spinner.fail();\n throw error;\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { GentConfig, AIProvider } from \"../types/index.js\";\n\n// Module-level variable to hold runtime provider override (e.g. from TUI)\nlet runtimeProvider: AIProvider | null = null;\n\nexport function setRuntimeProvider(provider: AIProvider): void {\n runtimeProvider = provider;\n}\n\n/**\n * Helper to resolve the active provider based on precedence:\n * 1. CLI options (explicit flag)\n * 2. Runtime override (in-memory state)\n * 3. Environment variable (GENT_AI_PROVIDER)\n * 4. Configuration (file)\n * 5. Default\n */\nexport function resolveProvider(\n options: { provider?: AIProvider } | undefined,\n config: GentConfig\n): AIProvider {\n return options?.provider ?? config.ai.provider;\n}\n\nconst DEFAULT_CONFIG: GentConfig = {\n version: 1,\n github: {\n labels: {\n workflow: {\n ready: \"ai-ready\",\n in_progress: \"ai-in-progress\",\n completed: \"ai-completed\",\n blocked: \"ai-blocked\",\n },\n types: [\"feature\", \"fix\", \"refactor\", \"chore\", \"docs\", \"test\"],\n priorities: [\"critical\", \"high\", \"medium\", \"low\"],\n risks: [\"low\", \"medium\", \"high\"],\n areas: [\"ui\", \"api\", \"database\", \"workers\", \"shared\", \"testing\", \"infra\"],\n },\n },\n branch: {\n pattern: \"{author}/{type}-{issue}-{slug}\",\n author_source: \"git\",\n author_env_var: \"GENT_AUTHOR\",\n },\n progress: {\n file: \"progress.txt\",\n archive_threshold: 500,\n archive_dir: \".gent/archive\",\n },\n claude: {\n permission_mode: \"acceptEdits\",\n agent_file: \"AGENT.md\",\n },\n gemini: {\n sandbox_mode: \"on\",\n agent_file: \"AGENT.md\",\n },\n codex: {\n agent_file: \"AGENT.md\",\n },\n ai: {\n provider: \"claude\",\n auto_fallback: true,\n },\n video: {\n enabled: true,\n max_duration: 30,\n width: 1280,\n height: 720,\n },\n validation: [\"npm run typecheck\", \"npm run lint\", \"npm run test\"],\n};\n\nexport function getConfigPath(cwd: string = process.cwd()): string {\n return join(cwd, \".gent.yml\");\n}\n\nexport function getAgentPath(cwd: string = process.cwd()): string | null {\n const config = loadConfig(cwd);\n // Use claude.agent_file for backward compatibility\n const agentPath = join(cwd, config.claude.agent_file);\n return existsSync(agentPath) ? agentPath : null;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): GentConfig {\n const configPath = getConfigPath(cwd);\n\n if (!existsSync(configPath)) {\n return DEFAULT_CONFIG;\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const userConfig = parseYaml(content) as Partial<GentConfig>;\n\n return mergeConfig(DEFAULT_CONFIG, userConfig);\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport function loadAgentInstructions(\n cwd: string = process.cwd()\n): string | null {\n const agentPath = getAgentPath(cwd);\n\n if (!agentPath) {\n return null;\n }\n\n try {\n return readFileSync(agentPath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport function configExists(cwd: string = process.cwd()): boolean {\n return existsSync(getConfigPath(cwd));\n}\n\nfunction mergeConfig(\n defaults: GentConfig,\n user: Partial<GentConfig>\n): GentConfig {\n // Support GENT_AI_PROVIDER environment variable override\n const envProvider = process.env.GENT_AI_PROVIDER as\n | \"claude\"\n | \"gemini\"\n | \"codex\"\n | undefined;\n\n // Runtime override takes precedence over env var\n const effectiveProvider = runtimeProvider ?? envProvider;\n\n return {\n version: user.version ?? defaults.version,\n github: {\n labels: {\n workflow: {\n ...defaults.github.labels.workflow,\n ...user.github?.labels?.workflow,\n },\n types: user.github?.labels?.types ?? defaults.github.labels.types,\n priorities:\n user.github?.labels?.priorities ?? defaults.github.labels.priorities,\n risks: user.github?.labels?.risks ?? defaults.github.labels.risks,\n areas: user.github?.labels?.areas ?? defaults.github.labels.areas,\n },\n },\n branch: {\n ...defaults.branch,\n ...user.branch,\n },\n progress: {\n ...defaults.progress,\n ...user.progress,\n },\n claude: {\n ...defaults.claude,\n ...user.claude,\n },\n gemini: {\n ...defaults.gemini,\n ...user.gemini,\n },\n codex: {\n ...defaults.codex,\n ...user.codex,\n },\n ai: {\n ...defaults.ai,\n ...user.ai,\n // Runtime/Env takes precedence\n ...(effectiveProvider && { provider: effectiveProvider }),\n },\n video: {\n ...defaults.video,\n ...user.video,\n },\n validation: user.validation ?? defaults.validation,\n };\n}\n\nexport function updateConfigProvider(\n provider: AIProvider,\n cwd: string = process.cwd()\n): void {\n const configPath = getConfigPath(cwd);\n if (!existsSync(configPath)) {\n writeFileSync(configPath, generateDefaultConfig(provider), \"utf-8\");\n return;\n }\n const content = readFileSync(configPath, \"utf-8\");\n const updated = content.replace(\n /^(\\s*provider:\\s*)\"[^\"]*\"/m,\n `$1\"${provider}\"`\n );\n writeFileSync(configPath, updated, \"utf-8\");\n}\n\nexport function generateDefaultConfig(provider: AIProvider = \"claude\"): string {\n return `# Gent Configuration\n# See https://github.com/rotorsoft/gent for documentation\nversion: 1\n\n# GitHub settings\ngithub:\n labels:\n workflow:\n ready: \"ai-ready\"\n in_progress: \"ai-in-progress\"\n completed: \"ai-completed\"\n blocked: \"ai-blocked\"\n types:\n - feature\n - fix\n - refactor\n - chore\n - docs\n - test\n priorities:\n - critical\n - high\n - medium\n - low\n risks:\n - low\n - medium\n - high\n areas:\n - ui\n - api\n - database\n - workers\n - shared\n - testing\n - infra\n\n# Branch naming convention\nbranch:\n pattern: \"{author}/{type}-{issue}-{slug}\"\n author_source: \"git\" # git | env | prompt\n author_env_var: \"GENT_AUTHOR\"\n\n# Progress tracking\nprogress:\n file: \"progress.txt\"\n archive_threshold: 500\n archive_dir: \".gent/archive\"\n\n# Claude settings\nclaude:\n permission_mode: \"acceptEdits\"\n agent_file: \"AGENT.md\"\n\n# Gemini settings\ngemini:\n sandbox_mode: \"on\"\n agent_file: \"AGENT.md\"\n\n# Codex settings\ncodex:\n agent_file: \"AGENT.md\"\n\n# AI provider settings\nai:\n provider: \"${provider}\" # claude | gemini | codex\n # fallback_provider: \"gemini\" # optional fallback when rate limited\n auto_fallback: true # automatically switch to fallback on rate limit\n\n# Video capture for UI changes (requires Playwright)\nvideo:\n enabled: true # set to false to disable video capture for PRs with UI changes\n max_duration: 30 # maximum video duration in seconds\n width: 1280 # video width\n height: 720 # video height\n\n# Validation commands (run before commit)\nvalidation:\n - \"npm run typecheck\"\n - \"npm run lint\"\n - \"npm run test\"\n`;\n}\n","export type AIProvider = \"claude\" | \"gemini\" | \"codex\";\n\nexport interface GentConfig {\n version: number;\n github: GitHubConfig;\n branch: BranchConfig;\n progress: ProgressConfig;\n claude: ClaudeConfig;\n gemini: GeminiConfig;\n codex: CodexConfig;\n ai: AIConfig;\n video: VideoConfig;\n validation: string[];\n}\n\nexport interface VideoConfig {\n enabled: boolean;\n max_duration: number; // seconds\n width: number;\n height: number;\n}\n\nexport interface AIConfig {\n provider: AIProvider;\n fallback_provider?: AIProvider;\n auto_fallback: boolean;\n}\n\nexport interface GitHubConfig {\n labels: {\n workflow: WorkflowLabels;\n types: string[];\n priorities: string[];\n risks: string[];\n areas: string[];\n };\n}\n\nexport interface WorkflowLabels {\n ready: string;\n in_progress: string;\n completed: string;\n blocked: string;\n}\n\nexport interface BranchConfig {\n pattern: string;\n author_source: \"git\" | \"env\" | \"prompt\";\n author_env_var: string;\n}\n\nexport interface ProgressConfig {\n file: string;\n archive_threshold: number;\n archive_dir: string;\n}\n\nexport interface ClaudeConfig {\n permission_mode: string;\n agent_file: string;\n}\n\nexport interface GeminiConfig {\n sandbox_mode: string;\n agent_file: string;\n}\n\nexport interface CodexConfig {\n agent_file: string;\n}\n\nexport interface GitHubIssue {\n number: number;\n title: string;\n body: string;\n labels: string[];\n state: \"open\" | \"closed\";\n assignee?: string;\n url: string;\n}\n\nexport interface GitHubLabel {\n name: string;\n color: string;\n description?: string;\n}\n\nexport interface GitHubReviewComment {\n id?: number;\n author: string;\n body: string;\n path?: string;\n line?: number | null;\n createdAt?: string;\n}\n\nexport interface GitHubReview {\n author: string;\n body: string;\n state: string;\n submittedAt?: string;\n}\n\nexport interface GitHubReviewThread {\n isResolved?: boolean | null;\n isOutdated?: boolean;\n path?: string;\n line?: number | null;\n comments: GitHubReviewComment[];\n}\n\nexport interface GitHubPRComment {\n id?: string;\n author: string;\n body: string;\n createdAt?: string;\n}\n\nexport interface GitHubReviewData {\n reviews: GitHubReview[];\n reviewThreads: GitHubReviewThread[];\n comments: GitHubPRComment[];\n}\n\nexport interface ProgressEntry {\n date: string;\n type: string;\n description: string;\n issue?: number;\n decisions: string[];\n files: string[];\n tests: string[];\n concerns: string[];\n followUp: string[];\n commit?: string;\n}\n\nexport interface BranchInfo {\n name: string;\n author: string;\n type: string;\n issueNumber: number;\n slug: string;\n}\n\nexport const DEFAULT_LABELS: Record<string, GitHubLabel[]> = {\n workflow: [\n {\n name: \"ai-ready\",\n color: \"0E8A16\",\n description: \"Issue ready for AI implementation\",\n },\n {\n name: \"ai-in-progress\",\n color: \"FFA500\",\n description: \"AI currently working on this\",\n },\n {\n name: \"ai-completed\",\n color: \"1D76DB\",\n description: \"AI done, needs human review\",\n },\n {\n name: \"ai-blocked\",\n color: \"D93F0B\",\n description: \"AI couldn't complete, needs help\",\n },\n ],\n priority: [\n {\n name: \"priority:critical\",\n color: \"B60205\",\n description: \"Blocking production\",\n },\n {\n name: \"priority:high\",\n color: \"D93F0B\",\n description: \"Important features/bugs\",\n },\n {\n name: \"priority:medium\",\n color: \"FBCA04\",\n description: \"Nice-to-have improvements\",\n },\n { name: \"priority:low\", color: \"0E8A16\", description: \"Minor tweaks\" },\n ],\n risk: [\n {\n name: \"risk:low\",\n color: \"C2E0C6\",\n description: \"UI changes, tests, non-critical\",\n },\n {\n name: \"risk:medium\",\n color: \"FEF2C0\",\n description: \"API changes, new features\",\n },\n {\n name: \"risk:high\",\n color: \"F9D0C4\",\n description: \"Migrations, auth, security\",\n },\n ],\n type: [\n { name: \"type:feature\", color: \"1D76DB\", description: \"New feature\" },\n { name: \"type:fix\", color: \"D73A4A\", description: \"Bug fix\" },\n {\n name: \"type:refactor\",\n color: \"5319E7\",\n description: \"Code improvement\",\n },\n { name: \"type:chore\", color: \"FEF2C0\", description: \"Maintenance\" },\n { name: \"type:docs\", color: \"0075CA\", description: \"Documentation\" },\n { name: \"type:test\", color: \"D4C5F9\", description: \"Testing\" },\n ],\n area: [\n { name: \"area:ui\", color: \"C5DEF5\", description: \"User interface\" },\n { name: \"area:api\", color: \"D4C5F9\", description: \"API/Backend\" },\n { name: \"area:database\", color: \"FEF2C0\", description: \"Database/Models\" },\n {\n name: \"area:workers\",\n color: \"F9D0C4\",\n description: \"Background workers\",\n },\n { name: \"area:shared\", color: \"C2E0C6\", description: \"Shared libraries\" },\n {\n name: \"area:testing\",\n color: \"E99695\",\n description: \"Test infrastructure\",\n },\n {\n name: \"area:infra\",\n color: \"BFD4F2\",\n description: \"Infrastructure/DevOps\",\n },\n ],\n};\n","import type { GentConfig, GitHubLabel } from \"../types/index.js\";\nimport { DEFAULT_LABELS } from \"../types/index.js\";\n\nexport function getAllLabels(config: GentConfig): GitHubLabel[] {\n const labels: GitHubLabel[] = [];\n\n // Workflow labels\n labels.push(...DEFAULT_LABELS.workflow);\n\n // Priority labels\n for (const priority of config.github.labels.priorities) {\n const defaultLabel = DEFAULT_LABELS.priority.find(\n (l) => l.name === `priority:${priority}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `priority:${priority}`,\n color: \"FBCA04\",\n description: `Priority: ${priority}`,\n });\n }\n }\n\n // Risk labels\n for (const risk of config.github.labels.risks) {\n const defaultLabel = DEFAULT_LABELS.risk.find(\n (l) => l.name === `risk:${risk}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `risk:${risk}`,\n color: \"FEF2C0\",\n description: `Risk: ${risk}`,\n });\n }\n }\n\n // Type labels\n for (const type of config.github.labels.types) {\n const defaultLabel = DEFAULT_LABELS.type.find(\n (l) => l.name === `type:${type}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `type:${type}`,\n color: \"1D76DB\",\n description: `Type: ${type}`,\n });\n }\n }\n\n // Area labels\n for (const area of config.github.labels.areas) {\n const defaultLabel = DEFAULT_LABELS.area.find(\n (l) => l.name === `area:${area}`\n );\n if (defaultLabel) {\n labels.push(defaultLabel);\n } else {\n labels.push({\n name: `area:${area}`,\n color: \"C5DEF5\",\n description: `Area: ${area}`,\n });\n }\n }\n\n return labels;\n}\n\nexport function getWorkflowLabels(config: GentConfig): {\n ready: string;\n inProgress: string;\n completed: string;\n blocked: string;\n} {\n return {\n ready: config.github.labels.workflow.ready,\n inProgress: config.github.labels.workflow.in_progress,\n completed: config.github.labels.workflow.completed,\n blocked: config.github.labels.workflow.blocked,\n };\n}\n\nexport function buildIssueLabels(meta: {\n type: string;\n priority: string;\n risk: string;\n area: string;\n}): string[] {\n return [\n \"ai-ready\",\n `type:${meta.type}`,\n `priority:${meta.priority}`,\n `risk:${meta.risk}`,\n `area:${meta.area}`,\n ];\n}\n\nexport function extractTypeFromLabels(labels: string[]): string {\n for (const label of labels) {\n if (label.startsWith(\"type:\")) {\n return label.replace(\"type:\", \"\");\n }\n }\n return \"feature\";\n}\n\nexport function extractPriorityFromLabels(labels: string[]): string {\n for (const label of labels) {\n if (label.startsWith(\"priority:\")) {\n return label.replace(\"priority:\", \"\");\n }\n }\n return \"medium\";\n}\n\nexport function hasWorkflowLabel(\n labels: string[],\n workflowLabel: string\n): boolean {\n return labels.includes(workflowLabel);\n}\n\nexport function sortByPriority(issues: { labels: string[] }[]): void {\n const priorityOrder = [\"critical\", \"high\", \"medium\", \"low\"];\n\n issues.sort((a, b) => {\n const aPriority = extractPriorityFromLabels(a.labels);\n const bPriority = extractPriorityFromLabels(b.labels);\n return priorityOrder.indexOf(aPriority) - priorityOrder.indexOf(bPriority);\n });\n}\n","import { execa } from \"execa\";\nimport type {\n GitHubIssue,\n GitHubLabel,\n GitHubReviewData,\n} from \"../types/index.js\";\n\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 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 { logger, colors } from \"../utils/logger.js\";\nimport { withSpinner } from \"../utils/spinner.js\";\nimport { loadConfig } from \"../lib/config.js\";\nimport { getAllLabels } from \"../lib/labels.js\";\nimport { createLabel } from \"../lib/github.js\";\nimport { checkGhAuth } from \"../utils/validators.js\";\n\nexport async function setupLabelsCommand(): Promise<void> {\n logger.bold(\"Setting up GitHub labels...\");\n logger.newline();\n\n // Check gh auth\n const isAuthed = await checkGhAuth();\n if (!isAuthed) {\n logger.error(\"Not authenticated with GitHub. Run 'gh auth login' first.\");\n process.exit(1);\n }\n\n const config = loadConfig();\n const labels = getAllLabels(config);\n\n logger.info(`Creating ${labels.length} labels...`);\n logger.newline();\n\n let created = 0;\n let failed = 0;\n\n for (const label of labels) {\n try {\n await withSpinner(`Creating ${colors.label(label.name)}`, async () => {\n await createLabel(label);\n });\n created++;\n } catch (error) {\n logger.error(`Failed to create ${label.name}: ${error}`);\n failed++;\n }\n }\n\n logger.newline();\n logger.success(`Created ${created} labels`);\n if (failed > 0) {\n logger.warning(`Failed to create ${failed} labels`);\n }\n\n logger.newline();\n logger.info(\"Labels are ready. You can now create AI-ready issues.\");\n}\n"],"mappings":"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","names":["execa","execa"]}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addIssueComment,
|
|
4
4
|
addPrComment,
|
|
5
|
+
aiSpinnerText,
|
|
5
6
|
assignIssue,
|
|
6
7
|
buildIssueLabels,
|
|
7
8
|
checkAIProvider,
|
|
@@ -9,10 +10,13 @@ import {
|
|
|
9
10
|
checkGeminiCli,
|
|
10
11
|
checkGhAuth,
|
|
11
12
|
checkGitRepo,
|
|
13
|
+
checkInitialized,
|
|
14
|
+
checkLabelsExist,
|
|
12
15
|
colors,
|
|
13
16
|
configExists,
|
|
14
17
|
createIssue,
|
|
15
18
|
createPullRequest,
|
|
19
|
+
createSpinner,
|
|
16
20
|
extractTypeFromLabels,
|
|
17
21
|
generateDefaultConfig,
|
|
18
22
|
getConfigPath,
|
|
@@ -36,7 +40,7 @@ import {
|
|
|
36
40
|
sortByPriority,
|
|
37
41
|
updateIssueLabels,
|
|
38
42
|
withSpinner
|
|
39
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-75KVN7ZC.js";
|
|
40
44
|
|
|
41
45
|
// src/index.ts
|
|
42
46
|
import { Command } from "commander";
|
|
@@ -200,7 +204,7 @@ async function initCommand(options) {
|
|
|
200
204
|
}
|
|
201
205
|
]);
|
|
202
206
|
if (setupLabels) {
|
|
203
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-
|
|
207
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-STWAFV2E.js");
|
|
204
208
|
await setupLabelsCommand2();
|
|
205
209
|
}
|
|
206
210
|
}
|
|
@@ -332,7 +336,12 @@ async function invokeClaudeInternal(options) {
|
|
|
332
336
|
stdio: ["inherit", "pipe", "pipe"]
|
|
333
337
|
});
|
|
334
338
|
let output = "";
|
|
339
|
+
let firstData = true;
|
|
335
340
|
child.stdout.on("data", (chunk) => {
|
|
341
|
+
if (firstData) {
|
|
342
|
+
firstData = false;
|
|
343
|
+
options.onFirstData?.();
|
|
344
|
+
}
|
|
336
345
|
const text = chunk.toString();
|
|
337
346
|
output += text;
|
|
338
347
|
process.stdout.write(text);
|
|
@@ -371,7 +380,12 @@ async function invokeGeminiInternal(options) {
|
|
|
371
380
|
stdio: ["inherit", "pipe", "pipe"]
|
|
372
381
|
});
|
|
373
382
|
let output = "";
|
|
383
|
+
let firstData = true;
|
|
374
384
|
child.stdout.on("data", (chunk) => {
|
|
385
|
+
if (firstData) {
|
|
386
|
+
firstData = false;
|
|
387
|
+
options.onFirstData?.();
|
|
388
|
+
}
|
|
375
389
|
const text = chunk.toString();
|
|
376
390
|
output += text;
|
|
377
391
|
process.stdout.write(text);
|
|
@@ -409,7 +423,12 @@ async function invokeCodexInternal(options) {
|
|
|
409
423
|
stdio: ["inherit", "pipe", "pipe"]
|
|
410
424
|
});
|
|
411
425
|
let output = "";
|
|
426
|
+
let firstData = true;
|
|
412
427
|
child.stdout.on("data", (chunk) => {
|
|
428
|
+
if (firstData) {
|
|
429
|
+
firstData = false;
|
|
430
|
+
options.onFirstData?.();
|
|
431
|
+
}
|
|
413
432
|
const text = chunk.toString();
|
|
414
433
|
output += text;
|
|
415
434
|
process.stdout.write(text);
|
|
@@ -628,8 +647,6 @@ function generateFallbackTitle(description) {
|
|
|
628
647
|
|
|
629
648
|
// src/commands/create.ts
|
|
630
649
|
async function createCommand(description, options) {
|
|
631
|
-
logger.bold("Creating AI-enhanced ticket...");
|
|
632
|
-
logger.newline();
|
|
633
650
|
const config = loadConfig();
|
|
634
651
|
let currentProvider = resolveProvider(options, config);
|
|
635
652
|
const [ghAuth, aiOk] = await Promise.all([
|
|
@@ -650,33 +667,24 @@ async function createCommand(description, options) {
|
|
|
650
667
|
let aiOutput;
|
|
651
668
|
let additionalHints = null;
|
|
652
669
|
while (true) {
|
|
653
|
-
const providerName = getProviderDisplayName(currentProvider);
|
|
654
670
|
const prompt = buildTicketPrompt(
|
|
655
671
|
description,
|
|
656
672
|
agentInstructions,
|
|
657
673
|
additionalHints
|
|
658
674
|
);
|
|
659
675
|
try {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
`\u250C\u2500 Generating ticket with ${providerName}... \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`
|
|
663
|
-
)
|
|
664
|
-
);
|
|
665
|
-
logger.newline();
|
|
676
|
+
const spinner = createSpinner(aiSpinnerText(getProviderDisplayName(currentProvider), "generate ticket"));
|
|
677
|
+
spinner.start();
|
|
666
678
|
const result = await invokeAI(
|
|
667
|
-
{ prompt, streamOutput: true },
|
|
679
|
+
{ prompt, streamOutput: true, onFirstData: () => spinner.stop() },
|
|
668
680
|
config,
|
|
669
681
|
currentProvider
|
|
670
682
|
);
|
|
683
|
+
spinner.stop();
|
|
671
684
|
aiOutput = result.output;
|
|
672
685
|
logger.newline();
|
|
673
|
-
console.log(
|
|
674
|
-
chalk.dim(
|
|
675
|
-
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
676
|
-
)
|
|
677
|
-
);
|
|
678
|
-
logger.newline();
|
|
679
686
|
} catch (error) {
|
|
687
|
+
const providerName = getProviderDisplayName(currentProvider);
|
|
680
688
|
if (error && typeof error === "object" && "rateLimited" in error) {
|
|
681
689
|
logger.warning(`${providerName} is rate limited.`);
|
|
682
690
|
const others = await getOtherAvailableProviders(currentProvider);
|
|
@@ -722,7 +730,7 @@ async function createCommand(description, options) {
|
|
|
722
730
|
const issueBody = extractIssueBody(aiOutput) + `
|
|
723
731
|
|
|
724
732
|
---
|
|
725
|
-
*Created with ${
|
|
733
|
+
*Created with ${getProviderDisplayName(currentProvider)} by [gent](https://github.com/Rotorsoft/gent)*`;
|
|
726
734
|
let title;
|
|
727
735
|
if (options.title) {
|
|
728
736
|
title = options.title;
|
|
@@ -1350,8 +1358,6 @@ async function offerCreateBranch(config, issueNumber, title) {
|
|
|
1350
1358
|
// src/commands/run.ts
|
|
1351
1359
|
import inquirer4 from "inquirer";
|
|
1352
1360
|
async function runCommand(issueNumberArg, options) {
|
|
1353
|
-
logger.bold("Running AI implementation workflow...");
|
|
1354
|
-
logger.newline();
|
|
1355
1361
|
const config = loadConfig();
|
|
1356
1362
|
const provider = options.provider ?? config.ai.provider;
|
|
1357
1363
|
const providerName = getProviderDisplayName(provider);
|
|
@@ -1504,12 +1510,8 @@ Labels: ${issue.labels.join(", ")}`
|
|
|
1504
1510
|
config
|
|
1505
1511
|
);
|
|
1506
1512
|
logger.newline();
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
);
|
|
1510
|
-
logger.dim(`${providerName} will implement the feature and create a commit.`);
|
|
1511
|
-
logger.dim("Review the changes before pushing.");
|
|
1512
|
-
logger.newline();
|
|
1513
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "implement ticket"));
|
|
1514
|
+
spinner.start();
|
|
1513
1515
|
const beforeSha = await getCurrentCommitSha();
|
|
1514
1516
|
let wasCancelled = false;
|
|
1515
1517
|
const handleSignal = () => {
|
|
@@ -1517,6 +1519,7 @@ Labels: ${issue.labels.join(", ")}`
|
|
|
1517
1519
|
};
|
|
1518
1520
|
process.on("SIGINT", handleSignal);
|
|
1519
1521
|
process.on("SIGTERM", handleSignal);
|
|
1522
|
+
spinner.stop();
|
|
1520
1523
|
let aiExitCode;
|
|
1521
1524
|
let usedProvider = provider;
|
|
1522
1525
|
try {
|
|
@@ -1661,8 +1664,6 @@ async function getChangedFiles(baseBranch = "main") {
|
|
|
1661
1664
|
// src/commands/pr.ts
|
|
1662
1665
|
import inquirer5 from "inquirer";
|
|
1663
1666
|
async function prCommand(options) {
|
|
1664
|
-
logger.bold("Creating AI-enhanced pull request...");
|
|
1665
|
-
logger.newline();
|
|
1666
1667
|
const config = loadConfig();
|
|
1667
1668
|
let currentProvider = options.provider ?? config.ai.provider;
|
|
1668
1669
|
const [ghAuth, aiOk] = await Promise.all([
|
|
@@ -1755,15 +1756,14 @@ IMPORTANT: This PR contains UI changes. Use the Playwright MCP plugin to:
|
|
|
1755
1756
|
while (true) {
|
|
1756
1757
|
const providerName = getProviderDisplayName(usedProvider);
|
|
1757
1758
|
try {
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
);
|
|
1761
|
-
logger.newline();
|
|
1759
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "generate PR"));
|
|
1760
|
+
spinner.start();
|
|
1762
1761
|
const result = await invokeAI(
|
|
1763
|
-
{ prompt, streamOutput: true },
|
|
1762
|
+
{ prompt, streamOutput: true, onFirstData: () => spinner.stop() },
|
|
1764
1763
|
config,
|
|
1765
1764
|
usedProvider
|
|
1766
1765
|
);
|
|
1766
|
+
spinner.stop();
|
|
1767
1767
|
prBody = result.output;
|
|
1768
1768
|
logger.newline();
|
|
1769
1769
|
break;
|
|
@@ -2054,8 +2054,6 @@ function truncateComment(body, maxLength = 200) {
|
|
|
2054
2054
|
|
|
2055
2055
|
// src/commands/fix.ts
|
|
2056
2056
|
async function fixCommand(options) {
|
|
2057
|
-
logger.bold("Applying PR review feedback with AI...");
|
|
2058
|
-
logger.newline();
|
|
2059
2057
|
const config = loadConfig();
|
|
2060
2058
|
const provider = options.provider ?? config.ai.provider;
|
|
2061
2059
|
const providerName = getProviderDisplayName(provider);
|
|
@@ -2148,9 +2146,8 @@ async function fixCommand(options) {
|
|
|
2148
2146
|
${summary}`
|
|
2149
2147
|
);
|
|
2150
2148
|
logger.newline();
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
logger.newline();
|
|
2149
|
+
const spinner = createSpinner(aiSpinnerText(providerName, "apply fixes"));
|
|
2150
|
+
spinner.start();
|
|
2154
2151
|
const beforeSha = await getCurrentCommitSha();
|
|
2155
2152
|
let wasCancelled = false;
|
|
2156
2153
|
const handleSignal = () => {
|
|
@@ -2158,6 +2155,7 @@ ${summary}`
|
|
|
2158
2155
|
};
|
|
2159
2156
|
process.on("SIGINT", handleSignal);
|
|
2160
2157
|
process.on("SIGTERM", handleSignal);
|
|
2158
|
+
spinner.stop();
|
|
2161
2159
|
let aiExitCode;
|
|
2162
2160
|
try {
|
|
2163
2161
|
const { result } = await invokeAIInteractive(
|
|
@@ -2242,7 +2240,7 @@ import { homedir } from "os";
|
|
|
2242
2240
|
// package.json
|
|
2243
2241
|
var package_default = {
|
|
2244
2242
|
name: "@rotorsoft/gent",
|
|
2245
|
-
version: "1.
|
|
2243
|
+
version: "1.21.0",
|
|
2246
2244
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
2247
2245
|
keywords: [
|
|
2248
2246
|
"cli",
|
|
@@ -2668,6 +2666,9 @@ import { execa as execa5 } from "execa";
|
|
|
2668
2666
|
|
|
2669
2667
|
// src/tui/state.ts
|
|
2670
2668
|
var envCache = null;
|
|
2669
|
+
function resetEnvCache() {
|
|
2670
|
+
envCache = null;
|
|
2671
|
+
}
|
|
2671
2672
|
async function aggregateState() {
|
|
2672
2673
|
const isGitRepo = await checkGitRepo();
|
|
2673
2674
|
if (!isGitRepo) {
|
|
@@ -2693,7 +2694,8 @@ async function aggregateState() {
|
|
|
2693
2694
|
hasActionableFeedback: false,
|
|
2694
2695
|
hasUIChanges: false,
|
|
2695
2696
|
isPlaywrightAvailable: false,
|
|
2696
|
-
hasValidRemote: false
|
|
2697
|
+
hasValidRemote: false,
|
|
2698
|
+
hasLabels: false
|
|
2697
2699
|
};
|
|
2698
2700
|
}
|
|
2699
2701
|
const config = loadConfig();
|
|
@@ -2706,8 +2708,10 @@ async function aggregateState() {
|
|
|
2706
2708
|
envCache = {
|
|
2707
2709
|
isGhAuthenticated: ghAuth,
|
|
2708
2710
|
isAIProviderAvailable: aiAvail,
|
|
2709
|
-
isPlaywrightAvailable: false
|
|
2711
|
+
isPlaywrightAvailable: false,
|
|
2710
2712
|
// resolved below on first feature branch visit
|
|
2713
|
+
hasLabels: null
|
|
2714
|
+
// resolved below when config and remote exist
|
|
2711
2715
|
};
|
|
2712
2716
|
}
|
|
2713
2717
|
const { isGhAuthenticated, isAIProviderAvailable } = envCache;
|
|
@@ -2725,6 +2729,11 @@ async function aggregateState() {
|
|
|
2725
2729
|
getCommitsSinceBase(baseBranch),
|
|
2726
2730
|
getUnpushedCommits()
|
|
2727
2731
|
]);
|
|
2732
|
+
const hasRemote = repoInfo !== null;
|
|
2733
|
+
if (hasConfig && hasRemote && envCache.hasLabels === null) {
|
|
2734
|
+
envCache.hasLabels = await checkLabelsExist().catch(() => false);
|
|
2735
|
+
}
|
|
2736
|
+
const hasLabels = envCache.hasLabels ?? false;
|
|
2728
2737
|
let issue = null;
|
|
2729
2738
|
let workflowStatus = "none";
|
|
2730
2739
|
let pr = null;
|
|
@@ -2793,7 +2802,8 @@ async function aggregateState() {
|
|
|
2793
2802
|
hasActionableFeedback,
|
|
2794
2803
|
hasUIChanges: uiChanges,
|
|
2795
2804
|
isPlaywrightAvailable: playwrightAvailable,
|
|
2796
|
-
hasValidRemote:
|
|
2805
|
+
hasValidRemote: hasRemote,
|
|
2806
|
+
hasLabels
|
|
2797
2807
|
};
|
|
2798
2808
|
}
|
|
2799
2809
|
|
|
@@ -2804,7 +2814,15 @@ function getAvailableActions(state) {
|
|
|
2804
2814
|
actions.push({ id: "quit", label: "quit", shortcut: "q" });
|
|
2805
2815
|
return actions;
|
|
2806
2816
|
}
|
|
2807
|
-
|
|
2817
|
+
const needsInit = !state.hasConfig;
|
|
2818
|
+
const needsLabels = state.hasConfig && state.hasValidRemote && !state.hasLabels;
|
|
2819
|
+
if (needsInit) {
|
|
2820
|
+
actions.push({ id: "init", label: "init", shortcut: "i" });
|
|
2821
|
+
} else if (needsLabels) {
|
|
2822
|
+
actions.push({ id: "setup-labels", label: "setup-labels", shortcut: "b" });
|
|
2823
|
+
}
|
|
2824
|
+
const isSetUp = state.hasConfig && (!state.hasValidRemote || state.hasLabels);
|
|
2825
|
+
if (isSetUp && state.hasValidRemote) {
|
|
2808
2826
|
actions.push({ id: "create", label: "new", shortcut: "n" });
|
|
2809
2827
|
}
|
|
2810
2828
|
if (!state.isOnMain) {
|
|
@@ -2814,16 +2832,16 @@ function getAvailableActions(state) {
|
|
|
2814
2832
|
if (state.hasUnpushedCommits && state.commits.length > 0) {
|
|
2815
2833
|
actions.push({ id: "push", label: "push", shortcut: "s" });
|
|
2816
2834
|
}
|
|
2817
|
-
if (state.hasValidRemote && !state.pr && state.commits.length > 0) {
|
|
2835
|
+
if (isSetUp && state.hasValidRemote && !state.pr && state.commits.length > 0) {
|
|
2818
2836
|
actions.push({ id: "pr", label: "pr", shortcut: "p" });
|
|
2819
2837
|
}
|
|
2820
|
-
if (state.issue && state.pr?.state !== "merged") {
|
|
2838
|
+
if (isSetUp && state.issue && state.pr?.state !== "merged") {
|
|
2821
2839
|
actions.push({ id: "run", label: "run", shortcut: "r" });
|
|
2822
2840
|
}
|
|
2823
2841
|
}
|
|
2824
|
-
if (state.hasValidRemote) {
|
|
2842
|
+
if (isSetUp && state.hasValidRemote) {
|
|
2825
2843
|
actions.push({ id: "list", label: "list", shortcut: "l" });
|
|
2826
|
-
} else {
|
|
2844
|
+
} else if (!state.hasValidRemote) {
|
|
2827
2845
|
actions.push({ id: "github-remote", label: "github", shortcut: "g" });
|
|
2828
2846
|
}
|
|
2829
2847
|
actions.push({ id: "refresh", label: "refresh", shortcut: "f" });
|
|
@@ -3113,7 +3131,15 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
|
3113
3131
|
out(row(chalk3.dim(" No commits"), w));
|
|
3114
3132
|
}
|
|
3115
3133
|
}
|
|
3116
|
-
if (!state.
|
|
3134
|
+
if (!state.hasConfig) {
|
|
3135
|
+
section("Setup");
|
|
3136
|
+
out(row(chalk3.yellow('Run "gent init" to set up this repository'), w));
|
|
3137
|
+
out(row(chalk3.dim("Press [i] to initialize"), w));
|
|
3138
|
+
} else if (state.hasValidRemote && !state.hasLabels) {
|
|
3139
|
+
section("Setup");
|
|
3140
|
+
out(row(chalk3.yellow('Run "gent setup-labels" to create required GitHub labels'), w));
|
|
3141
|
+
out(row(chalk3.dim("Press [b] to set up labels"), w));
|
|
3142
|
+
} else if (!state.hasValidRemote) {
|
|
3117
3143
|
section("Hint");
|
|
3118
3144
|
out(
|
|
3119
3145
|
row(
|
|
@@ -3737,6 +3763,26 @@ async function executeAction(actionId, state, dashboardLines) {
|
|
|
3737
3763
|
await handleRun(state);
|
|
3738
3764
|
return CONTINUE;
|
|
3739
3765
|
}
|
|
3766
|
+
case "init": {
|
|
3767
|
+
clearScreen();
|
|
3768
|
+
try {
|
|
3769
|
+
await initCommand({});
|
|
3770
|
+
} catch (error) {
|
|
3771
|
+
logger.error(`Init failed: ${error}`);
|
|
3772
|
+
}
|
|
3773
|
+
resetEnvCache();
|
|
3774
|
+
return CONTINUE;
|
|
3775
|
+
}
|
|
3776
|
+
case "setup-labels": {
|
|
3777
|
+
clearScreen();
|
|
3778
|
+
try {
|
|
3779
|
+
await setupLabelsCommand();
|
|
3780
|
+
} catch (error) {
|
|
3781
|
+
logger.error(`Setup labels failed: ${error}`);
|
|
3782
|
+
}
|
|
3783
|
+
resetEnvCache();
|
|
3784
|
+
return CONTINUE;
|
|
3785
|
+
}
|
|
3740
3786
|
case "github-remote": {
|
|
3741
3787
|
const confirmed = await showConfirm({
|
|
3742
3788
|
title: "Push to GitHub",
|
|
@@ -3798,7 +3844,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3798
3844
|
});
|
|
3799
3845
|
message = input || CANCEL;
|
|
3800
3846
|
} else {
|
|
3801
|
-
showStatus("Generating",
|
|
3847
|
+
showStatus("Generating", aiSpinnerText(providerName, "generate commit message"), dashboardLines);
|
|
3802
3848
|
message = await generateCommitMessage(
|
|
3803
3849
|
diffContent,
|
|
3804
3850
|
issueNumber,
|
|
@@ -4107,7 +4153,8 @@ async function tuiCommand() {
|
|
|
4107
4153
|
hasActionableFeedback: false,
|
|
4108
4154
|
hasUIChanges: false,
|
|
4109
4155
|
isPlaywrightAvailable: false,
|
|
4110
|
-
hasValidRemote: true
|
|
4156
|
+
hasValidRemote: true,
|
|
4157
|
+
hasLabels: true
|
|
4111
4158
|
};
|
|
4112
4159
|
let needsRefresh = true;
|
|
4113
4160
|
let isFirstLoad = true;
|
|
@@ -4164,6 +4211,18 @@ function startVersionCheck() {
|
|
|
4164
4211
|
}).catch(() => {
|
|
4165
4212
|
});
|
|
4166
4213
|
}
|
|
4214
|
+
async function checkPrerequisites() {
|
|
4215
|
+
if (!checkInitialized()) {
|
|
4216
|
+
logger.warning('Repository not initialized. Run "gent init" to set up this repository.');
|
|
4217
|
+
return false;
|
|
4218
|
+
}
|
|
4219
|
+
const labelsOk = await checkLabelsExist();
|
|
4220
|
+
if (!labelsOk) {
|
|
4221
|
+
logger.warning('GitHub labels not found. Run "gent setup-labels" to create required labels.');
|
|
4222
|
+
return false;
|
|
4223
|
+
}
|
|
4224
|
+
return true;
|
|
4225
|
+
}
|
|
4167
4226
|
var program = new Command();
|
|
4168
4227
|
program.name("gent").description(
|
|
4169
4228
|
"AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs"
|
|
@@ -4182,6 +4241,7 @@ program.command("create <description>").description("Create an AI-enhanced GitHu
|
|
|
4182
4241
|
"-p, --provider <provider>",
|
|
4183
4242
|
"AI provider to use (claude, gemini, or codex)"
|
|
4184
4243
|
).option("-t, --title <title>", "Override the generated issue title").action(async (description, options) => {
|
|
4244
|
+
if (!await checkPrerequisites()) return;
|
|
4185
4245
|
await createCommand(description, {
|
|
4186
4246
|
yes: options.yes,
|
|
4187
4247
|
provider: options.provider,
|
|
@@ -4192,6 +4252,7 @@ program.command("list").description("List and switch to GitHub issues").option("
|
|
|
4192
4252
|
"-s, --status <status>",
|
|
4193
4253
|
"Filter by workflow status (ready, in-progress, completed, blocked, all)"
|
|
4194
4254
|
).option("-n, --limit <number>", "Maximum number of issues to show", "20").action(async (options) => {
|
|
4255
|
+
if (!await checkPrerequisites()) return;
|
|
4195
4256
|
await listCommand({
|
|
4196
4257
|
label: options.label,
|
|
4197
4258
|
status: options.status,
|
|
@@ -4202,12 +4263,14 @@ program.command("run [issue-number]").description("Run AI to implement a GitHub
|
|
|
4202
4263
|
"-p, --provider <provider>",
|
|
4203
4264
|
"AI provider to use (claude, gemini, or codex)"
|
|
4204
4265
|
).action(async (issueNumber, options) => {
|
|
4266
|
+
if (!await checkPrerequisites()) return;
|
|
4205
4267
|
await runCommand(issueNumber, { provider: options.provider });
|
|
4206
4268
|
});
|
|
4207
4269
|
program.command("pr").description("Create an AI-enhanced pull request").option("-d, --draft", "Create as draft PR").option(
|
|
4208
4270
|
"-p, --provider <provider>",
|
|
4209
4271
|
"AI provider to use (claude, gemini, or codex)"
|
|
4210
4272
|
).option("--no-video", "Disable video capture for UI changes").action(async (options) => {
|
|
4273
|
+
if (!await checkPrerequisites()) return;
|
|
4211
4274
|
await prCommand({
|
|
4212
4275
|
draft: options.draft,
|
|
4213
4276
|
provider: options.provider,
|
|
@@ -4218,9 +4281,11 @@ program.command("fix").description("Apply PR review feedback using AI").option(
|
|
|
4218
4281
|
"-p, --provider <provider>",
|
|
4219
4282
|
"AI provider to use (claude, gemini, or codex)"
|
|
4220
4283
|
).action(async (options) => {
|
|
4284
|
+
if (!await checkPrerequisites()) return;
|
|
4221
4285
|
await fixCommand({ provider: options.provider });
|
|
4222
4286
|
});
|
|
4223
4287
|
program.command("status").description("Show current workflow status").action(async () => {
|
|
4288
|
+
if (!await checkPrerequisites()) return;
|
|
4224
4289
|
await statusCommand();
|
|
4225
4290
|
});
|
|
4226
4291
|
program.command("github-remote").description("Create a GitHub repository and push local repo").action(async () => {
|