@g-abhishek/gitx 0.1.4 β 0.1.5
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 +24 -12
- package/dist/ai/reviewHelpers.d.ts.map +1 -1
- package/dist/ai/reviewHelpers.js +53 -38
- package/dist/ai/reviewHelpers.js.map +1 -1
- package/dist/cli/commands/pr/fixComments.d.ts +5 -2
- package/dist/cli/commands/pr/fixComments.d.ts.map +1 -1
- package/dist/cli/commands/pr/fixComments.js +5 -82
- package/dist/cli/commands/pr/fixComments.js.map +1 -1
- package/dist/cli/commands/pr/index.js +2 -2
- package/dist/cli/commands/pr/index.js.map +1 -1
- package/dist/cli/commands/pr/resolve.d.ts +3 -0
- package/dist/cli/commands/pr/resolve.d.ts.map +1 -0
- package/dist/cli/commands/pr/resolve.js +92 -0
- package/dist/cli/commands/pr/resolve.js.map +1 -0
- package/dist/cli/commands/pr/review.js +1 -1
- package/dist/cli/commands/pr/review.js.map +1 -1
- package/dist/utils/gitOps.d.ts +11 -4
- package/dist/utils/gitOps.d.ts.map +1 -1
- package/dist/utils/gitOps.js +53 -37
- package/dist/utils/gitOps.js.map +1 -1
- package/dist/workflows/pr.d.ts +1 -1
- package/dist/workflows/pr.d.ts.map +1 -1
- package/dist/workflows/pr.js +11 -5
- package/dist/workflows/pr.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { logger } from "../../../logger/logger.js";
|
|
3
|
+
import { Gitx } from "../../../core/gitx.js";
|
|
4
|
+
import { runFixCommentsWorkflow } from "../../../workflows/pr.js";
|
|
5
|
+
import { pushBranch, getCurrentBranch, isWorkingTreeDirty } from "../../../utils/gitOps.js";
|
|
6
|
+
export function registerPrResolveCommand(pr) {
|
|
7
|
+
pr.command("resolve")
|
|
8
|
+
.description("π§ AI-resolve review comments: applies code fixes, commits, and pushes")
|
|
9
|
+
.argument("<id>", "Pull request number")
|
|
10
|
+
.option("--dry-run", "Preview fixes without applying or committing", false)
|
|
11
|
+
.option("--no-commit", "Apply fixes to working tree only β skip commit and push", false)
|
|
12
|
+
.option("--no-push", "Apply & commit locally but skip push", false)
|
|
13
|
+
.action(async (id, options) => {
|
|
14
|
+
const prNumber = parseInt(id, 10);
|
|
15
|
+
if (isNaN(prNumber) || prNumber <= 0) {
|
|
16
|
+
logger.error(`Invalid PR number: ${id}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
// Commander sets options.commit = false when --no-commit is passed
|
|
20
|
+
const noCommit = options.commit === false;
|
|
21
|
+
const gitx = await Gitx.fromCwd();
|
|
22
|
+
const ctx = await gitx.getRepoContext();
|
|
23
|
+
logger.info(`π§ Resolving review comments on PR #${prNumber} (${ctx.repoSlug})β¦\n`);
|
|
24
|
+
// ββ AI availability check βββββββββββββββββββββββββββββββββββββββββββββ
|
|
25
|
+
if (!await Gitx.isAiAvailable(gitx.config)) {
|
|
26
|
+
logger.warn("β οΈ No AI provider configured β cannot generate fixes.\n" +
|
|
27
|
+
" Run `gitx config` to set up an AI provider (Claude, OpenAI, or claude-cli).");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// ββ Confirmation prompt βββββββββββββββββββββββββββββββββββββββββββββββ
|
|
31
|
+
if (!options.dryRun) {
|
|
32
|
+
const modeDesc = noCommit
|
|
33
|
+
? "apply fixes to your working tree only (no commit or push)"
|
|
34
|
+
: options.push === false
|
|
35
|
+
? "apply fixes and commit locally (no push)"
|
|
36
|
+
: "apply fixes, commit, and push";
|
|
37
|
+
const { proceed } = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: "confirm",
|
|
40
|
+
name: "proceed",
|
|
41
|
+
message: `This will ${modeDesc}. Continue?`,
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
if (!proceed) {
|
|
46
|
+
logger.warn("Cancelled.");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ββ Guard: warn about uncommitted changes βββββββββββββββββββββββββββββ
|
|
51
|
+
if (!options.dryRun) {
|
|
52
|
+
const dirty = await isWorkingTreeDirty(gitx.cwd);
|
|
53
|
+
if (dirty) {
|
|
54
|
+
logger.warn("β οΈ You have uncommitted changes. They may conflict with applied fixes.");
|
|
55
|
+
const { cont } = await inquirer.prompt([
|
|
56
|
+
{ type: "confirm", name: "cont", message: "Continue anyway?", default: false },
|
|
57
|
+
]);
|
|
58
|
+
if (!cont) {
|
|
59
|
+
logger.warn("Cancelled.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const result = await runFixCommentsWorkflow(gitx, prNumber, options.dryRun, noCommit);
|
|
65
|
+
logger.info(`\nπ PR: ${result.pr.title}`);
|
|
66
|
+
if (result.appliedFixes.length === 0 && result.skippedFixes.length === 0) {
|
|
67
|
+
logger.info("No actionable review comments found.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (result.appliedFixes.length > 0) {
|
|
71
|
+
logger.success(`\nβ
Applied ${result.appliedFixes.length} fix(es):`);
|
|
72
|
+
result.appliedFixes.forEach((f) => logger.info(` β’ ${f.path} β ${f.rationale}`));
|
|
73
|
+
}
|
|
74
|
+
if (result.skippedFixes.length > 0) {
|
|
75
|
+
logger.warn(`\nβ οΈ Skipped ${result.skippedFixes.length} fix(es):`);
|
|
76
|
+
result.skippedFixes.forEach((f) => logger.warn(` β’ ${f.path}: ${f.reason}`));
|
|
77
|
+
}
|
|
78
|
+
// ββ Push if applicable ββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
79
|
+
if (!options.dryRun && !noCommit && options.push !== false && result.appliedFixes.length > 0) {
|
|
80
|
+
const branch = await getCurrentBranch(gitx.cwd);
|
|
81
|
+
logger.info(`\nπ Pushing ${branch}β¦`);
|
|
82
|
+
try {
|
|
83
|
+
await pushBranch(branch, gitx.cwd);
|
|
84
|
+
logger.success("Branch pushed.");
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
logger.warn(`Push failed: ${String(err.message ?? err)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\nimport { runFixCommentsWorkflow } from \"../../../workflows/pr.js\";\nimport { pushBranch, getCurrentBranch, isWorkingTreeDirty } from \"../../../utils/gitOps.js\";\n\nexport function registerPrResolveCommand(pr: Command): void {\n pr.command(\"resolve\")\n .description(\"π§ AI-resolve review comments: applies code fixes, commits, and pushes\")\n .argument(\"<id>\", \"Pull request number\")\n .option(\"--dry-run\", \"Preview fixes without applying or committing\", false)\n .option(\"--no-commit\", \"Apply fixes to working tree only β skip commit and push\", false)\n .option(\"--no-push\", \"Apply & commit locally but skip push\", false)\n .action(async (id: string, options: { dryRun: boolean; commit: boolean; push: boolean }) => {\n const prNumber = parseInt(id, 10);\n if (isNaN(prNumber) || prNumber <= 0) {\n logger.error(`Invalid PR number: ${id}`);\n process.exit(1);\n }\n\n // Commander sets options.commit = false when --no-commit is passed\n const noCommit = options.commit === false;\n\n const gitx = await Gitx.fromCwd();\n const ctx = await gitx.getRepoContext();\n logger.info(`π§ Resolving review comments on PR #${prNumber} (${ctx.repoSlug})β¦\\n`);\n\n // ββ AI availability check βββββββββββββββββββββββββββββββββββββββββββββ\n if (!await Gitx.isAiAvailable(gitx.config)) {\n logger.warn(\n \"β οΈ No AI provider configured β cannot generate fixes.\\n\" +\n \" Run `gitx config` to set up an AI provider (Claude, OpenAI, or claude-cli).\"\n );\n return;\n }\n\n // ββ Confirmation prompt βββββββββββββββββββββββββββββββββββββββββββββββ\n if (!options.dryRun) {\n const modeDesc = noCommit\n ? \"apply fixes to your working tree only (no commit or push)\"\n : options.push === false\n ? \"apply fixes and commit locally (no push)\"\n : \"apply fixes, commit, and push\";\n\n const { proceed } = await inquirer.prompt<{ proceed: boolean }>([\n {\n type: \"confirm\",\n name: \"proceed\",\n message: `This will ${modeDesc}. Continue?`,\n default: false,\n },\n ]);\n if (!proceed) {\n logger.warn(\"Cancelled.\");\n return;\n }\n }\n\n // ββ Guard: warn about uncommitted changes βββββββββββββββββββββββββββββ\n if (!options.dryRun) {\n const dirty = await isWorkingTreeDirty(gitx.cwd);\n if (dirty) {\n logger.warn(\"β οΈ You have uncommitted changes. They may conflict with applied fixes.\");\n const { cont } = await inquirer.prompt<{ cont: boolean }>([\n { type: \"confirm\", name: \"cont\", message: \"Continue anyway?\", default: false },\n ]);\n if (!cont) { logger.warn(\"Cancelled.\"); return; }\n }\n }\n\n const result = await runFixCommentsWorkflow(gitx, prNumber, options.dryRun, noCommit);\n\n logger.info(`\\nπ PR: ${result.pr.title}`);\n\n if (result.appliedFixes.length === 0 && result.skippedFixes.length === 0) {\n logger.info(\"No actionable review comments found.\");\n return;\n }\n\n if (result.appliedFixes.length > 0) {\n logger.success(`\\nβ
Applied ${result.appliedFixes.length} fix(es):`);\n result.appliedFixes.forEach((f) => logger.info(` β’ ${f.path} β ${f.rationale}`));\n }\n\n if (result.skippedFixes.length > 0) {\n logger.warn(`\\nβ οΈ Skipped ${result.skippedFixes.length} fix(es):`);\n result.skippedFixes.forEach((f) => logger.warn(` β’ ${f.path}: ${f.reason}`));\n }\n\n // ββ Push if applicable ββββββββββββββββββββββββββββββββββββββββββββββββ\n if (!options.dryRun && !noCommit && options.push !== false && result.appliedFixes.length > 0) {\n const branch = await getCurrentBranch(gitx.cwd);\n logger.info(`\\nπ Pushing ${branch}β¦`);\n try {\n await pushBranch(branch, gitx.cwd);\n logger.success(\"Branch pushed.\");\n } catch (err) {\n logger.warn(`Push failed: ${String((err as Error).message ?? err)}`);\n }\n }\n });\n}\n"]}
|
|
@@ -123,7 +123,7 @@ export function registerPrReviewCommand(pr) {
|
|
|
123
123
|
logger.warn("β οΈ Review could not be posted to the PR (see error above).\n" +
|
|
124
124
|
" The full review is shown above β you can copy it manually.");
|
|
125
125
|
}
|
|
126
|
-
// To
|
|
126
|
+
// To resolve the review comments, run: gitx pr resolve <PR_number>
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
//# sourceMappingURL=review.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/review.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\nimport { runReviewWorkflow } from \"../../../workflows/pr.js\";\n\nexport function registerPrReviewCommand(pr: Command): void {\n pr.command(\"review\")\n .description(\"π§ Senior-dev AI review: inline comments, checklist, verdict\")\n .argument(\"<id>\", \"Pull request number\")\n .option(\"--no-comment\", \"Show review locally only β do not post to PR\")\n .option(\"--no-inline\", \"Post overall review but skip inline file comments\")\n .action(async (id: string, options: { comment: boolean; inline: boolean }) => {\n const prNumber = parseInt(id, 10);\n if (isNaN(prNumber) || prNumber <= 0) {\n logger.error(`Invalid PR number: ${id}`);\n process.exit(1);\n }\n\n const gitx = await Gitx.fromCwd();\n\n if (!await Gitx.isAiAvailable(gitx.config)) {\n logger.warn(\n \"β οΈ No AI provider configured β review will not be meaningful.\\n\" +\n \" Run: gitx config set anthropic (or openai / claude-cli)\"\n );\n return;\n }\n\n const postComment = options.comment !== false;\n\n logger.info(`\\nπ§ Senior-dev AI review β PR #${prNumber}\\n`);\n\n let result: Awaited<ReturnType<typeof runReviewWorkflow>>;\n try {\n result = await runReviewWorkflow(gitx, prNumber, postComment);\n } catch (err) {\n logger.error(`Review failed: ${String((err as Error).message ?? err)}`);\n process.exitCode = 1;\n return;\n }\n\n const { pr: pullReq, review, comments } = result;\n\n // ββ PR header βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n logger.info(`\\nπ ${pullReq.title}`);\n logger.info(` ${pullReq.head} β ${pullReq.base} Β· by ${pullReq.author} Β· ${pullReq.state}`);\n logger.info(` ${pullReq.url}\\n`);\n\n if (!review) {\n logger.info(`π€ Review:\\n${result.aiSummary}`);\n return;\n }\n\n // ββ Verdict βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n const verdictIcon =\n review.verdict === \"approve\"\n ? \"β
APPROVE\"\n : review.verdict === \"request_changes\"\n ? \"π΄ REQUEST CHANGES\"\n : \"π¬ COMMENT\";\n\n logger.info(`π€ Verdict: ${verdictIcon}\\n`);\n\n // ββ Summary βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n logger.info(`π Summary:\\n ${review.summary.replace(/\\n/g, \"\\n \")}\\n`);\n\n // ββ Checklist βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.checklist.length > 0) {\n logger.info(\"β
Review Checklist:\");\n for (const item of review.checklist) {\n const icon = item.status === \"pass\" ? \"β
\" : item.status === \"warn\" ? \"β οΈ \" : \"β\";\n logger.info(` ${icon} ${item.area.padEnd(20)} ${item.note}`);\n }\n logger.info(\"\");\n }\n\n // ββ Issues ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n const criticals = review.issues.filter((i) => i.severity === \"critical\");\n const warnings = review.issues.filter((i) => i.severity === \"warning\");\n const suggestions = review.issues.filter((i) => i.severity === \"suggestion\");\n\n if (review.issues.length > 0) {\n logger.info(`π Issues (${review.issues.length}):`);\n for (const issue of [...criticals, ...warnings, ...suggestions]) {\n const icon = issue.severity === \"critical\" ? \"π΄\" : issue.severity === \"warning\" ? \"π‘\" : \"π‘\";\n const loc = issue.file ? ` [${issue.file}${issue.line ? `:${issue.line}` : \"\"}]` : \"\";\n logger.info(` ${icon} ${issue.description}${loc}`);\n }\n logger.info(\"\");\n }\n\n // ββ Inline comments βββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.inlineComments.length > 0) {\n logger.info(`π¬ Inline Comments (${review.inlineComments.length}):`);\n for (const c of review.inlineComments) {\n const icon = c.severity === \"critical\" ? \"π΄\" : c.severity === \"warning\" ? \"π‘\" : \"π‘\";\n logger.info(`\\n ${icon} ${c.path}:${c.line}`);\n logger.info(` ${c.body.replace(/\\n/g, \"\\n \")}`);\n if (c.suggestion) {\n logger.info(` π Suggestion: ${c.suggestion.split(\"\\n\")[0]}β¦`);\n }\n }\n logger.info(\"\");\n }\n\n // ββ Positives βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.positives.length > 0) {\n logger.info(\"π Positives:\");\n for (const p of review.positives) logger.info(` β ${p}`);\n logger.info(\"\");\n }\n\n // ββ Testing notes βββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.testingNotes) {\n logger.info(`π§ͺ How to test:\\n ${review.testingNotes.replace(/\\n/g, \"\\n \")}\\n`);\n }\n\n // ββ Existing PR comments ββββββββββββββββββββββββββββββββββββββββββββββ\n if (comments.length > 0) {\n logger.info(`π¬ Existing comments on PR (${comments.length}):`);\n for (const c of comments.slice(0, 4)) {\n const loc = c.path ? ` @ ${c.path}${c.line ? `:${c.line}` : \"\"}` : \"\";\n logger.info(\n ` [${c.author}${loc}]: ${c.body.slice(0, 120)}${c.body.length > 120 ? \"β¦\" : \"\"}`\n );\n }\n if (comments.length > 4) logger.info(` β¦ and ${comments.length - 4} more.`);\n logger.info(\"\");\n }\n\n // ββ Status ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (!postComment) {\n logger.info(\"βΉοΈ Review shown locally only (--no-comment).\");\n } else if (result.reviewPosted) {\n const inlineCount = review.inlineComments.length;\n if (inlineCount > 0) {\n logger.success(\n `β
Review posted to PR. ${inlineCount} inline comment(s) β check the PR to see delivery method.`\n );\n logger.info(\n ` (If lines were outside the diff, comments were posted as plain PR comments instead.)`\n );\n } else {\n logger.success(\"β
Review posted to PR.\");\n }\n } else {\n logger.warn(\n \"β οΈ Review could not be posted to the PR (see error above).\\n\" +\n \" The full review is shown above β you can copy it manually.\"\n );\n }\n\n // To address the review comments, run: gitx pr fix-comments <PR_number>\n });\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/review.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\nimport { runReviewWorkflow } from \"../../../workflows/pr.js\";\n\nexport function registerPrReviewCommand(pr: Command): void {\n pr.command(\"review\")\n .description(\"π§ Senior-dev AI review: inline comments, checklist, verdict\")\n .argument(\"<id>\", \"Pull request number\")\n .option(\"--no-comment\", \"Show review locally only β do not post to PR\")\n .option(\"--no-inline\", \"Post overall review but skip inline file comments\")\n .action(async (id: string, options: { comment: boolean; inline: boolean }) => {\n const prNumber = parseInt(id, 10);\n if (isNaN(prNumber) || prNumber <= 0) {\n logger.error(`Invalid PR number: ${id}`);\n process.exit(1);\n }\n\n const gitx = await Gitx.fromCwd();\n\n if (!await Gitx.isAiAvailable(gitx.config)) {\n logger.warn(\n \"β οΈ No AI provider configured β review will not be meaningful.\\n\" +\n \" Run: gitx config set anthropic (or openai / claude-cli)\"\n );\n return;\n }\n\n const postComment = options.comment !== false;\n\n logger.info(`\\nπ§ Senior-dev AI review β PR #${prNumber}\\n`);\n\n let result: Awaited<ReturnType<typeof runReviewWorkflow>>;\n try {\n result = await runReviewWorkflow(gitx, prNumber, postComment);\n } catch (err) {\n logger.error(`Review failed: ${String((err as Error).message ?? err)}`);\n process.exitCode = 1;\n return;\n }\n\n const { pr: pullReq, review, comments } = result;\n\n // ββ PR header βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n logger.info(`\\nπ ${pullReq.title}`);\n logger.info(` ${pullReq.head} β ${pullReq.base} Β· by ${pullReq.author} Β· ${pullReq.state}`);\n logger.info(` ${pullReq.url}\\n`);\n\n if (!review) {\n logger.info(`π€ Review:\\n${result.aiSummary}`);\n return;\n }\n\n // ββ Verdict βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n const verdictIcon =\n review.verdict === \"approve\"\n ? \"β
APPROVE\"\n : review.verdict === \"request_changes\"\n ? \"π΄ REQUEST CHANGES\"\n : \"π¬ COMMENT\";\n\n logger.info(`π€ Verdict: ${verdictIcon}\\n`);\n\n // ββ Summary βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n logger.info(`π Summary:\\n ${review.summary.replace(/\\n/g, \"\\n \")}\\n`);\n\n // ββ Checklist βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.checklist.length > 0) {\n logger.info(\"β
Review Checklist:\");\n for (const item of review.checklist) {\n const icon = item.status === \"pass\" ? \"β
\" : item.status === \"warn\" ? \"β οΈ \" : \"β\";\n logger.info(` ${icon} ${item.area.padEnd(20)} ${item.note}`);\n }\n logger.info(\"\");\n }\n\n // ββ Issues ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n const criticals = review.issues.filter((i) => i.severity === \"critical\");\n const warnings = review.issues.filter((i) => i.severity === \"warning\");\n const suggestions = review.issues.filter((i) => i.severity === \"suggestion\");\n\n if (review.issues.length > 0) {\n logger.info(`π Issues (${review.issues.length}):`);\n for (const issue of [...criticals, ...warnings, ...suggestions]) {\n const icon = issue.severity === \"critical\" ? \"π΄\" : issue.severity === \"warning\" ? \"π‘\" : \"π‘\";\n const loc = issue.file ? ` [${issue.file}${issue.line ? `:${issue.line}` : \"\"}]` : \"\";\n logger.info(` ${icon} ${issue.description}${loc}`);\n }\n logger.info(\"\");\n }\n\n // ββ Inline comments βββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.inlineComments.length > 0) {\n logger.info(`π¬ Inline Comments (${review.inlineComments.length}):`);\n for (const c of review.inlineComments) {\n const icon = c.severity === \"critical\" ? \"π΄\" : c.severity === \"warning\" ? \"π‘\" : \"π‘\";\n logger.info(`\\n ${icon} ${c.path}:${c.line}`);\n logger.info(` ${c.body.replace(/\\n/g, \"\\n \")}`);\n if (c.suggestion) {\n logger.info(` π Suggestion: ${c.suggestion.split(\"\\n\")[0]}β¦`);\n }\n }\n logger.info(\"\");\n }\n\n // ββ Positives βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.positives.length > 0) {\n logger.info(\"π Positives:\");\n for (const p of review.positives) logger.info(` β ${p}`);\n logger.info(\"\");\n }\n\n // ββ Testing notes βββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (review.testingNotes) {\n logger.info(`π§ͺ How to test:\\n ${review.testingNotes.replace(/\\n/g, \"\\n \")}\\n`);\n }\n\n // ββ Existing PR comments ββββββββββββββββββββββββββββββββββββββββββββββ\n if (comments.length > 0) {\n logger.info(`π¬ Existing comments on PR (${comments.length}):`);\n for (const c of comments.slice(0, 4)) {\n const loc = c.path ? ` @ ${c.path}${c.line ? `:${c.line}` : \"\"}` : \"\";\n logger.info(\n ` [${c.author}${loc}]: ${c.body.slice(0, 120)}${c.body.length > 120 ? \"β¦\" : \"\"}`\n );\n }\n if (comments.length > 4) logger.info(` β¦ and ${comments.length - 4} more.`);\n logger.info(\"\");\n }\n\n // ββ Status ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n if (!postComment) {\n logger.info(\"βΉοΈ Review shown locally only (--no-comment).\");\n } else if (result.reviewPosted) {\n const inlineCount = review.inlineComments.length;\n if (inlineCount > 0) {\n logger.success(\n `β
Review posted to PR. ${inlineCount} inline comment(s) β check the PR to see delivery method.`\n );\n logger.info(\n ` (If lines were outside the diff, comments were posted as plain PR comments instead.)`\n );\n } else {\n logger.success(\"β
Review posted to PR.\");\n }\n } else {\n logger.warn(\n \"β οΈ Review could not be posted to the PR (see error above).\\n\" +\n \" The full review is shown above β you can copy it manually.\"\n );\n }\n\n // To resolve the review comments, run: gitx pr resolve <PR_number>\n });\n}\n\n"]}
|
package/dist/utils/gitOps.d.ts
CHANGED
|
@@ -62,10 +62,17 @@ export declare function getWorkingDiffStat(cwd?: string): Promise<string>;
|
|
|
62
62
|
* Auto-detect the most likely base branch for the current feature branch.
|
|
63
63
|
*
|
|
64
64
|
* Strategy:
|
|
65
|
-
* 1.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* 1. Upstream tracking branch set via `git push -u` β strongest signal.
|
|
66
|
+
* Ignored if it points to the branch's own remote ref (origin/current).
|
|
67
|
+
* 2. Scan ALL remote tracking branches and count how many commits HEAD has
|
|
68
|
+
* that each candidate doesn't (`git rev-list --count <ref>..HEAD`).
|
|
69
|
+
* The branch with the FEWEST such commits is the closest ancestor β
|
|
70
|
+
* i.e. the branch this one was forked from.
|
|
71
|
+
*
|
|
72
|
+
* Example: main β feature1 (3 commits) β feature2 (2 commits)
|
|
73
|
+
* - origin/main..HEAD = 5 (feature1's 3 + feature2's 2)
|
|
74
|
+
* - origin/feature1..HEAD = 2 (just feature2's commits)
|
|
75
|
+
* β feature1 wins β
|
|
69
76
|
*
|
|
70
77
|
* Falls back to "main" if nothing can be determined.
|
|
71
78
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitOps.d.ts","sourceRoot":"","sources":["../../src/utils/gitOps.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH,wBAAsB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,SAAgB,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,GAAG,SAAgB,GAClB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,MAAM,CAQvE;AAID;;;GAGG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,GAAG,SAAgB,GAClB,OAAO,CAAC,IAAI,CAAC,CAIf;AAID;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,GAAG,SAAgB,GAClB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAe1C;AAID,wBAAsB,QAAQ,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;AAED,wBAAsB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5E;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,GAAG,SAAgB,GAClB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAID,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,GAAG,SAAgB,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAID;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAU7E;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQzE;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ7E;AAED
|
|
1
|
+
{"version":3,"file":"gitOps.d.ts","sourceRoot":"","sources":["../../src/utils/gitOps.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/gitOps.js
CHANGED
|
@@ -205,20 +205,24 @@ export async function getWorkingDiffStat(cwd = process.cwd()) {
|
|
|
205
205
|
* Auto-detect the most likely base branch for the current feature branch.
|
|
206
206
|
*
|
|
207
207
|
* Strategy:
|
|
208
|
-
* 1.
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
208
|
+
* 1. Upstream tracking branch set via `git push -u` β strongest signal.
|
|
209
|
+
* Ignored if it points to the branch's own remote ref (origin/current).
|
|
210
|
+
* 2. Scan ALL remote tracking branches and count how many commits HEAD has
|
|
211
|
+
* that each candidate doesn't (`git rev-list --count <ref>..HEAD`).
|
|
212
|
+
* The branch with the FEWEST such commits is the closest ancestor β
|
|
213
|
+
* i.e. the branch this one was forked from.
|
|
214
|
+
*
|
|
215
|
+
* Example: main β feature1 (3 commits) β feature2 (2 commits)
|
|
216
|
+
* - origin/main..HEAD = 5 (feature1's 3 + feature2's 2)
|
|
217
|
+
* - origin/feature1..HEAD = 2 (just feature2's commits)
|
|
218
|
+
* β feature1 wins β
|
|
212
219
|
*
|
|
213
220
|
* Falls back to "main" if nothing can be determined.
|
|
214
221
|
*/
|
|
215
222
|
export async function detectBaseBranch(cwd = process.cwd()) {
|
|
216
|
-
// Get current branch name upfront β used for all checks below
|
|
217
223
|
const current = await git(["rev-parse", "--abbrev-ref", "HEAD"], cwd).catch(() => "");
|
|
218
|
-
// 1.
|
|
219
|
-
// branch (
|
|
220
|
-
// e.g. "origin/gitx/test" β "gitx/test" == current β skip
|
|
221
|
-
// "origin/main" β "main" != current β use it
|
|
224
|
+
// 1. Explicit upstream tracking branch β only trust it if it differs from
|
|
225
|
+
// the current branch (guards against "origin/feature2" β "feature2").
|
|
222
226
|
try {
|
|
223
227
|
const upstream = await git(["rev-parse", "--abbrev-ref", "@{upstream}"], cwd);
|
|
224
228
|
const branch = upstream.replace(/^[^/]+\//, "").trim();
|
|
@@ -226,39 +230,51 @@ export async function detectBaseBranch(cwd = process.cwd()) {
|
|
|
226
230
|
return branch;
|
|
227
231
|
}
|
|
228
232
|
catch {
|
|
229
|
-
// No upstream
|
|
233
|
+
// No upstream set β fall through
|
|
230
234
|
}
|
|
231
|
-
// 2.
|
|
235
|
+
// 2. Scan all remote tracking branches (origin/*), compute how many commits
|
|
236
|
+
// HEAD has that each remote ref doesn't. Closest ancestor wins.
|
|
232
237
|
try {
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
const remoteOut = await git(["branch", "-r", "--format=%(refname:short)"], cwd);
|
|
239
|
+
const remoteRefs = remoteOut
|
|
240
|
+
.split("\n")
|
|
241
|
+
.map((b) => b.trim())
|
|
242
|
+
.filter((b) => b && !b.endsWith("/HEAD")); // skip "origin/HEAD" pointer
|
|
243
|
+
if (remoteRefs.length === 0)
|
|
244
|
+
return "main";
|
|
245
|
+
// Count commits ahead of every remote ref in parallel for speed
|
|
246
|
+
const results = await Promise.all(remoteRefs.map(async (ref) => {
|
|
247
|
+
const name = ref.replace(/^[^/]+\//, ""); // "origin/feature1" β "feature1"
|
|
248
|
+
if (name === current)
|
|
249
|
+
return null; // skip own remote tracking ref
|
|
250
|
+
try {
|
|
251
|
+
const out = await git(["rev-list", "--count", `${ref}..HEAD`], cwd);
|
|
252
|
+
return { branch: name, ahead: parseInt(out.trim(), 10) || 0 };
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}));
|
|
258
|
+
const counts = results.filter((r) => r !== null);
|
|
259
|
+
if (counts.length === 0)
|
|
260
|
+
return "main";
|
|
261
|
+
// Sort by fewest commits ahead β that's the closest ancestor.
|
|
262
|
+
// Tiebreak: prefer default branch names so "main" beats an equally-close
|
|
263
|
+
// unrelated branch that happens to have 0 commits.
|
|
264
|
+
const defaults = new Set(["main", "master", "develop", "dev", "staging"]);
|
|
265
|
+
counts.sort((a, b) => {
|
|
266
|
+
if (a.ahead !== b.ahead)
|
|
267
|
+
return a.ahead - b.ahead;
|
|
268
|
+
// Same distance β prefer default branches
|
|
269
|
+
const aDefault = defaults.has(a.branch) ? 0 : 1;
|
|
270
|
+
const bDefault = defaults.has(b.branch) ? 0 : 1;
|
|
271
|
+
return aDefault - bDefault;
|
|
272
|
+
});
|
|
273
|
+
return counts[0].branch;
|
|
237
274
|
}
|
|
238
275
|
catch {
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
// 3. Count commits ahead of each common default branch name
|
|
242
|
-
const candidates = ["main", "master", "develop", "dev", "staging"];
|
|
243
|
-
const counts = [];
|
|
244
|
-
for (const candidate of candidates) {
|
|
245
|
-
if (candidate === current)
|
|
246
|
-
continue;
|
|
247
|
-
try {
|
|
248
|
-
// Count commits on HEAD not in candidate
|
|
249
|
-
const out = await git(["rev-list", "--count", `${candidate}..HEAD`], cwd);
|
|
250
|
-
counts.push({ branch: candidate, ahead: parseInt(out.trim(), 10) || 0 });
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
// Branch doesn't exist locally β skip
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (counts.length > 0) {
|
|
257
|
-
// Pick the branch with the fewest commits ahead (closest ancestor)
|
|
258
|
-
counts.sort((a, b) => a.ahead - b.ahead);
|
|
259
|
-
return counts[0].branch;
|
|
276
|
+
return "main";
|
|
260
277
|
}
|
|
261
|
-
return "main";
|
|
262
278
|
}
|
|
263
279
|
/**
|
|
264
280
|
* Get the one-line commit log for commits on HEAD that are not in baseBranch.
|
package/dist/utils/gitOps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitOps.js","sourceRoot":"","sources":["../../src/utils/gitOps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extended git operations for the implement workflow.\n * All functions execute native git commands via child_process.\n */\n\nimport { execFile, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, resolve, join } from \"node:path\";\nimport { GitxError } from \"./errors.js\";\n\nconst execFileAsync = promisify(execFile);\nconst execAsync = promisify(exec);\n\n// βββ Internal helper ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nasync function git(args: string[], cwd: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return String(stdout ?? \"\").trim();\n } catch (err: unknown) {\n const stderr =\n (err as { stderr?: string }).stderr ??\n (err as Error).message ??\n String(err);\n throw new GitxError(`git ${args[0]} failed: ${stderr.trim()}`, {\n exitCode: 1,\n cause: err,\n });\n }\n}\n\n// βββ Branch operations ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function getCurrentBranch(cwd = process.cwd()): Promise<string> {\n return git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n}\n\n/**\n * Resolve the default branch (main/master/develop) by inspecting the remote.\n * Falls back to \"main\" if nothing can be determined.\n */\nexport async function getDefaultBranchFromGit(\n cwd = process.cwd(),\n configuredDefault?: string\n): Promise<string> {\n if (configuredDefault) return configuredDefault;\n\n try {\n // Try to read from remote HEAD reference\n const out = await git(\n [\"rev-parse\", \"--abbrev-ref\", \"origin/HEAD\"],\n cwd\n );\n // \"origin/main\" β \"main\"\n return out.replace(/^origin\\//, \"\") || \"main\";\n } catch {\n // Fall back to checking common branch names\n try {\n const branches = await git(\n [\"branch\", \"-r\", \"--format=%(refname:short)\"],\n cwd\n );\n const candidates = [\"origin/main\", \"origin/master\", \"origin/develop\"];\n for (const candidate of candidates) {\n if (branches.split(\"\\n\").some((b) => b.trim() === candidate)) {\n return candidate.replace(\"origin/\", \"\");\n }\n }\n } catch {\n /* ignore */\n }\n return \"main\";\n }\n}\n\n/**\n * Create and checkout a new branch.\n * If the branch already exists, just check it out.\n */\nexport async function createAndCheckoutBranch(\n branchName: string,\n cwd = process.cwd()\n): Promise<void> {\n try {\n await git([\"checkout\", \"-b\", branchName], cwd);\n } catch {\n // Branch might already exist β try checking it out\n await git([\"checkout\", branchName], cwd);\n }\n}\n\n/** Sanitise a free-form task string into a valid branch name */\nexport function slugifyBranchName(task: string, prefix = \"gitx\"): string {\n const slug = task\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 50);\n const ts = Date.now().toString(36);\n return `${prefix}/${slug}-${ts}`;\n}\n\n// βββ File operations ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Write content to a file inside the repo, creating parent directories as needed.\n * Paths should be relative to `cwd`.\n */\nexport async function writeRepoFile(\n relativePath: string,\n content: string,\n cwd = process.cwd()\n): Promise<void> {\n const abs = resolve(join(cwd, relativePath));\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, content, \"utf-8\");\n}\n\n// βββ Diff application βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Apply a unified diff string using `git apply`.\n * Returns `true` if applied cleanly, `false` if it failed (caller decides how\n * to handle partial failures).\n */\nexport async function applyUnifiedDiff(\n unifiedDiff: string,\n cwd = process.cwd()\n): Promise<{ ok: boolean; error?: string }> {\n try {\n // Use --3way to handle minor conflicts gracefully\n const { stdout, stderr } = await execAsync(\n `echo ${JSON.stringify(unifiedDiff)} | git apply --3way --whitespace=fix -`,\n { cwd }\n );\n return { ok: true, error: stderr || stdout || undefined };\n } catch (err: unknown) {\n const stderr =\n (err as { stderr?: string }).stderr ??\n (err as Error).message ??\n String(err);\n return { ok: false, error: stderr.trim() };\n }\n}\n\n// βββ Staging & committing βββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function stageAll(cwd = process.cwd()): Promise<void> {\n await git([\"add\", \"-A\"], cwd);\n}\n\nexport async function hasStagedChanges(cwd = process.cwd()): Promise<boolean> {\n try {\n const out = await git([\"diff\", \"--cached\", \"--name-only\"], cwd);\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function commitChanges(\n message: string,\n cwd = process.cwd()\n): Promise<string> {\n await git([\"commit\", \"-m\", message], cwd);\n return git([\"rev-parse\", \"HEAD\"], cwd);\n}\n\n// βββ Push βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function pushBranch(\n branchName: string,\n cwd = process.cwd()\n): Promise<void> {\n await git([\"push\", \"--set-upstream\", \"origin\", branchName], cwd);\n}\n\n// βββ Repo inspection ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * List all tracked files in the repo (respects .gitignore).\n * Returns paths relative to `cwd`.\n */\nexport async function listTrackedFiles(cwd = process.cwd()): Promise<string[]> {\n try {\n const out = await git([\"ls-files\"], cwd);\n return out\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the unified diff of only staged changes (`git diff --cached`).\n * Use this when the caller wants to commit only what's already in the index.\n */\nexport async function getStagedDiff(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"diff\", \"--cached\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get a compact stat summary of only staged changes (`git diff --cached --stat`).\n */\nexport async function getStagedDiffStat(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"diff\", \"--cached\", \"--stat\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get the unified diff of all uncommitted changes (staged + unstaged).\n */\nexport async function getWorkingDiff(cwd = process.cwd()): Promise<string> {\n try {\n const staged = await git([\"diff\", \"--cached\"], cwd);\n const unstaged = await git([\"diff\"], cwd);\n return [staged, unstaged].filter(Boolean).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get a compact summary of staged changes (--stat format):\n * lists every changed file with insertion/deletion counts.\n * Always small regardless of diff size β used to give the AI the\n * complete picture of what changed even when the full diff is truncated.\n */\nexport async function getWorkingDiffStat(cwd = process.cwd()): Promise<string> {\n try {\n const staged = await git([\"diff\", \"--cached\", \"--stat\"], cwd);\n const unstaged = await git([\"diff\", \"--stat\"], cwd);\n return [staged, unstaged].filter(Boolean).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Auto-detect the most likely base branch for the current feature branch.\n *\n * Strategy:\n * 1. Check if the current branch has a configured upstream tracking branch.\n * 2. Otherwise, try common default branch names (main, master, develop, dev).\n * 3. For each candidate, count commits on HEAD that are NOT in that branch.\n * The candidate with the fewest such commits is the likely origin.\n *\n * Falls back to \"main\" if nothing can be determined.\n */\nexport async function detectBaseBranch(cwd = process.cwd()): Promise<string> {\n // Get current branch name upfront β used for all checks below\n const current = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd).catch(() => \"\");\n\n // 1. Try upstream tracking branch β only useful if it points to a DIFFERENT\n // branch (e.g. origin/main), not the branch's own remote tracking ref.\n // e.g. \"origin/gitx/test\" β \"gitx/test\" == current β skip\n // \"origin/main\" β \"main\" != current β use it\n try {\n const upstream = await git([\"rev-parse\", \"--abbrev-ref\", \"@{upstream}\"], cwd);\n const branch = upstream.replace(/^[^/]+\\//, \"\").trim();\n if (branch && branch !== current) return branch;\n } catch {\n // No upstream configured β fall through\n }\n\n // 2. Check remote HEAD (origin's default branch)\n try {\n const remoteHead = await git([\"rev-parse\", \"--abbrev-ref\", \"origin/HEAD\"], cwd);\n const branch = remoteHead.replace(/^origin\\//, \"\").trim();\n if (branch && branch !== current) return branch;\n } catch {\n // Not available β fall through\n }\n\n // 3. Count commits ahead of each common default branch name\n const candidates = [\"main\", \"master\", \"develop\", \"dev\", \"staging\"];\n const counts: Array<{ branch: string; ahead: number }> = [];\n for (const candidate of candidates) {\n if (candidate === current) continue;\n try {\n // Count commits on HEAD not in candidate\n const out = await git([\"rev-list\", \"--count\", `${candidate}..HEAD`], cwd);\n counts.push({ branch: candidate, ahead: parseInt(out.trim(), 10) || 0 });\n } catch {\n // Branch doesn't exist locally β skip\n }\n }\n\n if (counts.length > 0) {\n // Pick the branch with the fewest commits ahead (closest ancestor)\n counts.sort((a, b) => a.ahead - b.ahead);\n return counts[0]!.branch;\n }\n\n return \"main\";\n}\n\n/**\n * Get the one-line commit log for commits on HEAD that are not in baseBranch.\n * Used to give AI context about what this branch adds.\n */\nexport async function getBranchCommits(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string[]> {\n try {\n const out = await git(\n [\"log\", \"--oneline\", \"--no-decorate\", `${baseBranch}..HEAD`],\n cwd\n );\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the unified diff of all changes between baseBranch and HEAD.\n * This is what the PR reviewer would see β all additions across all commits.\n */\nexport async function getBranchDiff(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string> {\n try {\n // Three-dot diff: all changes introduced by this branch vs. the merge base\n return await git([\"diff\", `${baseBranch}...HEAD`], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get the --stat summary of all changes between baseBranch and HEAD.\n * Lists every file changed with insertion/deletion counts.\n * Used alongside a truncated diff so the AI sees the full file list even\n * when the detailed patch is cut off.\n */\nexport async function getBranchStat(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string> {\n try {\n return await git([\"diff\", \"--stat\", `${baseBranch}...HEAD`], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Read file content as a string. Returns empty string if file doesn't exist.\n */\nexport async function readRepoFile(\n relativePath: string,\n cwd = process.cwd()\n): Promise<string | undefined> {\n try {\n const abs = resolve(join(cwd, relativePath));\n const { readFile } = await import(\"node:fs/promises\");\n return await readFile(abs, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * Check whether a branch exists on the remote (origin).\n * Uses git ls-remote which does not require a full fetch.\n */\nexport async function branchExistsOnRemote(\n branchName: string,\n cwd = process.cwd()\n): Promise<boolean> {\n try {\n const out = await git(\n [\"ls-remote\", \"--heads\", \"origin\", `refs/heads/${branchName}`],\n cwd\n );\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns true if the working tree has uncommitted changes (staged or unstaged).\n */\nexport async function isWorkingTreeDirty(cwd = process.cwd()): Promise<boolean> {\n try {\n const out = await git([\"status\", \"--porcelain\"], cwd);\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n// βββ Context helpers for `gitx ask` ββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Returns `git status --short` output for use in AI context.\n * Returns an empty string when the working tree is clean or git fails.\n */\nexport async function getGitStatus(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"status\", \"--short\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Returns the last `count` commits as one-line summaries (hash + subject).\n * Defaults to the last 10 commits.\n */\nexport async function getRecentCommits(cwd = process.cwd(), count = 10): Promise<string[]> {\n try {\n const out = await git([\"log\", \"--oneline\", \"--no-decorate\", `-${count}`], cwd);\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns the list of git stashes as `stash@{0}: ...` strings.\n * Returns an empty array when there are no stashes or git fails.\n */\nexport async function getStashList(cwd = process.cwd()): Promise<string[]> {\n try {\n const out = await git([\"stash\", \"list\"], cwd);\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"gitOps.js","sourceRoot":"","sources":["../../src/utils/gitOps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extended git operations for the implement workflow.\n * All functions execute native git commands via child_process.\n */\n\nimport { execFile, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, resolve, join } from \"node:path\";\nimport { GitxError } from \"./errors.js\";\n\nconst execFileAsync = promisify(execFile);\nconst execAsync = promisify(exec);\n\n// βββ Internal helper ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nasync function git(args: string[], cwd: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return String(stdout ?? \"\").trim();\n } catch (err: unknown) {\n const stderr =\n (err as { stderr?: string }).stderr ??\n (err as Error).message ??\n String(err);\n throw new GitxError(`git ${args[0]} failed: ${stderr.trim()}`, {\n exitCode: 1,\n cause: err,\n });\n }\n}\n\n// βββ Branch operations ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function getCurrentBranch(cwd = process.cwd()): Promise<string> {\n return git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n}\n\n/**\n * Resolve the default branch (main/master/develop) by inspecting the remote.\n * Falls back to \"main\" if nothing can be determined.\n */\nexport async function getDefaultBranchFromGit(\n cwd = process.cwd(),\n configuredDefault?: string\n): Promise<string> {\n if (configuredDefault) return configuredDefault;\n\n try {\n // Try to read from remote HEAD reference\n const out = await git(\n [\"rev-parse\", \"--abbrev-ref\", \"origin/HEAD\"],\n cwd\n );\n // \"origin/main\" β \"main\"\n return out.replace(/^origin\\//, \"\") || \"main\";\n } catch {\n // Fall back to checking common branch names\n try {\n const branches = await git(\n [\"branch\", \"-r\", \"--format=%(refname:short)\"],\n cwd\n );\n const candidates = [\"origin/main\", \"origin/master\", \"origin/develop\"];\n for (const candidate of candidates) {\n if (branches.split(\"\\n\").some((b) => b.trim() === candidate)) {\n return candidate.replace(\"origin/\", \"\");\n }\n }\n } catch {\n /* ignore */\n }\n return \"main\";\n }\n}\n\n/**\n * Create and checkout a new branch.\n * If the branch already exists, just check it out.\n */\nexport async function createAndCheckoutBranch(\n branchName: string,\n cwd = process.cwd()\n): Promise<void> {\n try {\n await git([\"checkout\", \"-b\", branchName], cwd);\n } catch {\n // Branch might already exist β try checking it out\n await git([\"checkout\", branchName], cwd);\n }\n}\n\n/** Sanitise a free-form task string into a valid branch name */\nexport function slugifyBranchName(task: string, prefix = \"gitx\"): string {\n const slug = task\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 50);\n const ts = Date.now().toString(36);\n return `${prefix}/${slug}-${ts}`;\n}\n\n// βββ File operations ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Write content to a file inside the repo, creating parent directories as needed.\n * Paths should be relative to `cwd`.\n */\nexport async function writeRepoFile(\n relativePath: string,\n content: string,\n cwd = process.cwd()\n): Promise<void> {\n const abs = resolve(join(cwd, relativePath));\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, content, \"utf-8\");\n}\n\n// βββ Diff application βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Apply a unified diff string using `git apply`.\n * Returns `true` if applied cleanly, `false` if it failed (caller decides how\n * to handle partial failures).\n */\nexport async function applyUnifiedDiff(\n unifiedDiff: string,\n cwd = process.cwd()\n): Promise<{ ok: boolean; error?: string }> {\n try {\n // Use --3way to handle minor conflicts gracefully\n const { stdout, stderr } = await execAsync(\n `echo ${JSON.stringify(unifiedDiff)} | git apply --3way --whitespace=fix -`,\n { cwd }\n );\n return { ok: true, error: stderr || stdout || undefined };\n } catch (err: unknown) {\n const stderr =\n (err as { stderr?: string }).stderr ??\n (err as Error).message ??\n String(err);\n return { ok: false, error: stderr.trim() };\n }\n}\n\n// βββ Staging & committing βββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function stageAll(cwd = process.cwd()): Promise<void> {\n await git([\"add\", \"-A\"], cwd);\n}\n\nexport async function hasStagedChanges(cwd = process.cwd()): Promise<boolean> {\n try {\n const out = await git([\"diff\", \"--cached\", \"--name-only\"], cwd);\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function commitChanges(\n message: string,\n cwd = process.cwd()\n): Promise<string> {\n await git([\"commit\", \"-m\", message], cwd);\n return git([\"rev-parse\", \"HEAD\"], cwd);\n}\n\n// βββ Push βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\nexport async function pushBranch(\n branchName: string,\n cwd = process.cwd()\n): Promise<void> {\n await git([\"push\", \"--set-upstream\", \"origin\", branchName], cwd);\n}\n\n// βββ Repo inspection ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * List all tracked files in the repo (respects .gitignore).\n * Returns paths relative to `cwd`.\n */\nexport async function listTrackedFiles(cwd = process.cwd()): Promise<string[]> {\n try {\n const out = await git([\"ls-files\"], cwd);\n return out\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the unified diff of only staged changes (`git diff --cached`).\n * Use this when the caller wants to commit only what's already in the index.\n */\nexport async function getStagedDiff(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"diff\", \"--cached\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get a compact stat summary of only staged changes (`git diff --cached --stat`).\n */\nexport async function getStagedDiffStat(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"diff\", \"--cached\", \"--stat\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get the unified diff of all uncommitted changes (staged + unstaged).\n */\nexport async function getWorkingDiff(cwd = process.cwd()): Promise<string> {\n try {\n const staged = await git([\"diff\", \"--cached\"], cwd);\n const unstaged = await git([\"diff\"], cwd);\n return [staged, unstaged].filter(Boolean).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get a compact summary of staged changes (--stat format):\n * lists every changed file with insertion/deletion counts.\n * Always small regardless of diff size β used to give the AI the\n * complete picture of what changed even when the full diff is truncated.\n */\nexport async function getWorkingDiffStat(cwd = process.cwd()): Promise<string> {\n try {\n const staged = await git([\"diff\", \"--cached\", \"--stat\"], cwd);\n const unstaged = await git([\"diff\", \"--stat\"], cwd);\n return [staged, unstaged].filter(Boolean).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Auto-detect the most likely base branch for the current feature branch.\n *\n * Strategy:\n * 1. Upstream tracking branch set via `git push -u` β strongest signal.\n * Ignored if it points to the branch's own remote ref (origin/current).\n * 2. Scan ALL remote tracking branches and count how many commits HEAD has\n * that each candidate doesn't (`git rev-list --count <ref>..HEAD`).\n * The branch with the FEWEST such commits is the closest ancestor β\n * i.e. the branch this one was forked from.\n *\n * Example: main β feature1 (3 commits) β feature2 (2 commits)\n * - origin/main..HEAD = 5 (feature1's 3 + feature2's 2)\n * - origin/feature1..HEAD = 2 (just feature2's commits)\n * β feature1 wins β\n *\n * Falls back to \"main\" if nothing can be determined.\n */\nexport async function detectBaseBranch(cwd = process.cwd()): Promise<string> {\n const current = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd).catch(() => \"\");\n\n // 1. Explicit upstream tracking branch β only trust it if it differs from\n // the current branch (guards against \"origin/feature2\" β \"feature2\").\n try {\n const upstream = await git([\"rev-parse\", \"--abbrev-ref\", \"@{upstream}\"], cwd);\n const branch = upstream.replace(/^[^/]+\\//, \"\").trim();\n if (branch && branch !== current) return branch;\n } catch {\n // No upstream set β fall through\n }\n\n // 2. Scan all remote tracking branches (origin/*), compute how many commits\n // HEAD has that each remote ref doesn't. Closest ancestor wins.\n try {\n const remoteOut = await git(\n [\"branch\", \"-r\", \"--format=%(refname:short)\"],\n cwd\n );\n\n const remoteRefs = remoteOut\n .split(\"\\n\")\n .map((b) => b.trim())\n .filter((b) => b && !b.endsWith(\"/HEAD\")); // skip \"origin/HEAD\" pointer\n\n if (remoteRefs.length === 0) return \"main\";\n\n // Count commits ahead of every remote ref in parallel for speed\n const results = await Promise.all(\n remoteRefs.map(async (ref) => {\n const name = ref.replace(/^[^/]+\\//, \"\"); // \"origin/feature1\" β \"feature1\"\n if (name === current) return null; // skip own remote tracking ref\n try {\n const out = await git([\"rev-list\", \"--count\", `${ref}..HEAD`], cwd);\n return { branch: name, ahead: parseInt(out.trim(), 10) || 0 };\n } catch {\n return null;\n }\n })\n );\n\n const counts = results.filter((r): r is { branch: string; ahead: number } => r !== null);\n if (counts.length === 0) return \"main\";\n\n // Sort by fewest commits ahead β that's the closest ancestor.\n // Tiebreak: prefer default branch names so \"main\" beats an equally-close\n // unrelated branch that happens to have 0 commits.\n const defaults = new Set([\"main\", \"master\", \"develop\", \"dev\", \"staging\"]);\n counts.sort((a, b) => {\n if (a.ahead !== b.ahead) return a.ahead - b.ahead;\n // Same distance β prefer default branches\n const aDefault = defaults.has(a.branch) ? 0 : 1;\n const bDefault = defaults.has(b.branch) ? 0 : 1;\n return aDefault - bDefault;\n });\n\n return counts[0]!.branch;\n } catch {\n return \"main\";\n }\n}\n\n/**\n * Get the one-line commit log for commits on HEAD that are not in baseBranch.\n * Used to give AI context about what this branch adds.\n */\nexport async function getBranchCommits(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string[]> {\n try {\n const out = await git(\n [\"log\", \"--oneline\", \"--no-decorate\", `${baseBranch}..HEAD`],\n cwd\n );\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Get the unified diff of all changes between baseBranch and HEAD.\n * This is what the PR reviewer would see β all additions across all commits.\n */\nexport async function getBranchDiff(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string> {\n try {\n // Three-dot diff: all changes introduced by this branch vs. the merge base\n return await git([\"diff\", `${baseBranch}...HEAD`], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get the --stat summary of all changes between baseBranch and HEAD.\n * Lists every file changed with insertion/deletion counts.\n * Used alongside a truncated diff so the AI sees the full file list even\n * when the detailed patch is cut off.\n */\nexport async function getBranchStat(\n cwd = process.cwd(),\n baseBranch = \"main\"\n): Promise<string> {\n try {\n return await git([\"diff\", \"--stat\", `${baseBranch}...HEAD`], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Read file content as a string. Returns empty string if file doesn't exist.\n */\nexport async function readRepoFile(\n relativePath: string,\n cwd = process.cwd()\n): Promise<string | undefined> {\n try {\n const abs = resolve(join(cwd, relativePath));\n const { readFile } = await import(\"node:fs/promises\");\n return await readFile(abs, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n\n/**\n * Check whether a branch exists on the remote (origin).\n * Uses git ls-remote which does not require a full fetch.\n */\nexport async function branchExistsOnRemote(\n branchName: string,\n cwd = process.cwd()\n): Promise<boolean> {\n try {\n const out = await git(\n [\"ls-remote\", \"--heads\", \"origin\", `refs/heads/${branchName}`],\n cwd\n );\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns true if the working tree has uncommitted changes (staged or unstaged).\n */\nexport async function isWorkingTreeDirty(cwd = process.cwd()): Promise<boolean> {\n try {\n const out = await git([\"status\", \"--porcelain\"], cwd);\n return out.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n// βββ Context helpers for `gitx ask` ββββββββββββββββββββββββββββββββββββββββββ\n\n/**\n * Returns `git status --short` output for use in AI context.\n * Returns an empty string when the working tree is clean or git fails.\n */\nexport async function getGitStatus(cwd = process.cwd()): Promise<string> {\n try {\n return await git([\"status\", \"--short\"], cwd);\n } catch {\n return \"\";\n }\n}\n\n/**\n * Returns the last `count` commits as one-line summaries (hash + subject).\n * Defaults to the last 10 commits.\n */\nexport async function getRecentCommits(cwd = process.cwd(), count = 10): Promise<string[]> {\n try {\n const out = await git([\"log\", \"--oneline\", \"--no-decorate\", `-${count}`], cwd);\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Returns the list of git stashes as `stash@{0}: ...` strings.\n * Returns an empty array when there are no stashes or git fails.\n */\nexport async function getStashList(cwd = process.cwd()): Promise<string[]> {\n try {\n const out = await git([\"stash\", \"list\"], cwd);\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\n } catch {\n return [];\n }\n}\n"]}
|
package/dist/workflows/pr.d.ts
CHANGED
|
@@ -37,5 +37,5 @@ export interface FixCommentsResult {
|
|
|
37
37
|
* Fetch PR review comments, ask AI to suggest fixes, apply them, and
|
|
38
38
|
* commit + push the changes.
|
|
39
39
|
*/
|
|
40
|
-
export declare function runFixCommentsWorkflow(gitx: Gitx, prNumber: number, dryRun?: boolean): Promise<FixCommentsResult>;
|
|
40
|
+
export declare function runFixCommentsWorkflow(gitx: Gitx, prNumber: number, dryRun?: boolean, noCommit?: boolean): Promise<FixCommentsResult>;
|
|
41
41
|
//# sourceMappingURL=pr.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../src/workflows/pr.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA4B5E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,WAAW,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,gBAAgB,EAAE,wBAAwB,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,OAAO,CAAC;IACtB,uFAAuF;IACvF,cAAc,EAAE,QAAQ,GAAG,gBAAgB,GAAG,MAAM,CAAC;CACtD;AA6DD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,WAAW,UAAO,GACjB,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../src/workflows/pr.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA4B5E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,WAAW,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,gBAAgB,EAAE,wBAAwB,CAAC;IAC3D,gEAAgE;IAChE,YAAY,EAAE,OAAO,CAAC;IACtB,uFAAuF;IACvF,cAAc,EAAE,QAAQ,GAAG,gBAAgB,GAAG,MAAM,CAAC;CACtD;AA6DD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,WAAW,UAAO,GACjB,OAAO,CAAC,YAAY,CAAC,CAoJvB;AAID,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,WAAW,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,UAAQ,EACd,QAAQ,UAAQ,GACf,OAAO,CAAC,iBAAiB,CAAC,CA8F5B"}
|
package/dist/workflows/pr.js
CHANGED
|
@@ -96,8 +96,10 @@ export async function runReviewWorkflow(gitx, prNumber, postComment = true) {
|
|
|
96
96
|
const ctxSpinner = ora("Building codebase context from changed filesβ¦").start();
|
|
97
97
|
const allTracked = await listTrackedFiles(cwd);
|
|
98
98
|
const allChangedPaths = parseChangedPathsFromDiff(diff);
|
|
99
|
-
// Filter out lockfiles / generated files β they waste tokens and add no review value
|
|
100
|
-
|
|
99
|
+
// Filter out lockfiles / generated files β they waste tokens and add no review value.
|
|
100
|
+
// No file count cap β per-file budgets in buildSeniorReviewPrompt already control
|
|
101
|
+
// total size. Dropping files arbitrarily is worse than sending all of them.
|
|
102
|
+
const changedPaths = allChangedPaths.filter(isReviewableFile);
|
|
101
103
|
const skippedCount = allChangedPaths.length - changedPaths.length;
|
|
102
104
|
// Read full content of changed files
|
|
103
105
|
const changedFiles = {};
|
|
@@ -107,7 +109,8 @@ export async function runReviewWorkflow(gitx, prNumber, postComment = true) {
|
|
|
107
109
|
changedFiles[p] = content;
|
|
108
110
|
}
|
|
109
111
|
// Read supporting context files (imported by the changed files)
|
|
110
|
-
|
|
112
|
+
// 10 context files gives the AI a solid picture of the codebase structure
|
|
113
|
+
const ctxPaths = await findContextFiles(changedPaths, allTracked, cwd, 10);
|
|
111
114
|
const contextFiles = {};
|
|
112
115
|
for (const p of ctxPaths) {
|
|
113
116
|
const content = await readRepoFile(p, cwd);
|
|
@@ -208,7 +211,7 @@ export async function runReviewWorkflow(gitx, prNumber, postComment = true) {
|
|
|
208
211
|
* Fetch PR review comments, ask AI to suggest fixes, apply them, and
|
|
209
212
|
* commit + push the changes.
|
|
210
213
|
*/
|
|
211
|
-
export async function runFixCommentsWorkflow(gitx, prNumber, dryRun = false) {
|
|
214
|
+
export async function runFixCommentsWorkflow(gitx, prNumber, dryRun = false, noCommit = false) {
|
|
212
215
|
const { applyUnifiedDiff, stageAll, hasStagedChanges, commitChanges } = await import("../utils/gitOps.js");
|
|
213
216
|
const cwd = gitx.cwd;
|
|
214
217
|
const ctx = await gitx.getRepoContext();
|
|
@@ -269,7 +272,7 @@ export async function runFixCommentsWorkflow(gitx, prNumber, dryRun = false) {
|
|
|
269
272
|
});
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
|
-
if (!dryRun && appliedFixes.length > 0) {
|
|
275
|
+
if (!dryRun && !noCommit && appliedFixes.length > 0) {
|
|
273
276
|
await stageAll(cwd);
|
|
274
277
|
if (await hasStagedChanges(cwd)) {
|
|
275
278
|
const msg = `fix: address PR #${prNumber} review comments\n\n${appliedFixes.map((f) => `- ${f.path}: ${f.rationale}`).join("\n")}`;
|
|
@@ -280,6 +283,9 @@ export async function runFixCommentsWorkflow(gitx, prNumber, dryRun = false) {
|
|
|
280
283
|
await provider.addPRComment(ctx.repoSlug, prNumber, `## π€ Auto-fixes applied (gitx)\n\n${fixSummary}\n\nCommit: \`${sha.slice(0, 8)}\``);
|
|
281
284
|
}
|
|
282
285
|
}
|
|
286
|
+
if (!dryRun && noCommit && appliedFixes.length > 0) {
|
|
287
|
+
logger.info("\nπ‘ Fixes applied to working tree. Review the changes and commit when ready.");
|
|
288
|
+
}
|
|
283
289
|
return { pr, comments, appliedFixes, skippedFixes };
|
|
284
290
|
}
|
|
285
291
|
//# sourceMappingURL=pr.js.map
|