gitpt 1.4.0 → 1.7.2

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 (116) hide show
  1. package/README.md +34 -160
  2. package/dist/commands/commit/commitFlags.js +7 -0
  3. package/dist/commands/commit/context/buildPrompt.js +16 -0
  4. package/dist/commands/commit/context/summaryPrompt.js +20 -0
  5. package/dist/commands/commit/context/systemPrompt.js +4 -1
  6. package/dist/commands/commit/context/userPrompt.js +4 -1
  7. package/dist/commands/commit/generateCommitMessage.js +16 -37
  8. package/dist/commands/commit/index.js +187 -178
  9. package/dist/commands/commit/summarizeDiff.js +193 -0
  10. package/dist/commands/config.js +24 -9
  11. package/dist/commands/hook/index.js +68 -0
  12. package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.js +20 -20
  13. package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.js +13 -10
  14. package/dist/commands/middleware/capabilitiesMiddleware/index.js +12 -11
  15. package/dist/commands/middleware/hasStagedChangesMiddleware.js +9 -6
  16. package/dist/commands/middleware/setupMiddleware/defaultModels.js +15 -0
  17. package/dist/commands/middleware/setupMiddleware/index.js +66 -38
  18. package/dist/commands/model.js +7 -4
  19. package/dist/commands/passthroughArgs.js +11 -0
  20. package/dist/commands/pr/context/systemPrompt.js +4 -1
  21. package/dist/commands/pr/context/userPrompt.js +4 -1
  22. package/dist/commands/pr/generatePRDetails.js +25 -31
  23. package/dist/commands/pr/getPRContext.js +37 -62
  24. package/dist/commands/pr/index.js +57 -62
  25. package/dist/commands/reset.js +26 -0
  26. package/dist/commands/review/index.js +31 -0
  27. package/dist/commands/setup.js +38 -13
  28. package/dist/config.js +63 -55
  29. package/dist/index.js +31 -62
  30. package/dist/llm/budget.js +10 -0
  31. package/dist/llm/index.js +12 -12
  32. package/dist/llm/providers/anthropic/index.js +31 -0
  33. package/dist/llm/providers/apiKey.js +41 -0
  34. package/dist/llm/providers/apple/client.js +77 -0
  35. package/dist/llm/providers/apple/index.js +69 -0
  36. package/dist/llm/providers/apple/models.js +29 -0
  37. package/dist/llm/providers/base.js +36 -0
  38. package/dist/llm/providers/local/index.js +84 -0
  39. package/dist/llm/providers/openai/index.js +19 -0
  40. package/dist/llm/providers/openaiCompatible.js +66 -0
  41. package/dist/llm/providers/openrouter/index.js +19 -0
  42. package/dist/llm/registry.js +39 -0
  43. package/dist/llm/setup/getAvailableModels.js +17 -0
  44. package/dist/llm/setup/selectModel.js +40 -0
  45. package/dist/llm/tokenCount.js +5 -0
  46. package/dist/package.js +67 -0
  47. package/dist/services/gh/createPullRequest.js +38 -58
  48. package/dist/services/gh/index.js +6 -3
  49. package/dist/services/gh/isAvailable.js +10 -8
  50. package/dist/services/git/executeGitAdd.js +11 -12
  51. package/dist/services/git/executeGitCommit.js +10 -11
  52. package/dist/services/git/getAmendChanges.js +23 -0
  53. package/dist/services/git/getChangedFiles.js +28 -61
  54. package/dist/services/git/getCommitsSinceBaseBranch.js +28 -56
  55. package/dist/services/git/getCurrentBranch.js +10 -8
  56. package/dist/services/git/getDefaultBranch.js +35 -60
  57. package/dist/services/git/getStagedChanges.js +10 -8
  58. package/dist/services/git/getStagedFiles.js +10 -9
  59. package/dist/services/git/hasStagedChanges.js +9 -8
  60. package/dist/services/git/index.js +17 -12
  61. package/dist/services/git/isAvailable.js +10 -8
  62. package/dist/services/git/isGitRepository.js +10 -8
  63. package/dist/utils/commitlint.js +97 -135
  64. package/dist/utils/formatBaseURL.js +7 -6
  65. package/dist/utils/maskApiKey.js +6 -7
  66. package/package.json +19 -5
  67. package/dist/commands/commit/context/systemPrompt.d.ts +0 -1
  68. package/dist/commands/commit/context/userPrompt.d.ts +0 -1
  69. package/dist/commands/commit/generateCommitMessage.d.ts +0 -1
  70. package/dist/commands/commit/index.d.ts +0 -7
  71. package/dist/commands/config.d.ts +0 -1
  72. package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.d.ts +0 -1
  73. package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.d.ts +0 -1
  74. package/dist/commands/middleware/capabilitiesMiddleware/index.d.ts +0 -3
  75. package/dist/commands/middleware/hasStagedChangesMiddleware.d.ts +0 -1
  76. package/dist/commands/middleware/setupMiddleware/getAvailableModels.d.ts +0 -4
  77. package/dist/commands/middleware/setupMiddleware/getAvailableModels.js +0 -11
  78. package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.d.ts +0 -1
  79. package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.js +0 -39
  80. package/dist/commands/middleware/setupMiddleware/index.d.ts +0 -4
  81. package/dist/commands/middleware/setupMiddleware/selectModel.d.ts +0 -5
  82. package/dist/commands/middleware/setupMiddleware/selectModel.js +0 -38
  83. package/dist/commands/middleware/setupMiddleware/setupLocalLLM.d.ts +0 -5
  84. package/dist/commands/middleware/setupMiddleware/setupLocalLLM.js +0 -60
  85. package/dist/commands/middleware/setupMiddleware/setupOpenRouter.d.ts +0 -2
  86. package/dist/commands/middleware/setupMiddleware/setupOpenRouter.js +0 -66
  87. package/dist/commands/middleware/setupMiddleware/types.d.ts +0 -13
  88. package/dist/commands/middleware/setupMiddleware/types.js +0 -1
  89. package/dist/commands/model.d.ts +0 -1
  90. package/dist/commands/pr/context/systemPrompt.d.ts +0 -1
  91. package/dist/commands/pr/context/userPrompt.d.ts +0 -1
  92. package/dist/commands/pr/generatePRDetails.d.ts +0 -4
  93. package/dist/commands/pr/getPRContext.d.ts +0 -1
  94. package/dist/commands/pr/index.d.ts +0 -10
  95. package/dist/commands/setup.d.ts +0 -3
  96. package/dist/config.d.ts +0 -18
  97. package/dist/index.d.ts +0 -2
  98. package/dist/llm/index.d.ts +0 -5
  99. package/dist/services/gh/createPullRequest.d.ts +0 -1
  100. package/dist/services/gh/index.d.ts +0 -4
  101. package/dist/services/gh/isAvailable.d.ts +0 -1
  102. package/dist/services/git/executeGitAdd.d.ts +0 -1
  103. package/dist/services/git/executeGitCommit.d.ts +0 -1
  104. package/dist/services/git/getChangedFiles.d.ts +0 -1
  105. package/dist/services/git/getCommitsSinceBaseBranch.d.ts +0 -1
  106. package/dist/services/git/getCurrentBranch.d.ts +0 -1
  107. package/dist/services/git/getDefaultBranch.d.ts +0 -1
  108. package/dist/services/git/getStagedChanges.d.ts +0 -1
  109. package/dist/services/git/getStagedFiles.d.ts +0 -1
  110. package/dist/services/git/hasStagedChanges.d.ts +0 -1
  111. package/dist/services/git/index.d.ts +0 -13
  112. package/dist/services/git/isAvailable.d.ts +0 -1
  113. package/dist/services/git/isGitRepository.d.ts +0 -1
  114. package/dist/utils/commitlint.d.ts +0 -25
  115. package/dist/utils/formatBaseURL.d.ts +0 -1
  116. package/dist/utils/maskApiKey.d.ts +0 -1
