import { note } from '@clack/prompts'; import { OpenAI } from 'openai'; import { getConfig } from './commands/config'; import { i18n, I18nLocals } from './i18n'; import { configureCommitlintIntegration } from './modules/commitlint/config'; import { commitlintPrompts } from './modules/commitlint/prompts'; import { ConsistencyPrompt } from './modules/commitlint/types'; import * as utils from './modules/commitlint/utils'; import { removeConventionalCommitWord } from './utils/removeConventionalCommitWord'; const config = getConfig(); const translation = i18n[(config.OCO_LANGUAGE as I18nLocals) || 'en']; export const IDENTITY = 'You are to act as an author of a commit message in git.'; const GITMOJI_HELP = `Use GitMoji convention to preface the commit. Here are some help to choose the right emoji (emoji, description): ๐Ÿ›, Fix a bug; โœจ, Introduce new features; ๐Ÿ“, Add or update documentation; ๐Ÿš€, Deploy stuff; โœ…, Add, update, or pass tests; โ™ป๏ธ, Refactor code; โฌ†๏ธ, Upgrade dependencies; ๐Ÿ”ง, Add or update configuration files; ๐ŸŒ, Internationalization and localization; ๐Ÿ’ก, Add or update comments in source code;`; const FULL_GITMOJI_SPEC = `${GITMOJI_HELP} ๐ŸŽจ, Improve structure / format of the code; โšก๏ธ, Improve performance; ๐Ÿ”ฅ, Remove code or files; ๐Ÿš‘๏ธ, Critical hotfix; ๐Ÿ’„, Add or update the UI and style files; ๐ŸŽ‰, Begin a project; ๐Ÿ”’๏ธ, Fix security issues; ๐Ÿ”, Add or update secrets; ๐Ÿ”–, Release / Version tags; ๐Ÿšจ, Fix compiler / linter warnings; ๐Ÿšง, Work in progress; ๐Ÿ’š, Fix CI Build; โฌ‡๏ธ, Downgrade dependencies; ๐Ÿ“Œ, Pin dependencies to specific versions; ๐Ÿ‘ท, Add or update CI build system; ๐Ÿ“ˆ, Add or update analytics or track code; โž•, Add a dependency; โž–, Remove a dependency; ๐Ÿ”จ, Add or update development scripts; โœ๏ธ, Fix typos; ๐Ÿ’ฉ, Write bad code that needs to be improved; โช๏ธ, Revert changes; ๐Ÿ”€, Merge branches; ๐Ÿ“ฆ๏ธ, Add or update compiled files or packages; ๐Ÿ‘ฝ๏ธ, Update code due to external API changes; ๐Ÿšš, Move or rename resources (e.g.: files, paths, routes); ๐Ÿ“„, Add or update license; ๐Ÿ’ฅ, Introduce breaking changes; ๐Ÿฑ, Add or update assets; โ™ฟ๏ธ, Improve accessibility; ๐Ÿป, Write code drunkenly; ๐Ÿ’ฌ, Add or update text and literals; ๐Ÿ—ƒ๏ธ, Perform database related changes; ๐Ÿ”Š, Add or update logs; ๐Ÿ”‡, Remove logs; ๐Ÿ‘ฅ, Add or update contributor(s); ๐Ÿšธ, Improve user experience / usability; ๐Ÿ—๏ธ, Make architectural changes; ๐Ÿ“ฑ, Work on responsive design; ๐Ÿคก, Mock things; ๐Ÿฅš, Add or update an easter egg; ๐Ÿ™ˆ, Add or update a .gitignore file; ๐Ÿ“ธ, Add or update snapshots; โš—๏ธ, Perform experiments; ๐Ÿ”๏ธ, Improve SEO; ๐Ÿท๏ธ, Add or update types; ๐ŸŒฑ, Add or update seed files; ๐Ÿšฉ, Add, update, or remove feature flags; ๐Ÿฅ…, Catch errors; ๐Ÿ’ซ, Add or update animations and transitions; ๐Ÿ—‘๏ธ, Deprecate code that needs to be cleaned up; ๐Ÿ›‚, Work on code related to authorization, roles and permissions; ๐Ÿฉน, Simple fix for a non-critical issue; ๐Ÿง, Data exploration/inspection; โšฐ๏ธ, Remove dead code; ๐Ÿงช, Add a failing test; ๐Ÿ‘”, Add or update business logic; ๐Ÿฉบ, Add or update healthcheck; ๐Ÿงฑ, Infrastructure related changes; ๐Ÿง‘โ€๐Ÿ’ป, Improve developer experience; ๐Ÿ’ธ, Add sponsorships or money related infrastructure; ๐Ÿงต, Add or update code related to multithreading or concurrency; ๐Ÿฆบ, Add or update code related to validation.`; const CONVENTIONAL_COMMIT_KEYWORDS = 'Do not preface the commit with anything, except for the conventional commit keywords: fix, feat, build, chore, ci, docs, style, refactor, perf, test.'; const getCommitConvention = (fullGitMojiSpec: boolean) => config.OCO_EMOJI ? fullGitMojiSpec ? FULL_GITMOJI_SPEC : GITMOJI_HELP : CONVENTIONAL_COMMIT_KEYWORDS; const getDescriptionInstruction = () => config.OCO_DESCRIPTION ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' : "Don't add any descriptions to the commit, only commit message."; const getOneLineCommitInstruction = () => config.OCO_ONE_LINE_COMMIT ? 'Craft a concise, single sentence, commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in one single message.' : ''; const getScopeInstruction = () => config.OCO_OMIT_SCOPE ? 'Do not include a scope in the commit message format. Use the format: : ' : ''; /** * Get the context of the user input * @param extraArgs - The arguments passed to the command line * @example * $ oco -- This is a context used to generate the commit message * @returns - The context of the user input */ const userInputCodeContext = (context: string) => { if (context !== '' && context !== ' ') { return `Additional context provided by the user: ${context}\nConsider this context when generating the commit message, incorporating relevant information when appropriate.`; } return ''; }; const INIT_MAIN_PROMPT = ( language: string, fullGitMojiSpec: boolean, context: string ): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({ role: 'system', content: (() => { const commitConvention = fullGitMojiSpec ? 'GitMoji specification' : 'Conventional Commit Convention'; const missionStatement = `${IDENTITY} Your mission is to create clean and comprehensive commit messages as per the ${commitConvention} and explain WHAT were the changes and mainly WHY the changes were done.`; const diffInstruction = "I'll send you an output of 'git diff --staged' command, and you are to convert it into a commit message."; const conventionGuidelines = getCommitConvention(fullGitMojiSpec); const descriptionGuideline = getDescriptionInstruction(); const oneLineCommitGuideline = getOneLineCommitInstruction(); const scopeInstruction = getScopeInstruction(); const generalGuidelines = `Use the present tense. Lines must not be longer than 74 characters. Use ${language} for the commit message.`; const userInputContext = userInputCodeContext(context); return `${missionStatement}\n${diffInstruction}\n${conventionGuidelines}\n${descriptionGuideline}\n${oneLineCommitGuideline}\n${scopeInstruction}\n${generalGuidelines}\n${userInputContext}`; })() }); export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessageParam = { role: 'user', content: `diff --git a/src/server.ts b/src/server.ts index ad4db42..f3b18a9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -10,7 +10,7 @@ import { initWinstonLogger(); const app = express(); -const port = 7799; +const PORT = 7799; app.use(express.json()); @@ -34,6 +34,6 @@ app.use((_, res, next) => { // ROUTES app.use(PROTECTED_ROUTER_URL, protectedRouter); -app.listen(port, () => { - console.log(\`Server listening on port \${port}\`); +app.listen(process.env.PORT || PORT, () => { + console.log(\`Server listening on port \${PORT}\`); });` }; const COMMIT_TYPES = { fix: '๐Ÿ›', feat: 'โœจ' } as const; const generateCommitString = ( type: keyof typeof COMMIT_TYPES, message: string ): string => { const cleanMessage = removeConventionalCommitWord(message); return config.OCO_EMOJI ? `${COMMIT_TYPES[type]} ${cleanMessage}` : message; }; const getConsistencyContent = (translation: ConsistencyPrompt) => { const fixMessage = config.OCO_OMIT_SCOPE && translation.commitFixOmitScope ? translation.commitFixOmitScope : translation.commitFix; const featMessage = config.OCO_OMIT_SCOPE && translation.commitFeatOmitScope ? translation.commitFeatOmitScope : translation.commitFeat; const fix = generateCommitString('fix', fixMessage); const feat = config.OCO_ONE_LINE_COMMIT ? '' : generateCommitString('feat', featMessage); const description = config.OCO_DESCRIPTION ? translation.commitDescription : ''; return [fix, feat, description].filter(Boolean).join('\n'); }; const INIT_CONSISTENCY_PROMPT = ( translation: ConsistencyPrompt ): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({ role: 'assistant', content: getConsistencyContent(translation) }); export const getMainCommitPrompt = async ( fullGitMojiSpec: boolean, context: string ): Promise> => { switch (config.OCO_PROMPT_MODULE) { case '@commitlint': if (!(await utils.commitlintLLMConfigExists())) { note( `OCO_PROMPT_MODULE is @commitlint but you haven't generated consistency for this project yet.` ); await configureCommitlintIntegration(); } // Replace example prompt with a prompt that's generated by OpenAI for the commitlint config. const commitLintConfig = await utils.getCommitlintLLMConfig(); return [ commitlintPrompts.INIT_MAIN_PROMPT( translation.localLanguage, commitLintConfig.prompts ), INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT( commitLintConfig.consistency[ translation.localLanguage ] as ConsistencyPrompt ) ]; default: return [ INIT_MAIN_PROMPT(translation.localLanguage, fullGitMojiSpec, context), INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT(translation) ]; } };