@ksw8954/git-ai-commit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/AGENTS.md +38 -0
  2. package/CRUSH.md +28 -0
  3. package/Makefile +32 -0
  4. package/README.md +145 -0
  5. package/dist/commands/ai.d.ts +35 -0
  6. package/dist/commands/ai.d.ts.map +1 -0
  7. package/dist/commands/ai.js +206 -0
  8. package/dist/commands/ai.js.map +1 -0
  9. package/dist/commands/commit.d.ts +17 -0
  10. package/dist/commands/commit.d.ts.map +1 -0
  11. package/dist/commands/commit.js +126 -0
  12. package/dist/commands/commit.js.map +1 -0
  13. package/dist/commands/config.d.ts +33 -0
  14. package/dist/commands/config.d.ts.map +1 -0
  15. package/dist/commands/config.js +141 -0
  16. package/dist/commands/config.js.map +1 -0
  17. package/dist/commands/configCommand.d.ts +20 -0
  18. package/dist/commands/configCommand.d.ts.map +1 -0
  19. package/dist/commands/configCommand.js +108 -0
  20. package/dist/commands/configCommand.js.map +1 -0
  21. package/dist/commands/git.d.ts +26 -0
  22. package/dist/commands/git.d.ts.map +1 -0
  23. package/dist/commands/git.js +150 -0
  24. package/dist/commands/git.js.map +1 -0
  25. package/dist/commands/loadEnv.d.ts +2 -0
  26. package/dist/commands/loadEnv.d.ts.map +1 -0
  27. package/dist/commands/loadEnv.js +11 -0
  28. package/dist/commands/loadEnv.js.map +1 -0
  29. package/dist/commands/prCommand.d.ts +16 -0
  30. package/dist/commands/prCommand.d.ts.map +1 -0
  31. package/dist/commands/prCommand.js +61 -0
  32. package/dist/commands/prCommand.js.map +1 -0
  33. package/dist/commands/tag.d.ts +17 -0
  34. package/dist/commands/tag.d.ts.map +1 -0
  35. package/dist/commands/tag.js +127 -0
  36. package/dist/commands/tag.js.map +1 -0
  37. package/dist/index.d.ts +3 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +23 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/prompts/commit.d.ts +3 -0
  42. package/dist/prompts/commit.d.ts.map +1 -0
  43. package/dist/prompts/commit.js +101 -0
  44. package/dist/prompts/commit.js.map +1 -0
  45. package/dist/prompts/pr.d.ts +3 -0
  46. package/dist/prompts/pr.d.ts.map +1 -0
  47. package/dist/prompts/pr.js +58 -0
  48. package/dist/prompts/pr.js.map +1 -0
  49. package/dist/prompts/tag.d.ts +3 -0
  50. package/dist/prompts/tag.d.ts.map +1 -0
  51. package/dist/prompts/tag.js +42 -0
  52. package/dist/prompts/tag.js.map +1 -0
  53. package/eslint.config.js +35 -0
  54. package/jest.config.js +16 -0
  55. package/package.json +51 -0
  56. package/src/__tests__/ai.test.ts +185 -0
  57. package/src/__tests__/commitCommand.test.ts +155 -0
  58. package/src/__tests__/config.test.ts +238 -0
  59. package/src/__tests__/git.test.ts +88 -0
  60. package/src/__tests__/integration.test.ts +138 -0
  61. package/src/__tests__/prCommand.test.ts +121 -0
  62. package/src/__tests__/tagCommand.test.ts +197 -0
  63. package/src/commands/ai.ts +266 -0
  64. package/src/commands/commit.ts +215 -0
  65. package/src/commands/config.ts +182 -0
  66. package/src/commands/configCommand.ts +139 -0
  67. package/src/commands/git.ts +174 -0
  68. package/src/commands/history.ts +82 -0
  69. package/src/commands/loadEnv.ts +5 -0
  70. package/src/commands/log.ts +71 -0
  71. package/src/commands/prCommand.ts +108 -0
  72. package/src/commands/tag.ts +230 -0
  73. package/src/index.ts +29 -0
  74. package/src/prompts/commit.ts +105 -0
  75. package/src/prompts/pr.ts +64 -0
  76. package/src/prompts/tag.ts +48 -0
  77. package/tsconfig.json +19 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/commands/tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAU;;IAgBzB,OAAO,CAAC,eAAe;YAoBT,SAAS;YAkFT,cAAc;IAgB5B,UAAU,IAAI,OAAO;CAGtB"}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TagCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const readline_1 = __importDefault(require("readline"));