@@ -0,0 +1,68 @@
1
+ import { git } from "../../services/git/index.js";
2
+ import { generateCommitMessage } from "../commit/generateCommitMessage.js";
3
+ import { prepareCommitContext } from "../commit/summarizeDiff.js";
4
+ import chalk from "chalk";
5
+ import { spawnSync } from "child_process";
6
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
7
+ import { join } from "path";
8
+ //#region src/commands/hook/index.ts
9
+ var HOOK_MARKER = "gitpt-hook";
10
+ var HOOK_SCRIPT = `#!/bin/sh
11
+ # ${HOOK_MARKER} (prepare-commit-msg) — managed by GitPT. Remove: gitpt hook uninstall
12
+ command -v gitpt >/dev/null 2>&1 && gitpt hook run "$1" "$2" || true
13
+ `;
14
+ var resolveHooksDir = () => {
15
+ const result = spawnSync("git", [
16
+ "rev-parse",
17
+ "--git-path",
18
+ "hooks"
19
+ ], { encoding: "utf-8" });
20
+ if (result.status !== 0) throw new Error("Not a git repository.");
21
+ return result.stdout.trim();
22
+ };
23
+ var hookInstallCommand = async (options = {}) => {
24
+ const hookPath = join(resolveHooksDir(), "prepare-commit-msg");
25
+ if (existsSync(hookPath)) {
26
+ if (!readFileSync(hookPath, "utf-8").includes(HOOK_MARKER) && !options.force) {
27
+ console.error(chalk.red(`A prepare-commit-msg hook already exists at ${hookPath}.`));
28
+ console.error(chalk.gray("Remove it, or re-run with --force, to install the GitPT hook."));
29
+ process.exit(1);
30
+ }
31
+ }
32
+ mkdirSync(join(hookPath, ".."), { recursive: true });
33
+ writeFileSync(hookPath, HOOK_SCRIPT, { mode: 493 });
34
+ console.log(chalk.green(`✓ Installed GitPT hook at ${hookPath}.`));
35
+ console.log(chalk.gray("`git commit` (without -m) now prefills an AI-generated message."));
36
+ };
37
+ var hookUninstallCommand = async () => {
38
+ const hookPath = join(resolveHooksDir(), "prepare-commit-msg");
39
+ if (!existsSync(hookPath)) {
40
+ console.log(chalk.gray("No prepare-commit-msg hook to remove."));
41
+ return;
42
+ }
43
+ if (!readFileSync(hookPath, "utf-8").includes(HOOK_MARKER)) {
44
+ console.error(chalk.red("The prepare-commit-msg hook isn't managed by GitPT — leaving it untouched."));
45
+ process.exit(1);
46
+ }
47
+ rmSync(hookPath);
48
+ console.log(chalk.green("✓ Removed GitPT prepare-commit-msg hook."));
49
+ };
50
+ var SKIP_SOURCES = /* @__PURE__ */ new Set([
51
+ "message",
52
+ "merge",
53
+ "squash",
54
+ "commit"
55
+ ]);
56
+ var hookRunCommand = async (msgFile, source) => {
57
+ try {
58
+ if (!msgFile) return;
59
+ if (source && SKIP_SOURCES.has(source)) return;
60
+ const diff = git.getStagedChanges();
61
+ if (!diff || !diff.trim()) return;
62
+ const message = (await generateCommitMessage(await prepareCommitContext(diff))).trim();
63
+ if (!message) return;
64
+ writeFileSync(msgFile, `${message}\n\n${existsSync(msgFile) ? readFileSync(msgFile, "utf-8") : ""}`);
65
+ } catch {}
66
+ };
67
+ //#endregion
68
+ export { hookInstallCommand, hookRunCommand, hookUninstallCommand };
@@ -1,23 +1,23 @@
1
+ import { gh } from "../../../services/gh/index.js";
1
2
  import chalk from "chalk";
