@g-abhishek/gitx 0.1.2 → 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.
Files changed (165) hide show
  1. package/README.md +386 -3
  2. package/dist/ai/claudeAi.d.ts +35 -0
  3. package/dist/ai/claudeAi.d.ts.map +1 -0
  4. package/dist/ai/claudeAi.js +396 -0
  5. package/dist/ai/claudeAi.js.map +1 -0
  6. package/dist/ai/claudeCliAi.d.ts +27 -0
  7. package/dist/ai/claudeCliAi.d.ts.map +1 -0
  8. package/dist/ai/claudeCliAi.js +312 -0
  9. package/dist/ai/claudeCliAi.js.map +1 -0
  10. package/dist/ai/localClaudeAi.d.ts +2 -0
  11. package/dist/ai/localClaudeAi.d.ts.map +1 -0
  12. package/dist/ai/localClaudeAi.js +4 -0
  13. package/dist/ai/localClaudeAi.js.map +1 -0
  14. package/dist/ai/mockAi.d.ts +8 -1
  15. package/dist/ai/mockAi.d.ts.map +1 -1
  16. package/dist/ai/mockAi.js +57 -0
  17. package/dist/ai/mockAi.js.map +1 -1
  18. package/dist/ai/openAiAi.d.ts +33 -0
  19. package/dist/ai/openAiAi.d.ts.map +1 -0
  20. package/dist/ai/openAiAi.js +388 -0
  21. package/dist/ai/openAiAi.js.map +1 -0
  22. package/dist/ai/reviewHelpers.d.ts +66 -0
  23. package/dist/ai/reviewHelpers.d.ts.map +1 -0
  24. package/dist/ai/reviewHelpers.js +574 -0
  25. package/dist/ai/reviewHelpers.js.map +1 -0
  26. package/dist/ai/types.d.ts +247 -0
  27. package/dist/ai/types.d.ts.map +1 -1
  28. package/dist/ai/types.js.map +1 -1
  29. package/dist/cli/commands/ask.d.ts +27 -0
  30. package/dist/cli/commands/ask.d.ts.map +1 -0
  31. package/dist/cli/commands/ask.js +230 -0
  32. package/dist/cli/commands/ask.js.map +1 -0
  33. package/dist/cli/commands/commit.d.ts +16 -0
  34. package/dist/cli/commands/commit.d.ts.map +1 -0
  35. package/dist/cli/commands/commit.js +163 -0
  36. package/dist/cli/commands/commit.js.map +1 -0
  37. package/dist/cli/commands/config.d.ts +4 -0
  38. package/dist/cli/commands/config.d.ts.map +1 -0
  39. package/dist/cli/commands/config.js +666 -0
  40. package/dist/cli/commands/config.js.map +1 -0
  41. package/dist/cli/commands/implement.d.ts.map +1 -1
  42. package/dist/cli/commands/implement.js +149 -31
  43. package/dist/cli/commands/implement.js.map +1 -1
  44. package/dist/cli/commands/init.d.ts +4 -0
  45. package/dist/cli/commands/init.d.ts.map +1 -1
  46. package/dist/cli/commands/init.js +7 -69
  47. package/dist/cli/commands/init.js.map +1 -1
  48. package/dist/cli/commands/port.d.ts +32 -0
  49. package/dist/cli/commands/port.d.ts.map +1 -0
  50. package/dist/cli/commands/port.js +554 -0
  51. package/dist/cli/commands/port.js.map +1 -0
  52. package/dist/cli/commands/pr/close.d.ts +15 -0
  53. package/dist/cli/commands/pr/close.d.ts.map +1 -0
  54. package/dist/cli/commands/pr/close.js +71 -0
  55. package/dist/cli/commands/pr/close.js.map +1 -0
  56. package/dist/cli/commands/pr/create.d.ts +17 -0
  57. package/dist/cli/commands/pr/create.d.ts.map +1 -1
  58. package/dist/cli/commands/pr/create.js +208 -7
  59. package/dist/cli/commands/pr/create.js.map +1 -1
  60. package/dist/cli/commands/pr/fixComments.d.ts +5 -2
  61. package/dist/cli/commands/pr/fixComments.d.ts.map +1 -1
  62. package/dist/cli/commands/pr/fixComments.js +5 -13
  63. package/dist/cli/commands/pr/fixComments.js.map +1 -1
  64. package/dist/cli/commands/pr/index.d.ts.map +1 -1
  65. package/dist/cli/commands/pr/index.js +6 -2
  66. package/dist/cli/commands/pr/index.js.map +1 -1
  67. package/dist/cli/commands/pr/list.d.ts.map +1 -1
  68. package/dist/cli/commands/pr/list.js +24 -4
  69. package/dist/cli/commands/pr/list.js.map +1 -1
  70. package/dist/cli/commands/pr/merge.d.ts +23 -0
  71. package/dist/cli/commands/pr/merge.d.ts.map +1 -0
  72. package/dist/cli/commands/pr/merge.js +191 -0
  73. package/dist/cli/commands/pr/merge.js.map +1 -0
  74. package/dist/cli/commands/pr/resolve.d.ts +3 -0
  75. package/dist/cli/commands/pr/resolve.d.ts.map +1 -0
  76. package/dist/cli/commands/pr/resolve.js +92 -0
  77. package/dist/cli/commands/pr/resolve.js.map +1 -0
  78. package/dist/cli/commands/pr/review.d.ts.map +1 -1
  79. package/dist/cli/commands/pr/review.js +121 -6
  80. package/dist/cli/commands/pr/review.js.map +1 -1
  81. package/dist/cli/commands/push.d.ts +16 -0
  82. package/dist/cli/commands/push.d.ts.map +1 -0
  83. package/dist/cli/commands/push.js +166 -0
  84. package/dist/cli/commands/push.js.map +1 -0
  85. package/dist/cli/commands/sync.d.ts +24 -0
  86. package/dist/cli/commands/sync.d.ts.map +1 -0
  87. package/dist/cli/commands/sync.js +414 -0
  88. package/dist/cli/commands/sync.js.map +1 -0
  89. package/dist/cli/index.d.ts.map +1 -1
  90. package/dist/cli/index.js +34 -6
  91. package/dist/cli/index.js.map +1 -1
  92. package/dist/config/config.d.ts +20 -3
  93. package/dist/config/config.d.ts.map +1 -1
  94. package/dist/config/config.js +98 -45
  95. package/dist/config/config.js.map +1 -1
  96. package/dist/config/schema.d.ts.map +1 -1
  97. package/dist/config/schema.js +61 -6
  98. package/dist/config/schema.js.map +1 -1
  99. package/dist/core/context.d.ts +6 -0
  100. package/dist/core/context.d.ts.map +1 -1
  101. package/dist/core/context.js.map +1 -1
  102. package/dist/core/gitx.d.ts +43 -0
  103. package/dist/core/gitx.d.ts.map +1 -1
  104. package/dist/core/gitx.js +187 -20
  105. package/dist/core/gitx.js.map +1 -1
  106. package/dist/index.d.ts +1 -5
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +4 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/providers/azure.d.ts +26 -0
  111. package/dist/providers/azure.d.ts.map +1 -0
  112. package/dist/providers/azure.js +256 -0
  113. package/dist/providers/azure.js.map +1 -0
  114. package/dist/providers/base.d.ts +104 -0
  115. package/dist/providers/base.d.ts.map +1 -0
  116. package/dist/providers/base.js +5 -0
  117. package/dist/providers/base.js.map +1 -0
  118. package/dist/providers/factory.d.ts +8 -0
  119. package/dist/providers/factory.d.ts.map +1 -0
  120. package/dist/providers/factory.js +25 -0
  121. package/dist/providers/factory.js.map +1 -0
  122. package/dist/providers/github.d.ts +19 -0
  123. package/dist/providers/github.d.ts.map +1 -0
  124. package/dist/providers/github.js +291 -0
  125. package/dist/providers/github.js.map +1 -0
  126. package/dist/providers/gitlab.d.ts +19 -0
  127. package/dist/providers/gitlab.d.ts.map +1 -0
  128. package/dist/providers/gitlab.js +186 -0
  129. package/dist/providers/gitlab.js.map +1 -0
  130. package/dist/types/config.d.ts +50 -7
  131. package/dist/types/config.d.ts.map +1 -1
  132. package/dist/types/config.js.map +1 -1
  133. package/dist/utils/azureAuth.d.ts +51 -0
  134. package/dist/utils/azureAuth.d.ts.map +1 -0
  135. package/dist/utils/azureAuth.js +172 -0
  136. package/dist/utils/azureAuth.js.map +1 -0
  137. package/dist/utils/git.d.ts +19 -0
  138. package/dist/utils/git.d.ts.map +1 -1
  139. package/dist/utils/git.js +45 -8
  140. package/dist/utils/git.js.map +1 -1
  141. package/dist/utils/gitOps.d.ts +125 -0
  142. package/dist/utils/gitOps.d.ts.map +1 -0
  143. package/dist/utils/gitOps.js +396 -0
  144. package/dist/utils/gitOps.js.map +1 -0
  145. package/dist/utils/lockFile.d.ts +13 -0
  146. package/dist/utils/lockFile.d.ts.map +1 -0
  147. package/dist/utils/lockFile.js +54 -0
  148. package/dist/utils/lockFile.js.map +1 -0
  149. package/dist/utils/retry.d.ts +10 -0
  150. package/dist/utils/retry.d.ts.map +1 -0
  151. package/dist/utils/retry.js +31 -0
  152. package/dist/utils/retry.js.map +1 -0
  153. package/dist/workflows/implement.d.ts +41 -0
  154. package/dist/workflows/implement.d.ts.map +1 -0
  155. package/dist/workflows/implement.js +219 -0
  156. package/dist/workflows/implement.js.map +1 -0
  157. package/dist/workflows/pr.d.ts +41 -0
  158. package/dist/workflows/pr.d.ts.map +1 -0
  159. package/dist/workflows/pr.js +291 -0
  160. package/dist/workflows/pr.js.map +1 -0
  161. package/dist/workflows/prAddress.d.ts +55 -0
  162. package/dist/workflows/prAddress.d.ts.map +1 -0
  163. package/dist/workflows/prAddress.js +349 -0
  164. package/dist/workflows/prAddress.js.map +1 -0
  165. package/package.json +1 -1