9
+ const ai_1 = require("./ai");
10
+ const config_1 = require("./config");
11
+ const git_1 = require("./git");
12
+ class TagCommand {
13
+ constructor() {
14
+ this.program = new commander_1.Command('tag')
15
+ .description('Create an annotated git tag with optional AI-generated notes')
16
+ .argument('<name>', 'Tag name to create')
17
+ .option('-k, --api-key <key>', 'OpenAI API key (overrides env var)')
18
+ .option('--base-url <url>', 'Custom API base URL (overrides env var)')
19
+ .option('-m, --model <model>', 'Model to use (overrides env var)')
20
+ .option('--message <message>', 'Tag message to use directly (skips AI generation)')
21
+ .option('-t, --base-tag <tag>', 'Existing tag to diff against when generating notes')
22
+ .action(async (tagName, options) => {
23
+ await this.handleTag(tagName, options);
24
+ });
25
+ }
26
+ resolveAIConfig(options) {
27
+ const storedConfig = config_1.ConfigService.getConfig();
28
+ const mergedApiKey = options.apiKey || storedConfig.apiKey;
29
+ const mergedBaseURL = options.baseUrl || storedConfig.baseURL;
30
+ const mergedModel = options.model || storedConfig.model;
31
+ config_1.ConfigService.validateConfig({
32
+ apiKey: mergedApiKey,
33
+ language: storedConfig.language
34
+ });
35
+ return {
36
+ apiKey: mergedApiKey,
37
+ baseURL: mergedBaseURL,
38
+ model: mergedModel,
39
+ language: storedConfig.language
40
+ };
41
+ }
42
+ async handleTag(tagName, options) {
43
+ const trimmedName = tagName?.trim();
44
+ if (!trimmedName) {
45
+ console.error('Tag name is required.');
46
+ process.exit(1);
47
+ return;
48
+ }
49
+ let tagMessage = options.message?.trim();
50
+ if (!tagMessage) {
51
+ console.log('Collecting commit history for tag notes...');
52
+ let baseTag = options.baseTag?.trim();
53
+ if (!baseTag) {
54
+ const latestTagResult = await git_1.GitService.getLatestTag();
55
+ if (latestTagResult.success && latestTagResult.tag) {
56
+ baseTag = latestTagResult.tag;
57
+ console.log(`Using latest tag ${baseTag} as base.`);
58
+ }
59
+ else {
60
+ console.log('No existing tag found; using entire commit history.');
61
+ }
62
+ }
63
+ const historyResult = await git_1.GitService.getCommitSummariesSince(baseTag);
64
+ if (!historyResult.success || !historyResult.log) {
65
+ console.error('Error:', historyResult.error ?? 'Unable to read commit history.');
66
+ process.exit(1);
67
+ return;
68
+ }
69
+ let aiConfig;
70
+ try {
71
+ aiConfig = this.resolveAIConfig(options);
72
+ }
73
+ catch (error) {
74
+ console.error('Error:', error instanceof Error ? error.message : error);
75
+ process.exit(1);
76
+ return;
77
+ }
78
+ const aiService = new ai_1.AIService(aiConfig);
79
+ const aiResult = await aiService.generateTagNotes(trimmedName, historyResult.log);
80
+ if (!aiResult.success || !aiResult.notes) {
81
+ console.error('Error:', aiResult.error ?? 'Failed to generate tag notes.');
82
+ process.exit(1);
83
+ return;
84
+ }
85
+ tagMessage = aiResult.notes;
86
+ }
87
+ console.log(`Creating annotated tag ${trimmedName}...`);
88
+ const created = await git_1.GitService.createAnnotatedTag(trimmedName, tagMessage);
89
+ if (!created) {
90
+ console.error('❌ Failed to create tag');
91
+ process.exit(1);
92
+ return;
93
+ }
94
+ console.log(`✅ Tag ${trimmedName} created successfully!`);
95
+ console.log('\nTag message:\n');
96
+ console.log(tagMessage);
97
+ const shouldPush = await this.confirmTagPush(trimmedName);
98
+ if (shouldPush) {
99
+ console.log(`Pushing tag ${trimmedName} to remote...`);
100
+ const pushSuccess = await git_1.GitService.pushTag(trimmedName);
101
+ if (pushSuccess) {
102
+ console.log(`✅ Tag ${trimmedName} pushed successfully!`);
103
+ }
104
+ else {
105
+ console.error('❌ Failed to push tag to remote');
106
+ process.exit(1);
107
+ }
108
+ }
109
+ }
110
+ async confirmTagPush(tagName) {
111
+ const rl = readline_1.default.createInterface({
112
+ input: process.stdin,
113
+ output: process.stdout
114
+ });
115
+ const answer = await new Promise(resolve => {
116
+ rl.question(`Push tag ${tagName} to remote? (y/n): `, resolve);
117
+ });
118
+ rl.close();
119
+ const normalized = answer.trim().toLowerCase();
120
+ return normalized === 'y' || normalized === 'yes';
121
+ }
122
+ getCommand() {
123
+ return this.program;
124
+ }
125
+ }
126
+ exports.TagCommand = TagCommand;
127
+ //# sourceMappingURL=tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../../src/commands/tag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const commit_1 = require("./commands/commit");
6
+ const configCommand_1 = require("./commands/configCommand");
7
+ const prCommand_1 = require("./commands/prCommand");
8
+ const tag_1 = require("./commands/tag");
9
+ const program = new commander_1.Command();
10
+ program
11
+ .name('git-ai-commit')
12
+ .description('AI-powered git commit message generator')
13
+ .version('1.0.0');
14
+ const commitCommand = new commit_1.CommitCommand();
15
+ const configCommand = new configCommand_1.ConfigCommand();
16
+ const pullRequestCommand = new prCommand_1.PullRequestCommand();
17
+ const tagCommand = new tag_1.TagCommand();
18
+ program.addCommand(commitCommand.getCommand());
19
+ program.addCommand(configCommand.getCommand());
20
+ program.addCommand(pullRequestCommand.getCommand());
21
+ program.addCommand(tagCommand.getCommand());
22
+ program.parse();
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,8CAAkD;AAClD,4DAAyD;AACzD,oDAA0D;AAC1D,wCAA4C;AAE5C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,yCAAyC,CAAC;KACtD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,MAAM,aAAa,GAAG,IAAI,sBAAa,EAAE,CAAC;AAC1C,MAAM,aAAa,GAAG,IAAI,6BAAa,EAAE,CAAC;AAC1C,MAAM,kBAAkB,GAAG,IAAI,8BAAkB,EAAE,CAAC;AACpD,MAAM,UAAU,GAAG,IAAI,gBAAU,EAAE,CAAC;AAEpC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC;AAC/C,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC,CAAC;AAC/C,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,UAAU,EAAE,CAAC,CAAC;AACpD,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;AAE5C,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type CommitPromptLanguage = 'ko' | 'en';
2
+ export declare const generateCommitPrompt: (gitContext: string, customInstructions?: string, language?: CommitPromptLanguage) => string;
3
+ //# sourceMappingURL=commit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/prompts/commit.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,EAClB,2BAAuB,EACvB,WAAU,oBAA2B,KACpC,MAkGF,CAAC"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCommitPrompt = void 0;
4
+ const generateCommitPrompt = (gitContext, customInstructions = '', language = 'ko') => {
5
+ const languageRequirement = language === 'ko'
6
+ ? `## Korean Language Requirement
7
+ - All commit messages MUST be written in Korean (한글)
8
+ - Description, body, and footer text must use Korean
9
+ - Keep Korean messages concise`
10
+ : `## English Language Requirement
11
+ - All commit messages MUST be written in English
12
+ - Description, body, and footer text must use English
13
+ - Keep the tone concise and professional`;
14
+ const bodyGuidelines = language === 'ko'
15
+ ? `### Body Guidelines (Optional)
16
+ - Insert one blank line after the description and express additional details as
17
+ markdown bullet points (\`- \`) written in Korean.
18
+ - Each bullet should explain the "what" and "why", not the "how".
19
+ - Wrap at 72 characters per line and keep bullets concise.
20
+ - Use the bullet body only for complex changes that need clarification; omit it
21
+ when the summary line is sufficient.`
22
+ : `### Body Guidelines (Optional)
23
+ - Insert one blank line after the description and use markdown bullet points (\`- \`) in English.
24
+ - Each bullet should explain the "what" and "why", not the "how".
25
+ - Wrap at 72 characters per line and keep bullets concise.
26
+ - Include a body only for complex changes that need clarification.`;
27
+ const footerGuidelines = language === 'ko'
28
+ ? `### Footer Guidelines (Optional)
29
+ - Start one blank line after body
30
+ - **Breaking Changes**: \`BREAKING CHANGE: description\``
31
+ : `### Footer Guidelines (Optional)
32
+ - Start one blank line after the body
33
+ - **Breaking Changes**: \`BREAKING CHANGE: description\``;
34
+ return `# Conventional Commit Message Generator
35
+ ## System Instructions
36
+ You are an expert Git commit message generator that creates conventional commit messages based on staged changes. Analyze the provided git diff output and generate appropriate conventional commit messages following the specification.
37
+
38
+ ${customInstructions}
39
+
40
+ ## CRITICAL: Commit Message Output Rules
41
+ - DO NOT include any memory bank status indicators like "[Memory Bank: Active]" or "[Memory Bank: Missing]"
42
+ - DO NOT include any task-specific formatting or artifacts from other rules
43
+ - ONLY Generate a clean conventional commit message as specified below
44
+
45
+ ${gitContext}
46
+
47
+ ## Conventional Commits Format
48
+ Generate commit messages following this exact structure:
49
+ \n<type>[optional scope]: <description>
50
+ [optional body]
51
+ [optional footer(s)]
52
+
53
+ ### Core Types (Required)
54
+ - **feat**: New feature or functionality (MINOR version bump)
55
+ - **fix**: Bug fix or error correction (PATCH version bump)
56
+
57
+ ### Additional Types (Extended)
58
+ - **docs**: Documentation changes only
59
+ - **style**: Code style changes (whitespace, formatting, semicolons, etc.)
60
+ - **refactor**: Code refactoring without feature changes or bug fixes
61
+ - **perf**: Performance improvements
62
+ - **test**: Adding or fixing tests
63
+ - **build**: Build system or external dependency changes
64
+ - **ci**: CI/CD configuration changes
65
+ - **chore**: Maintenance tasks, tooling changes
66
+ - **revert**: Reverting previous commits
67
+
68
+ ### Scope Guidelines
69
+ - Use parentheses: \`feat(api):\`, \`fix(ui):\`
70
+ - Common scopes: \`api\`, \`ui\`, \`auth\`, \`db\`, \`config\`, \`deps\`, \`docs\`
71
+ - For monorepos: package or module names
72
+ - Keep scope concise and lowercase
73
+
74
+ ### Description Rules
75
+ - Use imperative mood ("add" not "added" or "adds")
76
+ - Start with lowercase letter
77
+ - No period at the end
78
+ - Be concise but descriptive
79
+ - Must be written as a single line without line breaks
80
+
81
+ ${languageRequirement}
82
+
83
+ ${bodyGuidelines}
84
+
85
+ ${footerGuidelines}
86
+
87
+ ## Analysis Instructions
88
+ When analyzing staged changes:
89
+ 1. Determine Primary Type based on the nature of changes
90
+ 2. Identify Scope from modified directories or modules
91
+ 3. Craft Description focusing on the most significant change
92
+ 4. Determine if there are Breaking Changes
93
+ 5. For complex changes, include a detailed body explaining what and why
94
+ 6. Add appropriate footers for issue references or breaking changes
95
+
96
+ For significant changes, include a detailed body explaining the changes.
97
+
98
+ Return ONLY the commit message in the conventional format, nothing else.`;
99
+ };
100
+ exports.generateCommitPrompt = generateCommitPrompt;
101
+ //# sourceMappingURL=commit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../src/prompts/commit.ts"],"names":[],"mappings":";;;AAEO,MAAM,oBAAoB,GAAG,CAClC,UAAkB,EAClB,kBAAkB,GAAG,EAAE,EACvB,WAAiC,IAAI,EAC7B,EAAE;IACV,MAAM,mBAAmB,GAAG,QAAQ,KAAK,IAAI;QAC3C,CAAC,CAAC;;;+BAGyB;QAC3B,CAAC,CAAC;;;yCAGmC,CAAC;IAExC,MAAM,cAAc,GAAG,QAAQ,KAAK,IAAI;QACtC,CAAC,CAAC;;;;;;uCAMiC;QACnC,CAAC,CAAC;;;;mEAI6D,CAAC;IAElE,MAAM,gBAAgB,GAAG,QAAQ,KAAK,IAAI;QACxC,CAAC,CAAC;;yDAEmD;QACrD,CAAC,CAAC;;yDAEmD,CAAC;IAExD,OAAO;;;;EAIP,kBAAkB;;;;;;;EAOlB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoCV,mBAAmB;;EAEnB,cAAc;;EAEd,gBAAgB;;;;;;;;;;;;;yEAauD,CAAC;AAC1E,CAAC,CAAC;AAtGW,QAAA,oBAAoB,wBAsG/B"}
@@ -0,0 +1,3 @@
1
+ export type PullRequestPromptLanguage = 'ko' | 'en';
2
+ export declare const generatePullRequestPrompt: (baseBranch: string, compareBranch: string, customInstructions?: string, language?: PullRequestPromptLanguage) => string;
3
+ //# sourceMappingURL=pr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr.d.ts","sourceRoot":"","sources":["../../src/prompts/pr.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,eAAO,MAAM,yBAAyB,GACpC,YAAY,MAAM,EAClB,eAAe,MAAM,EACrB,2BAAuB,EACvB,WAAU,yBAAgC,KACzC,MAwDF,CAAC"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePullRequestPrompt = void 0;
4
+ const generatePullRequestPrompt = (baseBranch, compareBranch, customInstructions = '', language = 'ko') => {
5
+ const languageRequirement = language === 'ko'
6
+ ? 'Write the entire pull request title and body in Korean.'
7
+ : 'Write the entire pull request title and body in English.';
8
+ const titleGuidelines = language === 'ko'
9
+ ? `### 제목 작성 규칙
10
+ - 한 줄짜리 명령형 문장으로 작성합니다 (예: "Refactor validator 로직 정리").
11
+ - 접두사는 사용하지 않습니다 (예: "Feat:" 금지).
12
+ - 72자를 넘지 않도록 합니다.`
13
+ : `### Title Guidelines
14
+ - Use a single imperative sentence (e.g., "Refactor validator handling").
15
+ - Do not prefix with labels like "Feat:".
16
+ - Keep the title under 72 characters.`;
17
+ const summaryGuidelines = language === 'ko'
18
+ ? `### 본문 구성
19
+ - "## Summary" 헤딩 아래에 핵심 변경 사항을 강조하는 불릿 리스트를 작성합니다.
20
+ - 각 불릿은 "무엇을"과 "왜"를 포함하고, 한국어로 1줄 내로 작성합니다.
21
+ - 영향이 큰 변경은 별도의 불릿으로 구분합니다.`
22
+ : `### Body Structure
23
+ - Under a "## Summary" heading, add bullet points that explain what changed and why.
24
+ - Keep each bullet to a single concise English sentence.
25
+ - Separate large areas of impact into individual bullets.`;
26
+ const testingGuidelines = language === 'ko'
27
+ ? `### 테스트 정보
28
+ - "## Testing" 헤딩 아래에 검증 방법을 불릿으로 정리합니다.
29
+ - 수동 테스트, 자동 테스트, 혹은 "테스트 필요 없음"을 명시합니다.`
30
+ : `### Testing Details
31
+ - Under a "## Testing" heading, list how the changes were verified.
32
+ - Call out manual steps, automated checks, or "Not tested" when applicable.`;
33
+ return `You are an expert software engineer preparing a pull request description.
34
+ Compare the git history and code changes between the base branch "${baseBranch}" and the compare branch "${compareBranch}" and summarise the meaningful differences in a PR-friendly format.
35
+
36
+ ${customInstructions}
37
+
38
+ ## Output Contract
39
+ - Return ONLY markdown suitable for pasting into a pull request form.
40
+ - First line MUST be the pull request title, adhering to the title guidelines below.
41
+ - After one blank line, include the sections exactly as shown:
42
+ - "## Summary"
43
+ - "## Testing"
44
+ - If a section has no content, provide a single bullet: "- 없음" in Korean or "- None" in English (use language-appropriate wording).
45
+ - Do not invent changes that are not present in the provided diff.
46
+
47
+ ${languageRequirement}
48
+
49
+ ${titleGuidelines}
50
+
51
+ ${summaryGuidelines}
52
+
53
+ ${testingGuidelines}
54
+
55
+ Focus on user-facing impact, breaking changes, and notable refactors. Avoid raw diff dumps.`;
56
+ };
57
+ exports.generatePullRequestPrompt = generatePullRequestPrompt;
58
+ //# sourceMappingURL=pr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr.js","sourceRoot":"","sources":["../../src/prompts/pr.ts"],"names":[],"mappings":";;;AAEO,MAAM,yBAAyB,GAAG,CACvC,UAAkB,EAClB,aAAqB,EACrB,kBAAkB,GAAG,EAAE,EACvB,WAAsC,IAAI,EAClC,EAAE;IACV,MAAM,mBAAmB,GAAG,QAAQ,KAAK,IAAI;QAC3C,CAAC,CAAC,yDAAyD;QAC3D,CAAC,CAAC,0DAA0D,CAAC;IAE/D,MAAM,eAAe,GAAG,QAAQ,KAAK,IAAI;QACvC,CAAC,CAAC;;;mBAGa;QACf,CAAC,CAAC;;;sCAGgC,CAAC;IAErC,MAAM,iBAAiB,GAAG,QAAQ,KAAK,IAAI;QACzC,CAAC,CAAC;;;4BAGsB;QACxB,CAAC,CAAC;;;0DAGoD,CAAC;IAEzD,MAAM,iBAAiB,GAAG,QAAQ,KAAK,IAAI;QACzC,CAAC,CAAC;;yCAEmC;QACrC,CAAC,CAAC;;4EAEsE,CAAC;IAE3E,OAAO;oEAC2D,UAAU,6BAA6B,aAAa;;EAEtH,kBAAkB;;;;;;;;;;;EAWlB,mBAAmB;;EAEnB,eAAe;;EAEf,iBAAiB;;EAEjB,iBAAiB;;4FAEyE,CAAC;AAC7F,CAAC,CAAC;AA7DW,QAAA,yBAAyB,6BA6DpC"}
@@ -0,0 +1,3 @@
1
+ export type TagPromptLanguage = 'ko' | 'en';
2
+ export declare const generateTagPrompt: (tagName: string, customInstructions?: string, language?: TagPromptLanguage) => string;
3
+ //# sourceMappingURL=tag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/prompts/tag.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,2BAAuB,EACvB,WAAU,iBAAwB,KACjC,MAyCF,CAAC"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTagPrompt = void 0;
4
+ const generateTagPrompt = (tagName, customInstructions = '', language = 'ko') => {
5
+ const summaryInstruction = language === 'ko'
6
+ ? 'Begin with a short summary sentence (in Korean) that captures the overall impact of the release.'
7
+ : 'Begin with a short summary sentence (in English) that captures the overall impact of the release.';
8
+ const listInstruction = language === 'ko'
9
+ ? 'After the summary, list every change as a markdown bullet (-) grouped by category: 사용자 기능, 버그 수정, 유지 보수.'
10
+ : 'After the summary, list every change as a markdown bullet (-) grouped by category: User Features, Bug Fixes, Maintenance.';
11
+ const emptyCategoryInstruction = language === 'ko'
12
+ ? 'If a category has no changes, include a bullet stating - 해당 사항 없음.'
13
+ : 'If a category has no changes, include a bullet stating - None.';
14
+ const noChangesInstruction = language === 'ko'
15
+ ? 'If no changes exist at all, state "변경 사항 없음" plainly.'
16
+ : 'If no changes exist at all, state "No changes to report." plainly.';
17
+ const outputLanguageLine = language === 'ko'
18
+ ? 'Write the release notes in Korean using concise markdown.'
19
+ : 'Write the release notes in English using concise markdown.';
20
+ return `You are an experienced release manager. Produce clear, user-facing release notes that describe the differences between the previous tag and ${tagName}.
21
+
22
+ ## Objective
23
+ Summarize the meaningful changes that occurred between the prior release tag and ${tagName}. Treat the commit log provided by the user message as the complete history of changes since the previous tag.
24
+
25
+ ## Input Context
26
+ - Target tag to publish: ${tagName}
27
+ - Commit history between the previous tag and ${tagName} will be supplied in the user message (most recent first).
28
+
29
+ ${customInstructions}
30
+
31
+ ## Output Requirements
32
+ - ${outputLanguageLine}
33
+ - ${summaryInstruction}
34
+ - ${listInstruction}
35
+ - Use short phrases for each bullet and include scope/component names when helpful, without copying commit messages verbatim.
36
+ - ${emptyCategoryInstruction}
37
+ - ${noChangesInstruction}
38
+ - Do not invent work beyond what appears in the commit log.
39
+ - Return only the release notes content with no surrounding commentary.`;
40
+ };
41
+ exports.generateTagPrompt = generateTagPrompt;
42
+ //# sourceMappingURL=tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.js","sourceRoot":"","sources":["../../src/prompts/tag.ts"],"names":[],"mappings":";;;AAEO,MAAM,iBAAiB,GAAG,CAC/B,OAAe,EACf,kBAAkB,GAAG,EAAE,EACvB,WAA8B,IAAI,EAC1B,EAAE;IACV,MAAM,kBAAkB,GAAG,QAAQ,KAAK,IAAI;QAC1C,CAAC,CAAC,kGAAkG;QACpG,CAAC,CAAC,mGAAmG,CAAC;IAExG,MAAM,eAAe,GAAG,QAAQ,KAAK,IAAI;QACvC,CAAC,CAAC,0GAA0G;QAC5G,CAAC,CAAC,2HAA2H,CAAC;IAEhI,MAAM,wBAAwB,GAAG,QAAQ,KAAK,IAAI;QAChD,CAAC,CAAC,oEAAoE;QACtE,CAAC,CAAC,gEAAgE,CAAC;IAErE,MAAM,oBAAoB,GAAG,QAAQ,KAAK,IAAI;QAC5C,CAAC,CAAC,uDAAuD;QACzD,CAAC,CAAC,oEAAoE,CAAC;IAEzE,MAAM,kBAAkB,GAAG,QAAQ,KAAK,IAAI;QAC1C,CAAC,CAAC,2DAA2D;QAC7D,CAAC,CAAC,4DAA4D,CAAC;IAEjE,OAAO,+IAA+I,OAAO;;;mFAG5E,OAAO;;;2BAG/D,OAAO;gDACc,OAAO;;EAErD,kBAAkB;;;IAGhB,kBAAkB;IAClB,kBAAkB;IAClB,eAAe;;IAEf,wBAAwB;IACxB,oBAAoB;;wEAEgD,CAAC;AACzE,CAAC,CAAC;AA7CW,QAAA,iBAAiB,qBA6C5B"}
@@ -0,0 +1,35 @@
1
+ const js = require('@eslint/js');
2
+ const tsPlugin = require('@typescript-eslint/eslint-plugin');
3
+ const tsParser = require('@typescript-eslint/parser');
4
+ const globals = require('globals');
5
+
6
+ module.exports = [
7
+ {
8
+ ignores: ['dist/**', 'coverage/**', 'node_modules/**'],
9
+ },
10
+ {
11
+ files: ['**/*.ts'],
12
+ languageOptions: {
13
+ parser: tsParser,
14
+ parserOptions: {
15
+ ecmaVersion: 'latest',
16
+ sourceType: 'module',
17
+ },
18
+ globals: {
19
+ ...globals.node,
20
+ ...globals.es2021,
21
+ ...globals.jest,
22
+ },
23
+ },
24
+ plugins: {
25
+ '@typescript-eslint': tsPlugin,
26
+ },
27
+ rules: {
28
+ ...js.configs.recommended.rules,
29
+ ...tsPlugin.configs.recommended.rules,
30
+ '@typescript-eslint/no-unused-vars': 'error',
31
+ '@typescript-eslint/no-explicit-any': 'warn',
32
+ 'no-console': 'warn',
33
+ },
34
+ },
35
+ ];
package/jest.config.js ADDED
@@ -0,0 +1,16 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ roots: ['<rootDir>/src'],
5
+ testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6
+ transform: {
7
+ '^.+\\.ts$': 'ts-jest',
8
+ },
9
+ collectCoverageFrom: [
10
+ 'src/**/*.ts',
11
+ '!src/**/*.d.ts',
12
+ '!src/index.ts',
13
+ ],
14
+ coverageDirectory: 'coverage',
15
+ coverageReporters: ['text', 'lcov', 'html'],
16
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@ksw8954/git-ai-commit",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered git commit message generator",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "git-ai-commit": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/index.ts",
12
+ "test": "jest",
13
+ "test:file": "jest",
14
+ "test:watch": "jest --watch",
15
+ "test:coverage": "jest --coverage",
16
+ "lint": "eslint src/**/*.ts",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/onaries/git-ai-commit.git"
22
+ },
23
+ "keywords": [],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/onaries/git-ai-commit/issues"
31
+ },
32
+ "homepage": "https://github.com/onaries/git-ai-commit#readme",
33
+ "devDependencies": {
34
+ "@types/dotenv": "^6.1.1",
35
+ "@types/jest": "^30.0.0",
36
+ "@types/node": "^24.3.3",
37
+ "@typescript-eslint/eslint-plugin": "^8.43.0",
38
+ "@typescript-eslint/parser": "^8.43.0",
39
+ "dotenv": "^17.2.2",
40
+ "eslint": "^9.35.0",
41
+ "jest": "^30.1.3",
42
+ "nodemon": "^3.1.10",
43
+ "ts-jest": "^29.4.1",
44
+ "ts-node": "^10.9.2",
45
+ "typescript": "^5.9.2"
46
+ },
47
+ "dependencies": {
48
+ "commander": "^14.0.1",
49
+ "openai": "^5.20.2"
50
+ }
51
+ }