2
3
  import { execSync } from "child_process";
3
- import { gh } from "../../../services/gh/index.js";
4
- export const ghCapability = () => {
5
- if (!gh.isAvailable()) {
6
- console.error(chalk.red("GitHub CLI (gh) is not installed or available in PATH"));
7
- console.log(chalk.yellow("Please install GitHub CLI from https://cli.github.com/"));
8
- process.exit(1);
9
- }
10
- // Check if user is authenticated with GitHub CLI
11
- try {
12
- const authStatus = execSync("gh auth status -h github.com 2>&1 || true").toString();
13
- if (authStatus.includes("not logged")) {
14
- console.error(chalk.red("Error: You are not authenticated with GitHub CLI."));
15
- console.log(chalk.yellow("Please run `gh auth login` to authenticate."));
16
- process.exit(1);
17
- }
18
- }
19
- catch (error) {
20
- console.log(chalk.yellow("Warning: Could not verify GitHub CLI authentication."));
21
- console.log(chalk.yellow("If PR creation fails, please run `gh auth login` first."));
22
- }
4
+ //#region src/commands/middleware/capabilitiesMiddleware/ghCapability.ts
5
+ var ghCapability = () => {
6
+ if (!gh.isAvailable()) {
7
+ console.error(chalk.red("GitHub CLI (gh) is not installed or available in PATH"));
8
+ console.log(chalk.yellow("Please install GitHub CLI from https://cli.github.com/"));
9
+ process.exit(1);
10
+ }
11
+ try {
12
+ if (execSync("gh auth status -h github.com 2>&1 || true").toString().includes("not logged")) {
13
+ console.error(chalk.red("Error: You are not authenticated with GitHub CLI."));
14
+ console.log(chalk.yellow("Please run `gh auth login` to authenticate."));
15
+ process.exit(1);
16
+ }
17
+ } catch (error) {
18
+ console.log(chalk.yellow("Warning: Could not verify GitHub CLI authentication."));
19
+ console.log(chalk.yellow("If PR creation fails, please run `gh auth login` first."));
20
+ }
23
21
  };
22
+ //#endregion
23
+ export { ghCapability };
@@ -1,12 +1,15 @@
1
- import chalk from "chalk";
2
1
  import { git } from "../../../services/git/index.js";
3
- export const gitCapability = () => {
4
- if (!git.isAvailable()) {
5
- console.error(chalk.red("Git command is not available, install it from https://git-scm.com/"));
6
- process.exit(1);
7
- }
8
- if (!git.isGitRepository()) {
9
- console.error(chalk.red("Not a git repository"));
10
- process.exit(1);
11
- }
2
+ import chalk from "chalk";
3
+ //#region src/commands/middleware/capabilitiesMiddleware/gitCapability.ts
4
+ var gitCapability = () => {
5
+ if (!git.isAvailable()) {
6
+ console.error(chalk.red("Git command is not available, install it from https://git-scm.com/"));
7
+ process.exit(1);
8
+ }
9
+ if (!git.isGitRepository()) {
10
+ console.error(chalk.red("Not a git repository"));
11
+ process.exit(1);
12
+ }
12
13
  };
14
+ //#endregion
15
+ export { gitCapability };
@@ -1,15 +1,16 @@
1
1
  import { ghCapability } from "./ghCapability.js";
2
2
  import { gitCapability } from "./gitCapability.js";
3
- const capabilities = {
4
- git: gitCapability,
5
- gh: ghCapability,
3
+ //#region src/commands/middleware/capabilitiesMiddleware/index.ts
4
+ var capabilities = {
5
+ git: gitCapability,
6
+ gh: ghCapability
6
7
  };
7
- export const capabilitiesMiddleware = (requiredCapabilities) => {
8
- requiredCapabilities.forEach((capabilityKey) => {
9
- const resolver = capabilities[capabilityKey];
10
- if (!resolver) {
11
- throw new Error(`Capability ${capabilityKey} not found`);
12
- }
13
- resolver();
14
- });
8
+ var capabilitiesMiddleware = (requiredCapabilities) => {
9
+ requiredCapabilities.forEach((capabilityKey) => {
10
+ const resolver = capabilities[capabilityKey];
11
+ if (!resolver) throw new Error(`Capability ${capabilityKey} not found`);
12
+ resolver();
13
+ });
15
14
  };
15
+ //#endregion
16
+ export { capabilitiesMiddleware };
@@ -1,8 +1,11 @@
1
- import chalk from "chalk";
2
1
  import { git } from "../../services/git/index.js";
3
- export const hasStagedChangesMiddleware = () => {
4
- if (!git.hasStagedChanges()) {
5
- console.error(chalk.red("Error: No staged changes"));
6
- process.exit(1);
7
- }
2
+ import chalk from "chalk";
3
+ //#region src/commands/middleware/hasStagedChangesMiddleware.ts
4
+ var hasStagedChangesMiddleware = () => {
5
+ if (!git.hasStagedChanges()) {
6
+ console.error(chalk.red("Error: No staged changes"));
7
+ process.exit(1);
8
+ }
8
9
  };
10
+ //#endregion
11
+ export { hasStagedChangesMiddleware };
@@ -0,0 +1,15 @@
1
+ import { isAppleModelAvailable } from "../../../llm/providers/apple/models.js";
2
+ import { AppleProvider } from "../../../llm/providers/apple/index.js";
3
+ //#region src/commands/middleware/setupMiddleware/defaultModels.ts
4
+ var DEFAULT_MODELS = [{
5
+ id: "apple-foundation-models",
6
+ label: "Apple Foundation Models (on-device)",
7
+ isAvailable: () => AppleProvider.isAvailable() && isAppleModelAvailable("system"),
8
+ config: {
9
+ provider: "apple",
10
+ model: "system"
11
+ }
12
+ }];
13
+ var resolveDefaultModel = () => DEFAULT_MODELS.find((model) => model.isAvailable());
14
+ //#endregion
15
+ export { resolveDefaultModel };
@@ -1,40 +1,68 @@
1
+ import { clearAcceptedDefault, getAcceptedDefault, getConfig, saveConfig, setAcceptedDefault, unsetConfigKey } from "../../../config.js";
2
+ import { PROVIDERS, getProviderClass, validateConfig } from "../../../llm/registry.js";
3
+ import { resolveDefaultModel } from "./defaultModels.js";
4
+ import chalk from "chalk";
1
5
  import inquirer from "inquirer";
2
- import { getConfig, validateConfig } from "../../../config.js";
3
- import { setupLocalLLM } from "./setupLocalLLM.js";
4
- import { setupOpenRouter } from "./setupOpenRouter.js";
5
- export const setupMiddleware = async (options) => {
6
- const context = options?.context || "command";
7
- // Always get the current config, even if it's empty
8
- const existingConfig = getConfig();
9
- // If we're running a command, and the config is valid, continue with the command
10
- if (context === "command") {
11
- const isValidConfig = validateConfig();
12
- if (isValidConfig.isValid) {
13
- return getConfig();
14
- }
15
- }
16
- // For initial setup or model command, start by selecting the provider
17
- const useLocalLLMAnswer = await inquirer.prompt([
18
- {
19
- type: "list",
20
- name: "useLocalLLM",
21
- message: "Select LLM provider:",
22
- choices: [
23
- { name: "OpenRouter (remote)", value: false },
24
- { name: "Local LLM", value: true },
25
- ],
26
- default: existingConfig.provider === "local" ? 1 : 0,
27
- },
28
- ]);
29
- // Update config based on selected provider
30
- existingConfig.provider = useLocalLLMAnswer.useLocalLLM
31
- ? "local"
32
- : "openrouter";
33
- // Proceed based on selected provider
34
- if (useLocalLLMAnswer.useLocalLLM) {
35
- return await setupLocalLLM(existingConfig);
36
- }
37
- else {
38
- return await setupOpenRouter(existingConfig);
39
- }
6
+ //#region src/commands/middleware/setupMiddleware/index.ts
7
+ var setupMiddleware = async (options) => {
8
+ const context = options?.context || "command";
9
+ const existingConfig = getConfig();
10
+ const defaultModel = resolveDefaultModel();
11
+ const acceptedDefault = getAcceptedDefault();
12
+ const interactive = Boolean(process.stdin.isTTY);
13
+ const applyDefault = (model) => {
14
+ saveConfig(model.config);
15
+ setAcceptedDefault(model.id);
16
+ return getConfig();
17
+ };
18
+ const confirmDefault = async (message) => {
19
+ const { useDefault } = await inquirer.prompt([{
20
+ type: "confirm",
21
+ name: "useDefault",
22
+ message,
23
+ default: true
24
+ }]);
25
+ return useDefault;
26
+ };
27
+ if (context === "command") {
28
+ if (validateConfig().isValid) {
29
+ if (!acceptedDefault) return getConfig();
30
+ if (!defaultModel || defaultModel.id === acceptedDefault) return getConfig();
31
+ if (!interactive || await confirmDefault(`The recommended default changed to ${defaultModel.label}. Use it?`)) {
32
+ const result = applyDefault(defaultModel);
33
+ if (interactive) console.log(chalk.green(`✓ Now using ${defaultModel.label}.`));
34
+ return result;
35
+ }
36
+ return getConfig();
37
+ }
38
+ if (!existingConfig.provider && defaultModel) {
39
+ if (!interactive) return applyDefault(defaultModel);
40
+ if (await confirmDefault(`No model configured. Use ${defaultModel.label} by default?`)) {
41
+ const result = applyDefault(defaultModel);
42
+ console.log(chalk.green(`✓ Using ${defaultModel.label}. Run 'gitpt model' to change.`));
43
+ return result;
44
+ }
45
+ } else if (!interactive) throw new Error("GitPT is not configured. Run 'gitpt setup' in an interactive terminal.");
46
+ }
47
+ clearAcceptedDefault();
48
+ const providerChoices = PROVIDERS.filter((p) => p.isAvailable()).map((p) => ({
49
+ name: p.label,
50
+ value: p.id
51
+ }));
52
+ existingConfig.provider = (await inquirer.prompt([{
53
+ type: "list",
54
+ name: "provider",
55
+ message: "Select LLM provider:",
56
+ choices: providerChoices,
57
+ default: Math.max(providerChoices.findIndex((c) => c.value === existingConfig.provider), 0)
58
+ }])).provider;
59
+ if (existingConfig.provider !== "local") {
60
+ delete existingConfig.contextWindow;
61
+ unsetConfigKey("contextWindow");
62
+ }
63
+ const spec = getProviderClass(existingConfig.provider);
64
+ if (!spec) throw new Error(`Unknown provider: ${existingConfig.provider ?? "(none)"}.`);
65
+ return spec.setup(existingConfig);
40
66
  };
67
+ //#endregion
68
+ export { setupMiddleware };
@@ -1,6 +1,9 @@
1
- import chalk from "chalk";
2
1
  import { setupMiddleware } from "./middleware/setupMiddleware/index.js";
3
- export const modelCommand = async () => {
4
- console.log(chalk.blue("GitPT Model Selection"));
5
- await setupMiddleware({ context: "model" });
2
+ import chalk from "chalk";
3
+ //#region src/commands/model.ts
4
+ var modelCommand = async () => {
5
+ console.log(chalk.blue("GitPT Model Selection"));
6
+ await setupMiddleware({ context: "model" });
6
7
  };
8
+ //#endregion
9
+ export { modelCommand };
@@ -0,0 +1,11 @@
1
+ //#region src/commands/passthroughArgs.ts
2
+ /**
3
+ * Extra tokens the user passed to a GitPT command that aren't GitPT's own
4
+ * options (e.g. --allow-empty, --amend) — to forward to the wrapped git command.
5
+ * Commander leaves these in `command.args`; we drop the command's own name in
6
+ * case it leaks in. Shared so every command forwards passthrough flags the same
7
+ * way.
8
+ */
9
+ var passthroughArgs = (command) => (command.args ?? []).filter((arg) => arg !== command.name());
10
+ //#endregion
11
+ export { passthroughArgs };
@@ -1,4 +1,5 @@
1
- export const systemPrompt = `
1
+ //#region src/commands/pr/context/systemPrompt.ts
2
+ var systemPrompt = `
2
3
  You are a helpful assistant that generates clear, informative GitHub pull request titles and descriptions.
3
4
 
4
5
  For the title:
@@ -16,3 +17,5 @@ For the description:
16
17
  Format the description in Markdown with sections.
17
18
  Do not include "PR" or "Pull Request" in the title.
18
19
  `;
20
+ //#endregion
21
+ export { systemPrompt };
@@ -1,4 +1,5 @@
1
- export const userPrompt = (context) => `
1
+ //#region src/commands/pr/context/userPrompt.ts
2
+ var userPrompt = (context) => `
2
3
  Generate a pull request title and description for the following changes:
3
4
 
4
5
  ${context}
@@ -20,3 +21,5 @@ This PR adds user authentication using JWT tokens.
20
21
  2. Login with the new user credentials
21
22
  3. Use the returned token to access protected endpoints
22
23
  `;
24
+ //#endregion
25
+ export { userPrompt };
@@ -1,35 +1,29 @@
1
- import chalk from "chalk";
2
- import { getConfig } from "../../config.js";
3
- import { getLLMClient } from "../../llm/index.js";
1
+ import { getProvider } from "../../llm/registry.js";
4
2
  import { systemPrompt } from "./context/systemPrompt.js";
5
3
  import { userPrompt } from "./context/userPrompt.js";
6
4
  import { getPRContext } from "./getPRContext.js";
7
- export const generatePRDetails = async () => {
8
- const { model } = getConfig();
9
- const context = getPRContext().join("\n\n");
10
- const userPromptWithContext = userPrompt(context);
11
- const llmClient = getLLMClient();
12
- try {
13
- const response = await llmClient.chat.completions.create({
14
- model: model,
15
- messages: [
16
- { role: "system", content: systemPrompt },
17
- { role: "user", content: userPromptWithContext },
18
- ],
19
- max_completion_tokens: 1000,
20
- });
21
- const result = response.choices[0].message?.content?.trim();
22
- if (!result) {
23
- throw new Error("No response from LLM");
24
- }
25
- const titleMatch = result.match(/Title:\s*(.+?)(?:\n|$)/);
26
- const descMatch = result.match(/Description:\s*\n([\s\S]+)$/);
27
- const title = titleMatch ? titleMatch[1].trim() : "";
28
- const body = descMatch ? descMatch[1].trim() : result;
29
- return { title, body };
30
- }
31
- catch (error) {
32
- console.error(chalk.red("Error generating PR details:"), error);
33
- throw new Error("Failed to generate PR details");
34
- }
5
+ import chalk from "chalk";
6
+ //#region src/commands/pr/generatePRDetails.ts
7
+ var generatePRDetails = async () => {
8
+ const context = getPRContext().join("\n\n");
9
+ try {
10
+ const provider = getProvider();
11
+ const result = (await provider.complete({
12
+ system: systemPrompt,
13
+ user: userPrompt(context),
14
+ maxTokens: provider.maxOutputTokens
15
+ })).trim();
16
+ if (!result) throw new Error("No response from LLM");
17
+ const titleMatch = result.match(/Title:\s*(.+?)(?:\n|$)/);
18
+ const descMatch = result.match(/Description:\s*\n([\s\S]+)$/);
19
+ return {
20
+ title: titleMatch ? titleMatch[1].trim() : "",
21
+ body: descMatch ? descMatch[1].trim() : result
22
+ };
23
+ } catch (error) {
24
+ console.error(chalk.red("Error generating PR details:"), error);
25
+ throw new Error("Failed to generate PR details");
26
+ }
35
27
  };
28
+ //#endregion
29
+ export { generatePRDetails };
@@ -1,65 +1,40 @@
1
+ import { git } from "../../services/git/index.js";
1
2
  import chalk from "chalk";
2
3
  import { execSync } from "child_process";
3
- import { git } from "../../services/git/index.js";
4
- export const getPRContext = () => {
5
- const currentBranch = git.getCurrentBranch();
6
- const baseBranch = git.getDefaultBranch();
7
- // Get context for PR
8
- const commitMessages = git.getCommitsSinceBaseBranch(baseBranch);
9
- const changedFiles = git.getChangedFiles(baseBranch);
10
- // Check if we have any content to work with
11
- if (commitMessages.length === 0 && changedFiles.length === 0) {
12
- console.log(chalk.yellow("No commits or changed files detected."));
13
- console.log(chalk.yellow("Will attempt to generate PR details using branch name and repository context."));
14
- }
15
- // Get additional context from repository
16
- let repoName = "";
17
- let repoDescription = "";
18
- try {
19
- // Try to get repo information from GitHub CLI
20
- const repoInfo = JSON.parse(execSync("gh repo view --json name,description").toString().trim());
21
- repoName = repoInfo.name || "";
22
- repoDescription = repoInfo.description || "";
23
- }
24
- catch (error) {
25
- // Continue without this info
26
- }
27
- console.log(chalk.blue("Generating PR title and description..."));
28
- // Build a rich context for the AI
29
- let contextSections = [
30
- `Branch: ${currentBranch}`,
31
- `Base branch: ${baseBranch}`,
32
- ];
33
- // Add repository info if available
34
- if (repoName) {
35
- contextSections.push(`Repository: ${repoName}`);
36
- }
37
- if (repoDescription) {
38
- contextSections.push(`Repository description: ${repoDescription}`);
39
- }
40
- // Add commit messages if available
41
- if (commitMessages.length > 0) {
42
- contextSections.push("Commit messages in this branch:", commitMessages.map((msg) => `- ${msg}`).join("\n"));
43
- }
44
- else {
45
- contextSections.push("No commit messages available.");
46
- // Try to extract intent from branch name if no commits
47
- if (currentBranch.includes("/")) {
48
- const branchParts = currentBranch.split("/");
49
- const branchType = branchParts[0]; // e.g., "feature", "fix", "chore"
50
- const branchDescription = branchParts
51
- .slice(1)
52
- .join("/")
53
- .replace(/-/g, " ");
54
- contextSections.push("Branch name analysis:", `Type: ${branchType}`, `Description: ${branchDescription}`);
55
- }
56
- }
57
- // Add changed files if available
58
- if (changedFiles.length > 0) {
59
- contextSections.push("Files changed in this branch:", changedFiles.map((file) => `- ${file}`).join("\n"));
60
- }
61
- else {
62
- contextSections.push("No file changes detected.");
63
- }
64
- return contextSections;
4
+ //#region src/commands/pr/getPRContext.ts
5
+ var getPRContext = () => {
6
+ const currentBranch = git.getCurrentBranch();
7
+ const baseBranch = git.getDefaultBranch();
8
+ const commitMessages = git.getCommitsSinceBaseBranch(baseBranch);
9
+ const changedFiles = git.getChangedFiles(baseBranch);
10
+ if (commitMessages.length === 0 && changedFiles.length === 0) {
11
+ console.log(chalk.yellow("No commits or changed files detected."));
12
+ console.log(chalk.yellow("Will attempt to generate PR details using branch name and repository context."));
13
+ }
14
+ let repoName = "";
15
+ let repoDescription = "";
16
+ try {
17
+ const repoInfo = JSON.parse(execSync("gh repo view --json name,description").toString().trim());
18
+ repoName = repoInfo.name || "";
19
+ repoDescription = repoInfo.description || "";
20
+ } catch (error) {}
21
+ console.log(chalk.blue("Generating PR title and description..."));
22
+ let contextSections = [`Branch: ${currentBranch}`, `Base branch: ${baseBranch}`];
23
+ if (repoName) contextSections.push(`Repository: ${repoName}`);
24
+ if (repoDescription) contextSections.push(`Repository description: ${repoDescription}`);
25
+ if (commitMessages.length > 0) contextSections.push("Commit messages in this branch:", commitMessages.map((msg) => `- ${msg}`).join("\n"));
26
+ else {
27
+ contextSections.push("No commit messages available.");
28
+ if (currentBranch.includes("/")) {
29
+ const branchParts = currentBranch.split("/");
30
+ const branchType = branchParts[0];
31
+ const branchDescription = branchParts.slice(1).join("/").replace(/-/g, " ");
32
+ contextSections.push("Branch name analysis:", `Type: ${branchType}`, `Description: ${branchDescription}`);
33
+ }
34
+ }
35
+ if (changedFiles.length > 0) contextSections.push("Files changed in this branch:", changedFiles.map((file) => `- ${file}`).join("\n"));
36
+ else contextSections.push("No file changes detected.");
37
+ return contextSections;
65
38
  };
39
+ //#endregion
40
+ export { getPRContext };