@@ -1,12 +1,32 @@
1
1
  import { logger } from "../../../logger/logger.js";
2
2
  import { Gitx } from "../../../core/gitx.js";
3
+ import { createProvider } from "../../../providers/factory.js";
3
4
  export function registerPrListCommand(pr) {
4
5
  pr.command("list")
5
- .description("📋 List pull requests")
6
- .action(async () => {
6
+ .description("📋 List open pull requests")
7
+ .option("--state <state>", "Filter: open|closed|all", "open")
8
+ .action(async (options) => {
7
9
  const gitx = await Gitx.fromCwd();
8
- await gitx.getRepoContext();
9
- logger.warn("Not implemented yet (provider system next).");
10
+ const ctx = await gitx.getRepoContext();
11
+ logger.info(`📋 Fetching PRs for ${ctx.repoSlug} (${ctx.provider})…`);
12
+ const provider = createProvider(ctx);
13
+ const prs = await provider.listPRs(ctx.repoSlug);
14
+ const filtered = options.state === "all"
15
+ ? prs
16
+ : prs.filter((p) => p.state === options.state);
17
+ if (filtered.length === 0) {
18
+ logger.info(`No ${options.state} pull requests found.`);
19
+ return;
20
+ }
21
+ logger.info(`\nFound ${filtered.length} pull request(s):\n`);
22
+ for (const p of filtered) {
23
+ const stateIcon = p.state === "open" ? "🟢" : p.state === "merged" ? "🟣" : "🔴";
24
+ logger.info(` ${stateIcon} #${p.number} ${p.title}`);
25
+ logger.info(` Branch: ${p.head} → ${p.base}`);
26
+ logger.info(` Author: ${p.author} | Updated: ${new Date(p.updatedAt).toLocaleDateString()}`);
27
+ logger.info(` URL: ${p.url}`);
28
+ logger.info("");
29
+ }
10
30
  });
11
31
  }
12
32
  //# sourceMappingURL=list.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,MAAM,UAAU,qBAAqB,CAAC,EAAW;IAC/C,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import type { Command } from \"commander\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\n\nexport function registerPrListCommand(pr: Command): void {\n pr.command(\"list\")\n .description(\"📋 List pull requests\")\n .action(async () => {\n const gitx = await Gitx.fromCwd();\n await gitx.getRepoContext();\n logger.warn(\"Not implemented yet (provider system next).\");\n });\n}\n"]}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D,MAAM,UAAU,qBAAqB,CAAC,EAAW;IAC/C,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAExC,MAAM,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;QAEtE,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,GAAG,GAAkB,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEhE,MAAM,QAAQ,GACZ,OAAO,CAAC,KAAK,KAAK,KAAK;YACrB,CAAC,CAAC,GAAG;YACL,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;QAEnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,KAAK,uBAAuB,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,WAAW,QAAQ,CAAC,MAAM,qBAAqB,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACjF,MAAM,CAAC,IAAI,CACT,KAAK,SAAS,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,CAC1C,CAAC;YACF,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,iBAAiB,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;YACtG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import type { Command } from \"commander\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\nimport { createProvider } from \"../../../providers/factory.js\";\nimport type { PullRequest } from \"../../../providers/base.js\";\n\nexport function registerPrListCommand(pr: Command): void {\n pr.command(\"list\")\n .description(\"📋 List open pull requests\")\n .option(\"--state <state>\", \"Filter: open|closed|all\", \"open\")\n .action(async (options: { state: string }) => {\n const gitx = await Gitx.fromCwd();\n const ctx = await gitx.getRepoContext();\n\n logger.info(`📋 Fetching PRs for ${ctx.repoSlug} (${ctx.provider})…`);\n\n const provider = createProvider(ctx);\n const prs: PullRequest[] = await provider.listPRs(ctx.repoSlug);\n\n const filtered =\n options.state === \"all\"\n ? prs\n : prs.filter((p) => p.state === options.state);\n\n if (filtered.length === 0) {\n logger.info(`No ${options.state} pull requests found.`);\n return;\n }\n\n logger.info(`\\nFound ${filtered.length} pull request(s):\\n`);\n for (const p of filtered) {\n const stateIcon = p.state === \"open\" ? \"🟢\" : p.state === \"merged\" ? \"🟣\" : \"🔴\";\n logger.info(\n ` ${stateIcon} #${p.number} ${p.title}`\n );\n logger.info(` Branch: ${p.head} → ${p.base}`);\n logger.info(` Author: ${p.author} | Updated: ${new Date(p.updatedAt).toLocaleDateString()}`);\n logger.info(` URL: ${p.url}`);\n logger.info(\"\");\n }\n });\n}\n"]}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * gitx pr merge <number>
3
+ *
4
+ * Merge a pull request via the provider API.
5
+ *
6
+ * Flow:
7
+ * 1. Fetch PR details and show a summary
8
+ * 2. Guard: PR must be open (not already merged / closed)
9
+ * 3. Confirm (or skip with --force)
10
+ * 4. Merge via API using the chosen strategy
11
+ * 5. Optionally delete the source branch (local + remote)
12
+ * 6. Optionally checkout the base branch and pull
13
+ *
14
+ * Usage:
15
+ * gitx pr merge 42 # squash-merge (default), asks to confirm
16
+ * gitx pr merge 42 --method merge # regular merge commit
17
+ * gitx pr merge 42 --method rebase # rebase and merge
18
+ * gitx pr merge 42 --delete-branch # delete source branch after merging
19
+ * gitx pr merge 42 --force # skip confirmation prompt
20
+ */
21
+ import type { Command } from "commander";
22
+ export declare function registerPrMergeCommand(pr: Command): void;
23
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBzC,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAkLxD"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * gitx pr merge <number>
3
+ *
4
+ * Merge a pull request via the provider API.
5
+ *
6
+ * Flow:
7
+ * 1. Fetch PR details and show a summary
8
+ * 2. Guard: PR must be open (not already merged / closed)
9
+ * 3. Confirm (or skip with --force)
10
+ * 4. Merge via API using the chosen strategy
11
+ * 5. Optionally delete the source branch (local + remote)
12
+ * 6. Optionally checkout the base branch and pull
13
+ *
14
+ * Usage:
15
+ * gitx pr merge 42 # squash-merge (default), asks to confirm
16
+ * gitx pr merge 42 --method merge # regular merge commit
17
+ * gitx pr merge 42 --method rebase # rebase and merge
18
+ * gitx pr merge 42 --delete-branch # delete source branch after merging
19
+ * gitx pr merge 42 --force # skip confirmation prompt
20
+ */
21
+ import inquirer from "inquirer";
22
+ import ora from "ora";
23
+ import { execFile } from "node:child_process";
24
+ import { promisify } from "node:util";
25
+ import { logger } from "../../../logger/logger.js";
26
+ import { Gitx } from "../../../core/gitx.js";
27
+ import { createProvider } from "../../../providers/factory.js";
28
+ import { GitxError } from "../../../utils/errors.js";
29
+ import { getCurrentBranch } from "../../../utils/gitOps.js";
30
+ const execFileAsync = promisify(execFile);
31
+ async function gitLocal(args, cwd) {
32
+ const { stdout } = await execFileAsync("git", args, { cwd });
33
+ return stdout.trim();
34
+ }
35
+ export function registerPrMergeCommand(pr) {
36
+ pr.command("merge <number>")
37
+ .description("🔀 Merge a pull request")
38
+ .option("--method <method>", "Merge strategy: squash | merge | rebase (default: squash)", "squash")
39
+ .option("--delete-branch", "Delete the source branch after merging", false)
40
+ .option("-f, --force", "Skip confirmation prompt", false)
41
+ .action(async (numberArg, opts) => {
42
+ const prNumber = parseInt(numberArg, 10);
43
+ if (isNaN(prNumber) || prNumber <= 0) {
44
+ throw new GitxError(`Invalid PR number: "${numberArg}"`, { exitCode: 2 });
45
+ }
46
+ const validMethods = ["squash", "merge", "rebase"];
47
+ if (!validMethods.includes(opts.method)) {
48
+ throw new GitxError(`Invalid merge method "${opts.method}". Choose: squash | merge | rebase`, { exitCode: 2 });
49
+ }
50
+ const method = opts.method;
51
+ const cwd = process.cwd();
52
+ const gitx = await Gitx.fromCwd(cwd);
53
+ const ctx = await gitx.getRepoContext();
54
+ const provider = createProvider(ctx);
55
+ // ── Fetch PR details ───────────────────────────────────────────────────
56
+ const fetchSpinner = ora(`Fetching PR #${prNumber}…`).start();
57
+ let pr_;
58
+ try {
59
+ pr_ = await provider.getPR(ctx.repoSlug, prNumber);
60
+ fetchSpinner.stop();
61
+ }
62
+ catch (err) {
63
+ fetchSpinner.fail();
64
+ throw err;
65
+ }
66
+ // ── Guards ─────────────────────────────────────────────────────────────
67
+ if (pr_.state === "merged") {
68
+ logger.warn(`PR #${prNumber} is already merged.`);
69
+ logger.info(` ${pr_.url}`);
70
+ return;
71
+ }
72
+ if (pr_.state === "closed") {
73
+ throw new GitxError(`PR #${prNumber} is closed and cannot be merged. Reopen it first.`, { exitCode: 1 });
74
+ }
75
+ // ── Show PR summary ────────────────────────────────────────────────────
76
+ const methodLabel = {
77
+ squash: "Squash and merge",
78
+ merge: "Create a merge commit",
79
+ rebase: "Rebase and merge",
80
+ };
81
+ logger.info(`\n #${pr_.number} ${pr_.title}`);
82
+ logger.info(` Branch: ${pr_.head} → ${pr_.base}`);
83
+ logger.info(` Author: ${pr_.author}`);
84
+ logger.info(` Method: ${methodLabel[method]}`);
85
+ logger.info(` URL: ${pr_.url}\n`);
86
+ // ── Confirm ────────────────────────────────────────────────────────────
87
+ if (!opts.force) {
88
+ const { confirmed } = await inquirer.prompt([
89
+ {
90
+ type: "confirm",
91
+ name: "confirmed",
92
+ message: `Merge PR #${prNumber}${opts.deleteBranch ? " and delete source branch" : ""}?`,
93
+ default: true,
94
+ },
95
+ ]);
96
+ if (!confirmed) {
97
+ logger.info("Aborted — PR not merged.");
98
+ return;
99
+ }
100
+ }
101
+ // ── Merge via API ──────────────────────────────────────────────────────
102
+ const mergeSpinner = ora(`Merging PR #${prNumber} (${method})…`).start();
103
+ try {
104
+ await provider.mergePR(ctx.repoSlug, prNumber, {
105
+ method,
106
+ commitTitle: pr_.title,
107
+ deleteSourceBranch: opts.deleteBranch,
108
+ });
109
+ }
110
+ catch (err) {
111
+ mergeSpinner.fail("Merge failed.");
112
+ const msg = err instanceof Error ? err.message : String(err);
113
+ // GitHub 405 / GitLab 405/406 = merge conflicts or branch not mergeable
114
+ const isConflict = msg.includes("merge conflict") ||
115
+ msg.includes("405") ||
116
+ msg.includes("not mergeable") ||
117
+ msg.toLowerCase().includes("conflict");
118
+ if (isConflict) {
119
+ logger.error(`\n❌ PR #${prNumber} has merge conflicts.\n`);
120
+ logger.info(` The branch "${pr_.head}" is out of sync with "${pr_.base}".`);
121
+ logger.info(` Sync and resolve conflicts, then retry:\n`);
122
+ logger.info(` gitx sync ← rebase onto ${pr_.base} and push`);
123
+ logger.info(` gitx pr merge ${prNumber} ← retry after sync\n`);
124
+ logger.info(` Or resolve manually:`);
125
+ logger.info(` git fetch origin`);
126
+ logger.info(` git rebase origin/${pr_.base}`);
127
+ logger.info(` # fix conflicts in editor, then:`);
128
+ logger.info(` git add . && git rebase --continue`);
129
+ logger.info(` git push --force-with-lease`);
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ throw err; // re-throw non-conflict errors
134
+ }
135
+ mergeSpinner.succeed(`PR #${prNumber} merged ✓`);
136
+ // ── Delete source branch locally if requested ──────────────────────────
137
+ if (opts.deleteBranch) {
138
+ const currentBranch = await getCurrentBranch(cwd);
139
+ // If we're ON the merged branch, switch to base first
140
+ if (currentBranch === pr_.head) {
141
+ const switchSpinner = ora(`Switching to ${pr_.base}…`).start();
142
+ try {
143
+ await gitLocal(["checkout", pr_.base], cwd);
144
+ await gitLocal(["pull", "--ff-only"], cwd);
145
+ switchSpinner.succeed(`Switched to ${pr_.base} and pulled latest.`);
146
+ }
147
+ catch {
148
+ switchSpinner.fail(`Could not switch to ${pr_.base} — switch manually before deleting.`);
149
+ }
150
+ }
151
+ // Delete local branch
152
+ try {
153
+ await gitLocal(["branch", "-d", pr_.head], cwd);
154
+ logger.success(` Deleted local branch: ${pr_.head}`);
155
+ }
156
+ catch {
157
+ // -d refuses to delete if not fully merged locally; use -D
158
+ try {
159
+ await gitLocal(["branch", "-D", pr_.head], cwd);
160
+ logger.success(` Force-deleted local branch: ${pr_.head}`);
161
+ }
162
+ catch {
163
+ logger.warn(` Could not delete local branch "${pr_.head}" — delete it manually.`);
164
+ }
165
+ }
166
+ // Delete remote branch
167
+ try {
168
+ await gitLocal(["push", "origin", "--delete", pr_.head], cwd);
169
+ logger.success(` Deleted remote branch: origin/${pr_.head}`);
170
+ }
171
+ catch {
172
+ logger.warn(` Could not delete remote branch "origin/${pr_.head}" — it may already be gone.`);
173
+ }
174
+ }
175
+ else {
176
+ // Even without --delete-branch, offer to pull base if we're on it
177
+ const currentBranch = await getCurrentBranch(cwd);
178
+ if (currentBranch === pr_.base) {
179
+ try {
180
+ await gitLocal(["pull", "--ff-only"], cwd);
181
+ logger.success(` Pulled latest ${pr_.base}.`);
182
+ }
183
+ catch {
184
+ // Non-fatal
185
+ }
186
+ }
187
+ }
188
+ logger.success(`\n✅ Done! ${pr_.url}`);
189
+ });
190
+ }
191
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/merge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gitx pr merge <number>\n *\n * Merge a pull request via the provider API.\n *\n * Flow:\n * 1. Fetch PR details and show a summary\n * 2. Guard: PR must be open (not already merged / closed)\n * 3. Confirm (or skip with --force)\n * 4. Merge via API using the chosen strategy\n * 5. Optionally delete the source branch (local + remote)\n * 6. Optionally checkout the base branch and pull\n *\n * Usage:\n * gitx pr merge 42 # squash-merge (default), asks to confirm\n * gitx pr merge 42 --method merge # regular merge commit\n * gitx pr merge 42 --method rebase # rebase and merge\n * gitx pr merge 42 --delete-branch # delete source branch after merging\n * gitx pr merge 42 --force # skip confirmation prompt\n */\n\nimport type { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport ora from \"ora\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\nimport { createProvider } from \"../../../providers/factory.js\";\nimport type { MergePrOptions } from \"../../../providers/base.js\";\nimport { GitxError } from \"../../../utils/errors.js\";\nimport { getCurrentBranch } from \"../../../utils/gitOps.js\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function gitLocal(args: string[], cwd: string): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", args, { cwd });\n return stdout.trim();\n}\n\nexport function registerPrMergeCommand(pr: Command): void {\n pr.command(\"merge <number>\")\n .description(\"🔀 Merge a pull request\")\n .option(\n \"--method <method>\",\n \"Merge strategy: squash | merge | rebase (default: squash)\",\n \"squash\"\n )\n .option(\"--delete-branch\", \"Delete the source branch after merging\", false)\n .option(\"-f, --force\", \"Skip confirmation prompt\", false)\n .action(async (numberArg: string, opts: {\n method: string;\n deleteBranch: boolean;\n force: boolean;\n }) => {\n const prNumber = parseInt(numberArg, 10);\n if (isNaN(prNumber) || prNumber <= 0) {\n throw new GitxError(`Invalid PR number: \"${numberArg}\"`, { exitCode: 2 });\n }\n\n const validMethods = [\"squash\", \"merge\", \"rebase\"];\n if (!validMethods.includes(opts.method)) {\n throw new GitxError(\n `Invalid merge method \"${opts.method}\". Choose: squash | merge | rebase`,\n { exitCode: 2 }\n );\n }\n const method = opts.method as MergePrOptions[\"method\"];\n\n const cwd = process.cwd();\n const gitx = await Gitx.fromCwd(cwd);\n const ctx = await gitx.getRepoContext();\n const provider = createProvider(ctx);\n\n // ── Fetch PR details ───────────────────────────────────────────────────\n const fetchSpinner = ora(`Fetching PR #${prNumber}…`).start();\n let pr_: Awaited<ReturnType<typeof provider.getPR>>;\n try {\n pr_ = await provider.getPR(ctx.repoSlug, prNumber);\n fetchSpinner.stop();\n } catch (err) {\n fetchSpinner.fail();\n throw err;\n }\n\n // ── Guards ─────────────────────────────────────────────────────────────\n if (pr_.state === \"merged\") {\n logger.warn(`PR #${prNumber} is already merged.`);\n logger.info(` ${pr_.url}`);\n return;\n }\n if (pr_.state === \"closed\") {\n throw new GitxError(\n `PR #${prNumber} is closed and cannot be merged. Reopen it first.`,\n { exitCode: 1 }\n );\n }\n\n // ── Show PR summary ────────────────────────────────────────────────────\n const methodLabel: Record<MergePrOptions[\"method\"], string> = {\n squash: \"Squash and merge\",\n merge: \"Create a merge commit\",\n rebase: \"Rebase and merge\",\n };\n\n logger.info(`\\n #${pr_.number} ${pr_.title}`);\n logger.info(` Branch: ${pr_.head} → ${pr_.base}`);\n logger.info(` Author: ${pr_.author}`);\n logger.info(` Method: ${methodLabel[method]}`);\n logger.info(` URL: ${pr_.url}\\n`);\n\n // ── Confirm ────────────────────────────────────────────────────────────\n if (!opts.force) {\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: `Merge PR #${prNumber}${opts.deleteBranch ? \" and delete source branch\" : \"\"}?`,\n default: true,\n },\n ]);\n if (!confirmed) {\n logger.info(\"Aborted — PR not merged.\");\n return;\n }\n }\n\n // ── Merge via API ──────────────────────────────────────────────────────\n const mergeSpinner = ora(`Merging PR #${prNumber} (${method})…`).start();\n try {\n await provider.mergePR(ctx.repoSlug, prNumber, {\n method,\n commitTitle: pr_.title,\n deleteSourceBranch: opts.deleteBranch,\n });\n } catch (err) {\n mergeSpinner.fail(\"Merge failed.\");\n const msg = err instanceof Error ? err.message : String(err);\n\n // GitHub 405 / GitLab 405/406 = merge conflicts or branch not mergeable\n const isConflict =\n msg.includes(\"merge conflict\") ||\n msg.includes(\"405\") ||\n msg.includes(\"not mergeable\") ||\n msg.toLowerCase().includes(\"conflict\");\n\n if (isConflict) {\n logger.error(`\\n❌ PR #${prNumber} has merge conflicts.\\n`);\n logger.info(` The branch \"${pr_.head}\" is out of sync with \"${pr_.base}\".`);\n logger.info(` Sync and resolve conflicts, then retry:\\n`);\n logger.info(` gitx sync ← rebase onto ${pr_.base} and push`);\n logger.info(` gitx pr merge ${prNumber} ← retry after sync\\n`);\n logger.info(` Or resolve manually:`);\n logger.info(` git fetch origin`);\n logger.info(` git rebase origin/${pr_.base}`);\n logger.info(` # fix conflicts in editor, then:`);\n logger.info(` git add . && git rebase --continue`);\n logger.info(` git push --force-with-lease`);\n process.exitCode = 1;\n return;\n }\n\n throw err; // re-throw non-conflict errors\n }\n mergeSpinner.succeed(`PR #${prNumber} merged ✓`);\n\n // ── Delete source branch locally if requested ──────────────────────────\n if (opts.deleteBranch) {\n const currentBranch = await getCurrentBranch(cwd);\n\n // If we're ON the merged branch, switch to base first\n if (currentBranch === pr_.head) {\n const switchSpinner = ora(`Switching to ${pr_.base}…`).start();\n try {\n await gitLocal([\"checkout\", pr_.base], cwd);\n await gitLocal([\"pull\", \"--ff-only\"], cwd);\n switchSpinner.succeed(`Switched to ${pr_.base} and pulled latest.`);\n } catch {\n switchSpinner.fail(`Could not switch to ${pr_.base} — switch manually before deleting.`);\n }\n }\n\n // Delete local branch\n try {\n await gitLocal([\"branch\", \"-d\", pr_.head], cwd);\n logger.success(` Deleted local branch: ${pr_.head}`);\n } catch {\n // -d refuses to delete if not fully merged locally; use -D\n try {\n await gitLocal([\"branch\", \"-D\", pr_.head], cwd);\n logger.success(` Force-deleted local branch: ${pr_.head}`);\n } catch {\n logger.warn(` Could not delete local branch \"${pr_.head}\" — delete it manually.`);\n }\n }\n\n // Delete remote branch\n try {\n await gitLocal([\"push\", \"origin\", \"--delete\", pr_.head], cwd);\n logger.success(` Deleted remote branch: origin/${pr_.head}`);\n } catch {\n logger.warn(` Could not delete remote branch \"origin/${pr_.head}\" — it may already be gone.`);\n }\n } else {\n // Even without --delete-branch, offer to pull base if we're on it\n const currentBranch = await getCurrentBranch(cwd);\n if (currentBranch === pr_.base) {\n try {\n await gitLocal([\"pull\", \"--ff-only\"], cwd);\n logger.success(` Pulled latest ${pr_.base}.`);\n } catch {\n // Non-fatal\n }\n }\n }\n\n logger.success(`\\n✅ Done! ${pr_.url}`);\n });\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerPrResolveCommand(pr: Command): void;
3
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CA+F1D"}
@@ -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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAUzD"}
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAqJzD"}
@@ -1,14 +1,129 @@
1
1
  import { logger } from "../../../logger/logger.js";
2
2
  import { Gitx } from "../../../core/gitx.js";
3
+ import { runReviewWorkflow } from "../../../workflows/pr.js";
3
4
  export function registerPrReviewCommand(pr) {
4
5
  pr.command("review")
5
- .description("🧐 Review a pull request")
6
- .argument("<id>", "Pull request id/number")
7
- .action(async (id) => {
6
+ .description("🧐 Senior-dev AI review: inline comments, checklist, verdict")
7
+ .argument("<id>", "Pull request number")
8
+ .option("--no-comment", "Show review locally only — do not post to PR")
9
+ .option("--no-inline", "Post overall review but skip inline file comments")
10
+ .action(async (id, options) => {
11
+ const prNumber = parseInt(id, 10);
12
+ if (isNaN(prNumber) || prNumber <= 0) {
13
+ logger.error(`Invalid PR number: ${id}`);
14
+ process.exit(1);
15
+ }
8
16
  const gitx = await Gitx.fromCwd();
9
- await gitx.getRepoContext();
10
- logger.warn("Not implemented yet (provider system next).");
11
- logger.info(`PR: ${id}`);
17
+ if (!await Gitx.isAiAvailable(gitx.config)) {
18
+ logger.warn("⚠️ No AI provider configured — review will not be meaningful.\n" +
19
+ " Run: gitx config set anthropic (or openai / claude-cli)");
20
+ return;
21
+ }
22
+ const postComment = options.comment !== false;
23
+ logger.info(`\n🧐 Senior-dev AI review — PR #${prNumber}\n`);
24
+ let result;
25
+ try {
26
+ result = await runReviewWorkflow(gitx, prNumber, postComment);
27
+ }
28
+ catch (err) {
29
+ logger.error(`Review failed: ${String(err.message ?? err)}`);
30
+ process.exitCode = 1;
31
+ return;
32
+ }
33
+ const { pr: pullReq, review, comments } = result;
34
+ // ── PR header ─────────────────────────────────────────────────────────
35
+ logger.info(`\n📋 ${pullReq.title}`);
36
+ logger.info(` ${pullReq.head} → ${pullReq.base} · by ${pullReq.author} · ${pullReq.state}`);
37
+ logger.info(` ${pullReq.url}\n`);
38
+ if (!review) {
39
+ logger.info(`🤖 Review:\n${result.aiSummary}`);
40
+ return;
41
+ }
42
+ // ── Verdict ───────────────────────────────────────────────────────────
43
+ const verdictIcon = review.verdict === "approve"
44
+ ? "✅ APPROVE"
45
+ : review.verdict === "request_changes"
46
+ ? "🔴 REQUEST CHANGES"
47
+ : "💬 COMMENT";
48
+ logger.info(`🤖 Verdict: ${verdictIcon}\n`);
49
+ // ── Summary ───────────────────────────────────────────────────────────
50
+ logger.info(`📝 Summary:\n ${review.summary.replace(/\n/g, "\n ")}\n`);
51
+ // ── Checklist ─────────────────────────────────────────────────────────
52
+ if (review.checklist.length > 0) {
53
+ logger.info("✅ Review Checklist:");
54
+ for (const item of review.checklist) {
55
+ const icon = item.status === "pass" ? "✅" : item.status === "warn" ? "⚠️ " : "❌";
56
+ logger.info(` ${icon} ${item.area.padEnd(20)} ${item.note}`);
57
+ }
58
+ logger.info("");
59
+ }
60
+ // ── Issues ────────────────────────────────────────────────────────────
61
+ const criticals = review.issues.filter((i) => i.severity === "critical");
62
+ const warnings = review.issues.filter((i) => i.severity === "warning");
63
+ const suggestions = review.issues.filter((i) => i.severity === "suggestion");
64
+ if (review.issues.length > 0) {
65
+ logger.info(`🔎 Issues (${review.issues.length}):`);
66
+ for (const issue of [...criticals, ...warnings, ...suggestions]) {
67
+ const icon = issue.severity === "critical" ? "🔴" : issue.severity === "warning" ? "🟡" : "💡";
68
+ const loc = issue.file ? ` [${issue.file}${issue.line ? `:${issue.line}` : ""}]` : "";
69
+ logger.info(` ${icon} ${issue.description}${loc}`);
70
+ }
71
+ logger.info("");
72
+ }
73
+ // ── Inline comments ───────────────────────────────────────────────────
74
+ if (review.inlineComments.length > 0) {
75
+ logger.info(`💬 Inline Comments (${review.inlineComments.length}):`);
76
+ for (const c of review.inlineComments) {
77
+ const icon = c.severity === "critical" ? "🔴" : c.severity === "warning" ? "🟡" : "💡";
78
+ logger.info(`\n ${icon} ${c.path}:${c.line}`);
79
+ logger.info(` ${c.body.replace(/\n/g, "\n ")}`);
80
+ if (c.suggestion) {
81
+ logger.info(` 📌 Suggestion: ${c.suggestion.split("\n")[0]}…`);
82
+ }
83
+ }
84
+ logger.info("");
85
+ }
86
+ // ── Positives ─────────────────────────────────────────────────────────
87
+ if (review.positives.length > 0) {
88
+ logger.info("👍 Positives:");
89
+ for (const p of review.positives)
90
+ logger.info(` ✔ ${p}`);
91
+ logger.info("");
92
+ }
93
+ // ── Testing notes ─────────────────────────────────────────────────────
94
+ if (review.testingNotes) {
95
+ logger.info(`🧪 How to test:\n ${review.testingNotes.replace(/\n/g, "\n ")}\n`);
96
+ }
97
+ // ── Existing PR comments ──────────────────────────────────────────────
98
+ if (comments.length > 0) {
99
+ logger.info(`💬 Existing comments on PR (${comments.length}):`);
100
+ for (const c of comments.slice(0, 4)) {
101
+ const loc = c.path ? ` @ ${c.path}${c.line ? `:${c.line}` : ""}` : "";
102
+ logger.info(` [${c.author}${loc}]: ${c.body.slice(0, 120)}${c.body.length > 120 ? "…" : ""}`);
103
+ }
104
+ if (comments.length > 4)
105
+ logger.info(` … and ${comments.length - 4} more.`);
106
+ logger.info("");
107
+ }
108
+ // ── Status ────────────────────────────────────────────────────────────
109
+ if (!postComment) {
110
+ logger.info("ℹ️ Review shown locally only (--no-comment).");
111
+ }
112
+ else if (result.reviewPosted) {
113
+ const inlineCount = review.inlineComments.length;
114
+ if (inlineCount > 0) {
115
+ logger.success(`✅ Review posted to PR. ${inlineCount} inline comment(s) — check the PR to see delivery method.`);
116
+ logger.info(` (If lines were outside the diff, comments were posted as plain PR comments instead.)`);
117
+ }
118
+ else {
119
+ logger.success("✅ Review posted to PR.");
120
+ }
121
+ }
122
+ else {
123
+ logger.warn("⚠️ Review could not be posted to the PR (see error above).\n" +
124
+ " The full review is shown above — you can copy it manually.");
125
+ }
126
+ // To resolve the review comments, run: gitx pr resolve <PR_number>
12
127
  });
13
128
  }
14
129
  //# sourceMappingURL=review.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/review.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,MAAM,UAAU,uBAAuB,CAAC,EAAW;IACjD,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,0BAA0B,CAAC;SACvC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;SAC1C,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import type { Command } from \"commander\";\nimport { logger } from \"../../../logger/logger.js\";\nimport { Gitx } from \"../../../core/gitx.js\";\n\nexport function registerPrReviewCommand(pr: Command): void {\n pr.command(\"review\")\n .description(\"🧐 Review a pull request\")\n .argument(\"<id>\", \"Pull request id/number\")\n .action(async (id: string) => {\n const gitx = await Gitx.fromCwd();\n await gitx.getRepoContext();\n logger.warn(\"Not implemented yet (provider system next).\");\n logger.info(`PR: ${id}`);\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"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * gitx push
3
+ *
4
+ * The single-command workflow: stage everything → AI-generate a conventional
5
+ * commit message → commit → push to origin. No flags needed.
6
+ *
7
+ * Usage:
8
+ * gitx push # full auto: stage + AI commit + push
9
+ * gitx push --staged # commit only already-staged changes, leave the rest untouched
10
+ * gitx push -m "fix: typo" # use a custom message, skip AI
11
+ * gitx push --dry-run # preview message without committing or pushing
12
+ * gitx push --branch feat # push to a specific branch instead of current
13
+ */
14
+ import type { Command } from "commander";
15
+ export declare function registerPushCommand(program: Command): void;
16
+ //# sourceMappingURL=push.d.ts.map