cast-code 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/services/llm.service.js +1 -1
- package/dist/common/services/llm.service.js.map +1 -1
- package/dist/common/services/multi-llm.service.js +2 -2
- package/dist/common/services/multi-llm.service.js.map +1 -1
- package/dist/modules/git/services/commit-generator.service.js +21 -5
- package/dist/modules/git/services/commit-generator.service.js.map +1 -1
- package/dist/modules/git/services/pr-generator.service.js +70 -69
- package/dist/modules/git/services/pr-generator.service.js.map +1 -1
- package/package.json +1 -1
- package/dist/modules/agents/definitions/architect.md +0 -35
- package/dist/modules/agents/definitions/backend.md +0 -43
- package/dist/modules/agents/definitions/coder.md +0 -34
- package/dist/modules/agents/definitions/devops.md +0 -42
- package/dist/modules/agents/definitions/frontend.md +0 -46
- package/dist/modules/agents/definitions/reviewer.md +0 -35
- package/dist/modules/agents/definitions/tester.md +0 -41
- package/dist/modules/skills/definitions/general/file-operations.md +0 -60
- package/dist/modules/skills/definitions/general/git-operations.md +0 -59
- package/dist/modules/skills/definitions/general/planning.md +0 -86
- package/dist/modules/skills/definitions/general/search.md +0 -59
- package/dist/modules/skills/definitions/specialized/api-design.md +0 -85
- package/dist/modules/skills/definitions/specialized/database-operations.md +0 -78
- package/dist/modules/skills/definitions/specialized/frontend-bootstrap.md +0 -71
- package/dist/modules/skills/definitions/specialized/react-patterns.md +0 -77
- package/dist/modules/skills/definitions/specialized/testing-strategies.md +0 -79
|
@@ -34,7 +34,7 @@ let LlmService = class LlmService {
|
|
|
34
34
|
return new _openai.ChatOpenAI({
|
|
35
35
|
modelName: this.configService.getModel(),
|
|
36
36
|
temperature: this.configService.getTemperature(),
|
|
37
|
-
|
|
37
|
+
apiKey: this.configService.getApiKey(),
|
|
38
38
|
streaming
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/common/services/llm.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { ChatOllama } from '@langchain/ollama';\nimport { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { ConfigService } from './config.service';\n\n@Injectable()\nexport class LlmService {\n constructor(private readonly configService: ConfigService) {}\n\n createModel(streaming = false): BaseChatModel {\n const provider = this.configService.getProvider();\n\n if (provider === 'ollama') {\n return new ChatOllama({\n model: this.configService.getModel(),\n temperature: this.configService.getTemperature(),\n baseUrl: this.configService.getOllamaBaseUrl(),\n });\n }\n\n return new ChatOpenAI({\n modelName: this.configService.getModel(),\n temperature: this.configService.getTemperature(),\n
|
|
1
|
+
{"version":3,"sources":["../../../src/common/services/llm.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { ChatOllama } from '@langchain/ollama';\nimport { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { ConfigService } from './config.service';\n\n@Injectable()\nexport class LlmService {\n constructor(private readonly configService: ConfigService) {}\n\n createModel(streaming = false): BaseChatModel {\n const provider = this.configService.getProvider();\n\n if (provider === 'ollama') {\n return new ChatOllama({\n model: this.configService.getModel(),\n temperature: this.configService.getTemperature(),\n baseUrl: this.configService.getOllamaBaseUrl(),\n });\n }\n\n return new ChatOpenAI({\n modelName: this.configService.getModel(),\n temperature: this.configService.getTemperature(),\n apiKey: this.configService.getApiKey(),\n streaming,\n });\n }\n\n createStreamingModel(): BaseChatModel {\n return this.createModel(true);\n }\n}\n"],"names":["LlmService","createModel","streaming","provider","configService","getProvider","ChatOllama","model","getModel","temperature","getTemperature","baseUrl","getOllamaBaseUrl","ChatOpenAI","modelName","apiKey","getApiKey","createStreamingModel"],"mappings":";;;;+BAOaA;;;eAAAA;;;wBAPc;wBACA;wBACA;+BAEG;;;;;;;;;;AAGvB,IAAA,AAAMA,aAAN,MAAMA;IAGXC,YAAYC,YAAY,KAAK,EAAiB;QAC5C,MAAMC,WAAW,IAAI,CAACC,aAAa,CAACC,WAAW;QAE/C,IAAIF,aAAa,UAAU;YACzB,OAAO,IAAIG,kBAAU,CAAC;gBACpBC,OAAO,IAAI,CAACH,aAAa,CAACI,QAAQ;gBAClCC,aAAa,IAAI,CAACL,aAAa,CAACM,cAAc;gBAC9CC,SAAS,IAAI,CAACP,aAAa,CAACQ,gBAAgB;YAC9C;QACF;QAEA,OAAO,IAAIC,kBAAU,CAAC;YACpBC,WAAW,IAAI,CAACV,aAAa,CAACI,QAAQ;YACtCC,aAAa,IAAI,CAACL,aAAa,CAACM,cAAc;YAC9CK,QAAQ,IAAI,CAACX,aAAa,CAACY,SAAS;YACpCd;QACF;IACF;IAEAe,uBAAsC;QACpC,OAAO,IAAI,CAAChB,WAAW,CAAC;IAC1B;IAvBA,YAAY,AAAiBG,aAA4B,CAAE;aAA9BA,gBAAAA;IAA+B;AAwB9D"}
|
|
@@ -49,7 +49,7 @@ let MultiLlmService = class MultiLlmService {
|
|
|
49
49
|
...temperature !== undefined ? {
|
|
50
50
|
temperature
|
|
51
51
|
} : {},
|
|
52
|
-
|
|
52
|
+
apiKey: config.apiKey,
|
|
53
53
|
configuration: {
|
|
54
54
|
baseURL: config.baseUrl
|
|
55
55
|
},
|
|
@@ -81,7 +81,7 @@ let MultiLlmService = class MultiLlmService {
|
|
|
81
81
|
...temperature !== undefined ? {
|
|
82
82
|
temperature
|
|
83
83
|
} : {},
|
|
84
|
-
|
|
84
|
+
apiKey: config.apiKey,
|
|
85
85
|
configuration: {
|
|
86
86
|
baseURL: config.baseUrl || 'https://api.moonshot.cn/v1'
|
|
87
87
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/common/services/multi-llm.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { ChatOllama } from '@langchain/ollama';\nimport { ChatAnthropic } from '@langchain/anthropic';\nimport { ChatGoogleGenerativeAI } from '@langchain/google-genai';\nimport { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { ConfigManagerService } from '../../modules/config/services/config-manager.service';\nimport {\n ModelPurpose,\n ProviderType,\n} from '../../modules/config/types/config.types';\n\n@Injectable()\nexport class MultiLlmService {\n constructor(private readonly configManager: ConfigManagerService) {}\n\n createModel(purpose: ModelPurpose = 'default', streaming = false): BaseChatModel {\n const modelConfig = this.configManager.getModelConfig(purpose);\n\n if (!modelConfig) {\n throw new Error(\n `No model configured for purpose \"${purpose}\". ` +\n 'Run \"cast config init\" to configure.'\n );\n }\n\n const { provider, model, temperature } = modelConfig;\n const providerConfig = this.configManager.getProviderConfig(provider);\n\n if (!providerConfig) {\n throw new Error(\n `Provider \"${provider}\" is not configured. ` +\n 'Run \"cast config init\" to configure.'\n );\n }\n\n return this.createModelForProvider(\n provider,\n providerConfig,\n model,\n temperature,\n streaming\n );\n }\n\n createStreamingModel(purpose: ModelPurpose = 'default'): BaseChatModel {\n return this.createModel(purpose, true);\n }\n\n private createModelForProvider(\n provider: ProviderType,\n config: { apiKey?: string; baseUrl?: string },\n model: string,\n temperature: number | undefined,\n streaming: boolean\n ): BaseChatModel {\n switch (provider) {\n case 'openai':\n case 'deepseek':\n case 'openrouter':\n return new ChatOpenAI({\n modelName: model,\n ...(temperature !== undefined ? { temperature } : {}),\n
|
|
1
|
+
{"version":3,"sources":["../../../src/common/services/multi-llm.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { ChatOllama } from '@langchain/ollama';\nimport { ChatAnthropic } from '@langchain/anthropic';\nimport { ChatGoogleGenerativeAI } from '@langchain/google-genai';\nimport { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { ConfigManagerService } from '../../modules/config/services/config-manager.service';\nimport {\n ModelPurpose,\n ProviderType,\n} from '../../modules/config/types/config.types';\n\n@Injectable()\nexport class MultiLlmService {\n constructor(private readonly configManager: ConfigManagerService) {}\n\n createModel(purpose: ModelPurpose = 'default', streaming = false): BaseChatModel {\n const modelConfig = this.configManager.getModelConfig(purpose);\n\n if (!modelConfig) {\n throw new Error(\n `No model configured for purpose \"${purpose}\". ` +\n 'Run \"cast config init\" to configure.'\n );\n }\n\n const { provider, model, temperature } = modelConfig;\n const providerConfig = this.configManager.getProviderConfig(provider);\n\n if (!providerConfig) {\n throw new Error(\n `Provider \"${provider}\" is not configured. ` +\n 'Run \"cast config init\" to configure.'\n );\n }\n\n return this.createModelForProvider(\n provider,\n providerConfig,\n model,\n temperature,\n streaming\n );\n }\n\n createStreamingModel(purpose: ModelPurpose = 'default'): BaseChatModel {\n return this.createModel(purpose, true);\n }\n\n private createModelForProvider(\n provider: ProviderType,\n config: { apiKey?: string; baseUrl?: string },\n model: string,\n temperature: number | undefined,\n streaming: boolean\n ): BaseChatModel {\n switch (provider) {\n case 'openai':\n case 'deepseek':\n case 'openrouter':\n return new ChatOpenAI({\n modelName: model,\n ...(temperature !== undefined ? { temperature } : {}),\n apiKey: config.apiKey,\n configuration: {\n baseURL: config.baseUrl,\n },\n streaming,\n streamUsage: streaming,\n });\n\n case 'anthropic':\n return new ChatAnthropic({\n modelName: model,\n ...(temperature !== undefined ? { temperature } : {}),\n anthropicApiKey: config.apiKey,\n anthropicApiUrl: config.baseUrl,\n streaming,\n });\n\n case 'gemini':\n return new ChatGoogleGenerativeAI({\n modelName: model,\n ...(temperature !== undefined ? { temperature } : {}),\n apiKey: config.apiKey,\n streaming,\n });\n\n case 'kimi':\n return new ChatOpenAI({\n modelName: model,\n ...(temperature !== undefined ? { temperature } : {}),\n apiKey: config.apiKey,\n configuration: {\n baseURL: config.baseUrl || 'https://api.moonshot.cn/v1',\n },\n streaming,\n streamUsage: streaming,\n });\n\n case 'ollama':\n return new ChatOllama({\n model,\n ...(temperature !== undefined ? { temperature } : {}),\n baseUrl: config.baseUrl || 'http://localhost:11434',\n });\n\n default:\n throw new Error(`Unsupported provider: ${provider}`);\n }\n }\n}\n"],"names":["MultiLlmService","createModel","purpose","streaming","modelConfig","configManager","getModelConfig","Error","provider","model","temperature","providerConfig","getProviderConfig","createModelForProvider","createStreamingModel","config","ChatOpenAI","modelName","undefined","apiKey","configuration","baseURL","baseUrl","streamUsage","ChatAnthropic","anthropicApiKey","anthropicApiUrl","ChatGoogleGenerativeAI","ChatOllama"],"mappings":";;;;+BAaaA;;;eAAAA;;;wBAbc;wBACA;wBACA;2BACG;6BACS;sCAEF;;;;;;;;;;AAO9B,IAAA,AAAMA,kBAAN,MAAMA;IAGXC,YAAYC,UAAwB,SAAS,EAAEC,YAAY,KAAK,EAAiB;QAC/E,MAAMC,cAAc,IAAI,CAACC,aAAa,CAACC,cAAc,CAACJ;QAEtD,IAAI,CAACE,aAAa;YAChB,MAAM,IAAIG,MACR,CAAC,iCAAiC,EAAEL,QAAQ,GAAG,CAAC,GAC9C;QAEN;QAEA,MAAM,EAAEM,QAAQ,EAAEC,KAAK,EAAEC,WAAW,EAAE,GAAGN;QACzC,MAAMO,iBAAiB,IAAI,CAACN,aAAa,CAACO,iBAAiB,CAACJ;QAE5D,IAAI,CAACG,gBAAgB;YACnB,MAAM,IAAIJ,MACR,CAAC,UAAU,EAAEC,SAAS,qBAAqB,CAAC,GAC1C;QAEN;QAEA,OAAO,IAAI,CAACK,sBAAsB,CAChCL,UACAG,gBACAF,OACAC,aACAP;IAEJ;IAEAW,qBAAqBZ,UAAwB,SAAS,EAAiB;QACrE,OAAO,IAAI,CAACD,WAAW,CAACC,SAAS;IACnC;IAEQW,uBACNL,QAAsB,EACtBO,MAA6C,EAC7CN,KAAa,EACbC,WAA+B,EAC/BP,SAAkB,EACH;QACf,OAAQK;YACN,KAAK;YACL,KAAK;YACL,KAAK;gBACH,OAAO,IAAIQ,kBAAU,CAAC;oBACpBC,WAAWR;oBACX,GAAIC,gBAAgBQ,YAAY;wBAAER;oBAAY,IAAI,CAAC,CAAC;oBACpDS,QAAQJ,OAAOI,MAAM;oBACrBC,eAAe;wBACbC,SAASN,OAAOO,OAAO;oBACzB;oBACAnB;oBACAoB,aAAapB;gBACf;YAEF,KAAK;gBACH,OAAO,IAAIqB,wBAAa,CAAC;oBACvBP,WAAWR;oBACX,GAAIC,gBAAgBQ,YAAY;wBAAER;oBAAY,IAAI,CAAC,CAAC;oBACpDe,iBAAiBV,OAAOI,MAAM;oBAC9BO,iBAAiBX,OAAOO,OAAO;oBAC/BnB;gBACF;YAEF,KAAK;gBACH,OAAO,IAAIwB,mCAAsB,CAAC;oBAChCV,WAAWR;oBACX,GAAIC,gBAAgBQ,YAAY;wBAAER;oBAAY,IAAI,CAAC,CAAC;oBACpDS,QAAQJ,OAAOI,MAAM;oBACrBhB;gBACF;YAEF,KAAK;gBACH,OAAO,IAAIa,kBAAU,CAAC;oBACpBC,WAAWR;oBACX,GAAIC,gBAAgBQ,YAAY;wBAAER;oBAAY,IAAI,CAAC,CAAC;oBACpDS,QAAQJ,OAAOI,MAAM;oBACrBC,eAAe;wBACbC,SAASN,OAAOO,OAAO,IAAI;oBAC7B;oBACAnB;oBACAoB,aAAapB;gBACf;YAEF,KAAK;gBACH,OAAO,IAAIyB,kBAAU,CAAC;oBACpBnB;oBACA,GAAIC,gBAAgBQ,YAAY;wBAAER;oBAAY,IAAI,CAAC,CAAC;oBACpDY,SAASP,OAAOO,OAAO,IAAI;gBAC7B;YAEF;gBACE,MAAM,IAAIf,MAAM,CAAC,sBAAsB,EAAEC,UAAU;QACvD;IACF;IAhGA,YAAY,AAAiBH,aAAmC,CAAE;aAArCA,gBAAAA;IAAsC;AAiGrE"}
|
|
@@ -38,11 +38,17 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
38
38
|
cwd,
|
|
39
39
|
encoding: 'utf-8'
|
|
40
40
|
});
|
|
41
|
+
const untrackedRaw = (0, _child_process.execSync)('git ls-files --others --exclude-standard', {
|
|
42
|
+
cwd,
|
|
43
|
+
encoding: 'utf-8'
|
|
44
|
+
});
|
|
45
|
+
const untrackedFiles = untrackedRaw.trim() ? untrackedRaw.trim().split('\n').filter((f)=>f.trim()) : [];
|
|
41
46
|
return {
|
|
42
47
|
staged,
|
|
43
48
|
unstaged,
|
|
44
49
|
stagedFiles: this.extractFiles(staged),
|
|
45
50
|
unstagedFiles: this.extractFiles(unstaged),
|
|
51
|
+
untrackedFiles,
|
|
46
52
|
stats
|
|
47
53
|
};
|
|
48
54
|
} catch {
|
|
@@ -66,7 +72,8 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
66
72
|
const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());
|
|
67
73
|
const allFiles = [
|
|
68
74
|
...diffInfo.stagedFiles,
|
|
69
|
-
...diffInfo.unstagedFiles
|
|
75
|
+
...diffInfo.unstagedFiles,
|
|
76
|
+
...diffInfo.untrackedFiles
|
|
70
77
|
];
|
|
71
78
|
const scope = this.monorepoDetector.determineScope(allFiles, monorepoInfo);
|
|
72
79
|
const llm = this.multiLlmService.createModel('cheap');
|
|
@@ -84,7 +91,8 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
84
91
|
const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());
|
|
85
92
|
const allFiles = [
|
|
86
93
|
...diffInfo.stagedFiles,
|
|
87
|
-
...diffInfo.unstagedFiles
|
|
94
|
+
...diffInfo.unstagedFiles,
|
|
95
|
+
...diffInfo.untrackedFiles
|
|
88
96
|
];
|
|
89
97
|
const llm = this.multiLlmService.createModel('cheap');
|
|
90
98
|
const splitPrompt = this.buildSplitPrompt(diffInfo, allFiles);
|
|
@@ -237,7 +245,9 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
237
245
|
return String(content);
|
|
238
246
|
}
|
|
239
247
|
cleanCommitMessage(message) {
|
|
240
|
-
|
|
248
|
+
const lines = message.trim().split('\n').map((l)=>l.trim()).filter((l)=>l.length > 0);
|
|
249
|
+
const firstCommitLine = lines.find((l)=>l.includes(':')) || lines[0] || '';
|
|
250
|
+
return firstCommitLine.replace(/^["']|["']$/g, '').trim();
|
|
241
251
|
}
|
|
242
252
|
buildCommitPrompt(diffInfo, scope) {
|
|
243
253
|
const scopeHint = scope ? `\n\nThis is a monorepo. The changes are primarily in the "${scope}" module.` : '';
|
|
@@ -248,11 +258,12 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
248
258
|
if (diffInfo.unstaged) {
|
|
249
259
|
fullDiff += `=== Unstaged changes ===\n${diffInfo.unstaged}\n\n`;
|
|
250
260
|
}
|
|
261
|
+
const untrackedHint = diffInfo.untrackedFiles.length > 0 ? `\nNew untracked files (will also be committed): ${diffInfo.untrackedFiles.join(', ')}\n` : '';
|
|
251
262
|
const maxLength = 10000;
|
|
252
263
|
if (fullDiff.length > maxLength) {
|
|
253
264
|
fullDiff = fullDiff.slice(0, maxLength) + '\n\n... (truncated)';
|
|
254
265
|
}
|
|
255
|
-
return `Generate a conventional commit message for the following changes.${scopeHint}\n\nFiles changed:\n${diffInfo.stats}\n\n${fullDiff}`;
|
|
266
|
+
return `Generate a single conventional commit message for ALL of the following changes.${scopeHint}\n\nFiles changed:\n${diffInfo.stats}${untrackedHint}\n\n${fullDiff}`;
|
|
256
267
|
}
|
|
257
268
|
buildSplitPrompt(diffInfo, files) {
|
|
258
269
|
let fullDiff = '';
|
|
@@ -262,11 +273,16 @@ let CommitGeneratorService = class CommitGeneratorService {
|
|
|
262
273
|
if (diffInfo.unstaged) {
|
|
263
274
|
fullDiff += `=== Unstaged changes ===\n${diffInfo.unstaged}\n\n`;
|
|
264
275
|
}
|
|
276
|
+
const allFiles = [
|
|
277
|
+
...files,
|
|
278
|
+
...diffInfo.untrackedFiles.filter((f)=>!files.includes(f))
|
|
279
|
+
];
|
|
280
|
+
const untrackedHint = diffInfo.untrackedFiles.length > 0 ? `\nNew untracked files (must be included in commits): ${diffInfo.untrackedFiles.join(', ')}\n` : '';
|
|
265
281
|
const maxLength = 8000;
|
|
266
282
|
if (fullDiff.length > maxLength) {
|
|
267
283
|
fullDiff = fullDiff.slice(0, maxLength) + '\n\n... (truncated)';
|
|
268
284
|
}
|
|
269
|
-
return `Analyze all the files below and group them into logical commits.\n\nFiles: ${
|
|
285
|
+
return `Analyze all the files below and group them into logical commits. Include ALL files in the result.\n\nFiles: ${allFiles.join(', ')}${untrackedHint}\n\nStats:\n${diffInfo.stats}\n\n${fullDiff}`;
|
|
270
286
|
}
|
|
271
287
|
async generateMessageForGroup(group, diffInfo) {
|
|
272
288
|
const llm = this.multiLlmService.createModel('cheap');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/git/services/commit-generator.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { execSync } from 'child_process';\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { MultiLlmService } from '../../../common/services/multi-llm.service';\nimport { MonorepoDetectorService } from './monorepo-detector.service';\nimport { GitDiffInfo, SplitCommit, CommitGroup, MonorepoInfo } from '../types/git.types';\n\n@Injectable()\nexport class CommitGeneratorService {\n constructor(\n private readonly multiLlmService: MultiLlmService,\n private readonly monorepoDetector: MonorepoDetectorService,\n ) {}\n\n getDiffInfo(): GitDiffInfo | null {\n try {\n const cwd = process.cwd();\n const staged = execSync('git diff --cached', { cwd, encoding: 'utf-8' });\n const unstaged = execSync('git diff', { cwd, encoding: 'utf-8' });\n const stats = execSync('git diff --stat', { cwd, encoding: 'utf-8' });\n\n return {\n staged,\n unstaged,\n stagedFiles: this.extractFiles(staged),\n unstagedFiles: this.extractFiles(unstaged),\n stats,\n };\n } catch {\n return null;\n }\n }\n\n hasChanges(): boolean {\n try {\n const output = execSync('git status --porcelain', { cwd: process.cwd(), encoding: 'utf-8' });\n return output.trim().length > 0;\n } catch {\n return false;\n }\n }\n\n async generateCommitMessage(): Promise<string | null> {\n const diffInfo = this.getDiffInfo();\n if (!diffInfo) return null;\n\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const allFiles = [...diffInfo.stagedFiles, ...diffInfo.unstagedFiles];\n const scope = this.monorepoDetector.determineScope(allFiles, monorepoInfo);\n\n const llm = this.multiLlmService.createModel('cheap');\n const prompt = this.buildCommitPrompt(diffInfo, scope);\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const message = this.extractContent(response.content);\n return this.cleanCommitMessage(message);\n }\n\n async splitCommits(): Promise<SplitCommit[] | null> {\n const diffInfo = this.getDiffInfo();\n if (!diffInfo) return null;\n\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const allFiles = [...diffInfo.stagedFiles, ...diffInfo.unstagedFiles];\n\n const llm = this.multiLlmService.createModel('cheap');\n const splitPrompt = this.buildSplitPrompt(diffInfo, allFiles);\n\n const splitResponse = await llm.invoke([\n new SystemMessage(this.getSplitSystemPrompt()),\n new HumanMessage(splitPrompt),\n ]);\n\n const splitContent = this.extractContent(splitResponse.content);\n const commitGroups = this.parseCommitGroups(splitContent);\n\n if (!commitGroups?.length) return null;\n\n const validGroups = commitGroups.filter((group) => Array.isArray(group.files) && group.files.length > 0);\n if (validGroups.length === 0) return null;\n\n for (const group of validGroups) {\n if (!group.scope) {\n group.scope = this.monorepoDetector.determineScope(group.files, monorepoInfo);\n }\n }\n\n const splitCommits: SplitCommit[] = [];\n for (const group of validGroups) {\n const message = await this.generateMessageForGroup(group, diffInfo);\n splitCommits.push({ ...group, message });\n }\n\n return splitCommits;\n }\n\n executeCommit(message: string, autoStage = true): boolean {\n try {\n const cwd = process.cwd();\n if (autoStage) {\n execSync('git add -A', { cwd });\n }\n execSync('git commit -F -', { cwd, input: `${message}\\n`, encoding: 'utf-8' });\n return true;\n } catch {\n return false;\n }\n }\n\n executePush(): { success: boolean; error?: string } {\n try {\n const cwd = process.cwd();\n const branch = execSync('git branch --show-current', { cwd, encoding: 'utf-8' }).trim();\n execSync(`git push origin ${branch}`, { cwd, encoding: 'utf-8' });\n return { success: true };\n } catch (error: any) {\n const message = error.message || 'Push failed';\n if (message.includes('rejected') || message.includes('diverged')) {\n return { success: false, error: 'Push rejected. Run \"git pull --rebase\" first.' };\n }\n return { success: false, error: message };\n }\n }\n\n executeSplitCommits(commits: SplitCommit[]): { success: boolean; committed: number; error?: string; originalHead?: string } {\n const cwd = process.cwd();\n let committedCount = 0;\n let originalHead: string | undefined;\n\n try {\n originalHead = execSync('git rev-parse HEAD', { cwd, encoding: 'utf-8' }).trim();\n\n for (const commit of commits) {\n execSync('git reset', { cwd });\n\n for (const file of commit.files) {\n try {\n execSync(`git add \"${file}\"`, { cwd });\n } catch {}\n }\n\n const staged = execSync('git diff --cached --name-only', { cwd, encoding: 'utf-8' });\n if (!staged.trim()) continue;\n\n execSync('git commit -F -', { cwd, input: `${commit.message}\\n`, encoding: 'utf-8' });\n committedCount++;\n }\n\n return { success: true, committed: committedCount };\n } catch (error: any) {\n return {\n success: false,\n committed: committedCount,\n error: error.message || 'Failed to execute commits',\n originalHead\n };\n }\n }\n\n async refineCommitMessage(\n currentMessage: string,\n userSuggestion: string,\n diffInfo: GitDiffInfo,\n ): Promise<string> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const prompt = `Current commit message: ${currentMessage}\\n\\nUser suggestion: ${userSuggestion}\\n\\nDiff:\\n${diffInfo.staged.slice(0, 3000)}`;\n\n const response = await llm.invoke([\n new SystemMessage(this.getRefineSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const message = this.extractContent(response.content);\n return this.cleanCommitMessage(message);\n }\n\n private extractFiles(diff: string): string[] {\n const files = new Set<string>();\n const lines = diff.split('\\n');\n\n for (const line of lines) {\n if (line.startsWith('diff --git')) {\n const match = line.match(/diff --git a\\/(.+?) b\\/(.+?)$/);\n if (match) {\n files.add(match[2]);\n }\n }\n }\n\n return Array.from(files);\n }\n\n private extractContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (typeof first === 'object' && first !== null && 'text' in first) {\n return String(first.text);\n }\n }\n return String(content);\n }\n\n private cleanCommitMessage(message: string): string {\n return message\n .trim()\n .replace(/^[\"']|[\"']$/g, '')\n .replace(/\\n/g, ' ')\n .trim();\n }\n\n private buildCommitPrompt(diffInfo: GitDiffInfo, scope?: string): string {\n const scopeHint = scope ? `\\n\\nThis is a monorepo. The changes are primarily in the \"${scope}\" module.` : '';\n\n let fullDiff = '';\n if (diffInfo.staged) {\n fullDiff += `=== Staged changes ===\\n${diffInfo.staged}\\n\\n`;\n }\n if (diffInfo.unstaged) {\n fullDiff += `=== Unstaged changes ===\\n${diffInfo.unstaged}\\n\\n`;\n }\n\n const maxLength = 10000;\n if (fullDiff.length > maxLength) {\n fullDiff = fullDiff.slice(0, maxLength) + '\\n\\n... (truncated)';\n }\n\n return `Generate a conventional commit message for the following changes.${scopeHint}\\n\\nFiles changed:\\n${diffInfo.stats}\\n\\n${fullDiff}`;\n }\n\n private buildSplitPrompt(diffInfo: GitDiffInfo, files: string[]): string {\n let fullDiff = '';\n if (diffInfo.staged) {\n fullDiff += `=== Staged changes ===\\n${diffInfo.staged}\\n\\n`;\n }\n if (diffInfo.unstaged) {\n fullDiff += `=== Unstaged changes ===\\n${diffInfo.unstaged}\\n\\n`;\n }\n\n const maxLength = 8000;\n if (fullDiff.length > maxLength) {\n fullDiff = fullDiff.slice(0, maxLength) + '\\n\\n... (truncated)';\n }\n\n return `Analyze all the files below and group them into logical commits.\\n\\nFiles: ${files.join(', ')}\\n\\nStats:\\n${diffInfo.stats}\\n\\n${fullDiff}`;\n }\n\n private async generateMessageForGroup(group: CommitGroup, diffInfo: GitDiffInfo): Promise<string> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const scopePart = group.scope ? `(${group.scope})` : '';\n\n const prompt = `Generate a conventional commit message (max 72 chars) for:\\n\\nType: ${group.type}${scopePart}\\nFiles: ${group.files.join(', ')}\\nDescription: ${group.description}\\n\\nIMPORTANT: Return ONLY the commit message in format: \"type(scope): description\" or \"type: description\"\\nThe message MUST be in English.`;\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n let message = this.extractContent(response.content);\n message = this.cleanCommitMessage(message);\n\n if (!message.includes(':')) {\n const scope = group.scope ? `(${group.scope})` : '';\n message = `${group.type}${scope}: ${message}`;\n }\n\n return message;\n }\n\n private parseCommitGroups(content: string): CommitGroup[] | null {\n const jsonMatch = content.match(/```json\\s*([\\s\\S]*?)\\s*```/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[1]);\n if (parsed.commits && Array.isArray(parsed.commits)) {\n return parsed.commits;\n }\n } catch {}\n }\n\n try {\n const parsed = JSON.parse(content);\n if (parsed.commits && Array.isArray(parsed.commits)) {\n return parsed.commits;\n }\n } catch {}\n\n return null;\n }\n\n private getCommitSystemPrompt(): string {\n return `You are a Git commit message expert. Generate concise commit messages following Conventional Commits.\n\n**Available types:**\n- feat: new feature\n- fix: bug fix\n- docs: documentation\n- style: formatting (no logic change)\n- refactor: refactoring (no functionality change)\n- perf: performance improvement\n- test: tests\n- build: build/dependencies\n- ci: continuous integration\n- chore: general tasks\n- cleanup: code cleanup\n- remove: code removal\n\n**Format:**\n- With scope: <type>(<scope>): <description>\n- Without scope: <type>: <description>\n\n**Rules:**\n- Maximum 72 characters for the subject line\n- Description in English\n- No period at the end\n- Use imperative mood (\"add\" not \"added\")\n- Be specific but concise\n\nIf this is a monorepo and you can identify the module, include the scope.\n\nReturn ONLY the commit message, nothing else.`;\n }\n\n private getSplitSystemPrompt(): string {\n return `You are a Git commit organization expert. Analyze the changes and divide them into logical commits.\n\n**YOUR TASK:**\nGroup changes into logical commits based on:\n1. **Functional cohesion**: Changes that make sense together\n2. **Change type**: features, fixes, docs, refactorings separated\n3. **Related files**: Files that work together\n\n**RETURN FORMAT (JSON):**\nReturn ONLY valid JSON in this format:\n\\`\\`\\`json\n{\n \"commits\": [\n {\n \"type\": \"feat\",\n \"files\": [\"src/auth.ts\", \"src/models/user.ts\"],\n \"description\": \"add user authentication\"\n },\n {\n \"type\": \"docs\",\n \"files\": [\"README.md\"],\n \"description\": \"update documentation\"\n }\n ]\n}\n\\`\\`\\`\n\n**AVAILABLE TYPES:**\nfeat, fix, docs, style, refactor, perf, test, build, ci, chore\n\n**RULES:**\n- Each commit should have a clear purpose\n- Group functionally related files\n- Separate features from fixes from documentation\n- Maximum 5 files per commit (ideally fewer)\n- If diff is small (<3 files), can be 1 commit only\n- Description must ALWAYS be in English\n- Include ALL files in the result\n\n**IMPORTANT:** Return ONLY the JSON, no additional text.`;\n }\n\n private getRefineSystemPrompt(): string {\n return `You are refining a commit message based on user feedback.\n\n**Instructions:**\n- Keep the message concise (max 72 characters)\n- Follow Conventional Commits format\n- Incorporate the user's suggestion\n- Return ONLY the new commit message, without explanations\n- Message MUST be in English`;\n }\n}\n"],"names":["CommitGeneratorService","getDiffInfo","cwd","process","staged","execSync","encoding","unstaged","stats","stagedFiles","extractFiles","unstagedFiles","hasChanges","output","trim","length","generateCommitMessage","diffInfo","monorepoInfo","monorepoDetector","detectMonorepo","allFiles","scope","determineScope","llm","multiLlmService","createModel","prompt","buildCommitPrompt","response","invoke","SystemMessage","getCommitSystemPrompt","HumanMessage","message","extractContent","content","cleanCommitMessage","splitCommits","splitPrompt","buildSplitPrompt","splitResponse","getSplitSystemPrompt","splitContent","commitGroups","parseCommitGroups","validGroups","filter","group","Array","isArray","files","generateMessageForGroup","push","executeCommit","autoStage","input","executePush","branch","success","error","includes","executeSplitCommits","commits","committedCount","originalHead","commit","file","committed","refineCommitMessage","currentMessage","userSuggestion","slice","getRefineSystemPrompt","diff","Set","lines","split","line","startsWith","match","add","from","first","String","text","replace","scopeHint","fullDiff","maxLength","join","scopePart","type","description","jsonMatch","parsed","JSON","parse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/git/services/commit-generator.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { execSync } from 'child_process';\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { MultiLlmService } from '../../../common/services/multi-llm.service';\nimport { MonorepoDetectorService } from './monorepo-detector.service';\nimport { GitDiffInfo, SplitCommit, CommitGroup, MonorepoInfo } from '../types/git.types';\n\n@Injectable()\nexport class CommitGeneratorService {\n constructor(\n private readonly multiLlmService: MultiLlmService,\n private readonly monorepoDetector: MonorepoDetectorService,\n ) {}\n\n getDiffInfo(): GitDiffInfo | null {\n try {\n const cwd = process.cwd();\n const staged = execSync('git diff --cached', { cwd, encoding: 'utf-8' });\n const unstaged = execSync('git diff', { cwd, encoding: 'utf-8' });\n const stats = execSync('git diff --stat', { cwd, encoding: 'utf-8' });\n const untrackedRaw = execSync('git ls-files --others --exclude-standard', { cwd, encoding: 'utf-8' });\n const untrackedFiles = untrackedRaw.trim() ? untrackedRaw.trim().split('\\n').filter(f => f.trim()) : [];\n\n return {\n staged,\n unstaged,\n stagedFiles: this.extractFiles(staged),\n unstagedFiles: this.extractFiles(unstaged),\n untrackedFiles,\n stats,\n };\n } catch {\n return null;\n }\n }\n\n hasChanges(): boolean {\n try {\n const output = execSync('git status --porcelain', { cwd: process.cwd(), encoding: 'utf-8' });\n return output.trim().length > 0;\n } catch {\n return false;\n }\n }\n\n async generateCommitMessage(): Promise<string | null> {\n const diffInfo = this.getDiffInfo();\n if (!diffInfo) return null;\n\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const allFiles = [...diffInfo.stagedFiles, ...diffInfo.unstagedFiles, ...diffInfo.untrackedFiles];\n const scope = this.monorepoDetector.determineScope(allFiles, monorepoInfo);\n\n const llm = this.multiLlmService.createModel('cheap');\n const prompt = this.buildCommitPrompt(diffInfo, scope);\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const message = this.extractContent(response.content);\n return this.cleanCommitMessage(message);\n }\n\n async splitCommits(): Promise<SplitCommit[] | null> {\n const diffInfo = this.getDiffInfo();\n if (!diffInfo) return null;\n\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const allFiles = [...diffInfo.stagedFiles, ...diffInfo.unstagedFiles, ...diffInfo.untrackedFiles];\n\n const llm = this.multiLlmService.createModel('cheap');\n const splitPrompt = this.buildSplitPrompt(diffInfo, allFiles);\n\n const splitResponse = await llm.invoke([\n new SystemMessage(this.getSplitSystemPrompt()),\n new HumanMessage(splitPrompt),\n ]);\n\n const splitContent = this.extractContent(splitResponse.content);\n const commitGroups = this.parseCommitGroups(splitContent);\n\n if (!commitGroups?.length) return null;\n\n const validGroups = commitGroups.filter((group) => Array.isArray(group.files) && group.files.length > 0);\n if (validGroups.length === 0) return null;\n\n for (const group of validGroups) {\n if (!group.scope) {\n group.scope = this.monorepoDetector.determineScope(group.files, monorepoInfo);\n }\n }\n\n const splitCommits: SplitCommit[] = [];\n for (const group of validGroups) {\n const message = await this.generateMessageForGroup(group, diffInfo);\n splitCommits.push({ ...group, message });\n }\n\n return splitCommits;\n }\n\n executeCommit(message: string, autoStage = true): boolean {\n try {\n const cwd = process.cwd();\n if (autoStage) {\n execSync('git add -A', { cwd });\n }\n execSync('git commit -F -', { cwd, input: `${message}\\n`, encoding: 'utf-8' });\n return true;\n } catch {\n return false;\n }\n }\n\n executePush(): { success: boolean; error?: string } {\n try {\n const cwd = process.cwd();\n const branch = execSync('git branch --show-current', { cwd, encoding: 'utf-8' }).trim();\n execSync(`git push origin ${branch}`, { cwd, encoding: 'utf-8' });\n return { success: true };\n } catch (error: any) {\n const message = error.message || 'Push failed';\n if (message.includes('rejected') || message.includes('diverged')) {\n return { success: false, error: 'Push rejected. Run \"git pull --rebase\" first.' };\n }\n return { success: false, error: message };\n }\n }\n\n executeSplitCommits(commits: SplitCommit[]): { success: boolean; committed: number; error?: string; originalHead?: string } {\n const cwd = process.cwd();\n let committedCount = 0;\n let originalHead: string | undefined;\n\n try {\n originalHead = execSync('git rev-parse HEAD', { cwd, encoding: 'utf-8' }).trim();\n\n for (const commit of commits) {\n execSync('git reset', { cwd });\n\n for (const file of commit.files) {\n try {\n execSync(`git add \"${file}\"`, { cwd });\n } catch {}\n }\n\n const staged = execSync('git diff --cached --name-only', { cwd, encoding: 'utf-8' });\n if (!staged.trim()) continue;\n\n execSync('git commit -F -', { cwd, input: `${commit.message}\\n`, encoding: 'utf-8' });\n committedCount++;\n }\n\n return { success: true, committed: committedCount };\n } catch (error: any) {\n return {\n success: false,\n committed: committedCount,\n error: error.message || 'Failed to execute commits',\n originalHead\n };\n }\n }\n\n async refineCommitMessage(\n currentMessage: string,\n userSuggestion: string,\n diffInfo: GitDiffInfo,\n ): Promise<string> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const prompt = `Current commit message: ${currentMessage}\\n\\nUser suggestion: ${userSuggestion}\\n\\nDiff:\\n${diffInfo.staged.slice(0, 3000)}`;\n\n const response = await llm.invoke([\n new SystemMessage(this.getRefineSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const message = this.extractContent(response.content);\n return this.cleanCommitMessage(message);\n }\n\n private extractFiles(diff: string): string[] {\n const files = new Set<string>();\n const lines = diff.split('\\n');\n\n for (const line of lines) {\n if (line.startsWith('diff --git')) {\n const match = line.match(/diff --git a\\/(.+?) b\\/(.+?)$/);\n if (match) {\n files.add(match[2]);\n }\n }\n }\n\n return Array.from(files);\n }\n\n private extractContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (typeof first === 'object' && first !== null && 'text' in first) {\n return String(first.text);\n }\n }\n return String(content);\n }\n\n private cleanCommitMessage(message: string): string {\n const lines = message.trim().split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n const firstCommitLine = lines.find(l => l.includes(':')) || lines[0] || '';\n return firstCommitLine.replace(/^[\"']|[\"']$/g, '').trim();\n }\n\n private buildCommitPrompt(diffInfo: GitDiffInfo, scope?: string): string {\n const scopeHint = scope ? `\\n\\nThis is a monorepo. The changes are primarily in the \"${scope}\" module.` : '';\n\n let fullDiff = '';\n if (diffInfo.staged) {\n fullDiff += `=== Staged changes ===\\n${diffInfo.staged}\\n\\n`;\n }\n if (diffInfo.unstaged) {\n fullDiff += `=== Unstaged changes ===\\n${diffInfo.unstaged}\\n\\n`;\n }\n\n const untrackedHint = diffInfo.untrackedFiles.length > 0\n ? `\\nNew untracked files (will also be committed): ${diffInfo.untrackedFiles.join(', ')}\\n`\n : '';\n\n const maxLength = 10000;\n if (fullDiff.length > maxLength) {\n fullDiff = fullDiff.slice(0, maxLength) + '\\n\\n... (truncated)';\n }\n\n return `Generate a single conventional commit message for ALL of the following changes.${scopeHint}\\n\\nFiles changed:\\n${diffInfo.stats}${untrackedHint}\\n\\n${fullDiff}`;\n }\n\n private buildSplitPrompt(diffInfo: GitDiffInfo, files: string[]): string {\n let fullDiff = '';\n if (diffInfo.staged) {\n fullDiff += `=== Staged changes ===\\n${diffInfo.staged}\\n\\n`;\n }\n if (diffInfo.unstaged) {\n fullDiff += `=== Unstaged changes ===\\n${diffInfo.unstaged}\\n\\n`;\n }\n\n const allFiles = [...files, ...diffInfo.untrackedFiles.filter(f => !files.includes(f))];\n const untrackedHint = diffInfo.untrackedFiles.length > 0\n ? `\\nNew untracked files (must be included in commits): ${diffInfo.untrackedFiles.join(', ')}\\n`\n : '';\n\n const maxLength = 8000;\n if (fullDiff.length > maxLength) {\n fullDiff = fullDiff.slice(0, maxLength) + '\\n\\n... (truncated)';\n }\n\n return `Analyze all the files below and group them into logical commits. Include ALL files in the result.\\n\\nFiles: ${allFiles.join(', ')}${untrackedHint}\\n\\nStats:\\n${diffInfo.stats}\\n\\n${fullDiff}`;\n }\n\n private async generateMessageForGroup(group: CommitGroup, diffInfo: GitDiffInfo): Promise<string> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const scopePart = group.scope ? `(${group.scope})` : '';\n\n const prompt = `Generate a conventional commit message (max 72 chars) for:\\n\\nType: ${group.type}${scopePart}\\nFiles: ${group.files.join(', ')}\\nDescription: ${group.description}\\n\\nIMPORTANT: Return ONLY the commit message in format: \"type(scope): description\" or \"type: description\"\\nThe message MUST be in English.`;\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n let message = this.extractContent(response.content);\n message = this.cleanCommitMessage(message);\n\n if (!message.includes(':')) {\n const scope = group.scope ? `(${group.scope})` : '';\n message = `${group.type}${scope}: ${message}`;\n }\n\n return message;\n }\n\n private parseCommitGroups(content: string): CommitGroup[] | null {\n const jsonMatch = content.match(/```json\\s*([\\s\\S]*?)\\s*```/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[1]);\n if (parsed.commits && Array.isArray(parsed.commits)) {\n return parsed.commits;\n }\n } catch {}\n }\n\n try {\n const parsed = JSON.parse(content);\n if (parsed.commits && Array.isArray(parsed.commits)) {\n return parsed.commits;\n }\n } catch {}\n\n return null;\n }\n\n private getCommitSystemPrompt(): string {\n return `You are a Git commit message expert. Generate concise commit messages following Conventional Commits.\n\n**Available types:**\n- feat: new feature\n- fix: bug fix\n- docs: documentation\n- style: formatting (no logic change)\n- refactor: refactoring (no functionality change)\n- perf: performance improvement\n- test: tests\n- build: build/dependencies\n- ci: continuous integration\n- chore: general tasks\n- cleanup: code cleanup\n- remove: code removal\n\n**Format:**\n- With scope: <type>(<scope>): <description>\n- Without scope: <type>: <description>\n\n**Rules:**\n- Maximum 72 characters for the subject line\n- Description in English\n- No period at the end\n- Use imperative mood (\"add\" not \"added\")\n- Be specific but concise\n\nIf this is a monorepo and you can identify the module, include the scope.\n\nReturn ONLY the commit message, nothing else.`;\n }\n\n private getSplitSystemPrompt(): string {\n return `You are a Git commit organization expert. Analyze the changes and divide them into logical commits.\n\n**YOUR TASK:**\nGroup changes into logical commits based on:\n1. **Functional cohesion**: Changes that make sense together\n2. **Change type**: features, fixes, docs, refactorings separated\n3. **Related files**: Files that work together\n\n**RETURN FORMAT (JSON):**\nReturn ONLY valid JSON in this format:\n\\`\\`\\`json\n{\n \"commits\": [\n {\n \"type\": \"feat\",\n \"files\": [\"src/auth.ts\", \"src/models/user.ts\"],\n \"description\": \"add user authentication\"\n },\n {\n \"type\": \"docs\",\n \"files\": [\"README.md\"],\n \"description\": \"update documentation\"\n }\n ]\n}\n\\`\\`\\`\n\n**AVAILABLE TYPES:**\nfeat, fix, docs, style, refactor, perf, test, build, ci, chore\n\n**RULES:**\n- Each commit should have a clear purpose\n- Group functionally related files\n- Separate features from fixes from documentation\n- Maximum 5 files per commit (ideally fewer)\n- If diff is small (<3 files), can be 1 commit only\n- Description must ALWAYS be in English\n- Include ALL files in the result\n\n**IMPORTANT:** Return ONLY the JSON, no additional text.`;\n }\n\n private getRefineSystemPrompt(): string {\n return `You are refining a commit message based on user feedback.\n\n**Instructions:**\n- Keep the message concise (max 72 characters)\n- Follow Conventional Commits format\n- Incorporate the user's suggestion\n- Return ONLY the new commit message, without explanations\n- Message MUST be in English`;\n }\n}\n"],"names":["CommitGeneratorService","getDiffInfo","cwd","process","staged","execSync","encoding","unstaged","stats","untrackedRaw","untrackedFiles","trim","split","filter","f","stagedFiles","extractFiles","unstagedFiles","hasChanges","output","length","generateCommitMessage","diffInfo","monorepoInfo","monorepoDetector","detectMonorepo","allFiles","scope","determineScope","llm","multiLlmService","createModel","prompt","buildCommitPrompt","response","invoke","SystemMessage","getCommitSystemPrompt","HumanMessage","message","extractContent","content","cleanCommitMessage","splitCommits","splitPrompt","buildSplitPrompt","splitResponse","getSplitSystemPrompt","splitContent","commitGroups","parseCommitGroups","validGroups","group","Array","isArray","files","generateMessageForGroup","push","executeCommit","autoStage","input","executePush","branch","success","error","includes","executeSplitCommits","commits","committedCount","originalHead","commit","file","committed","refineCommitMessage","currentMessage","userSuggestion","slice","getRefineSystemPrompt","diff","Set","lines","line","startsWith","match","add","from","first","String","text","map","l","firstCommitLine","find","replace","scopeHint","fullDiff","untrackedHint","join","maxLength","scopePart","type","description","jsonMatch","parsed","JSON","parse"],"mappings":"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"}
|
|
@@ -170,7 +170,7 @@ let PrGeneratorService = class PrGeneratorService {
|
|
|
170
170
|
const { title, description, commitSummaries } = this.parseSingleResponse(content, commits);
|
|
171
171
|
return {
|
|
172
172
|
title: title || this.generateDefaultTitle(branchName),
|
|
173
|
-
description,
|
|
173
|
+
description: description && description.trim().length > 0 ? description : this.getPRTemplate(),
|
|
174
174
|
commits: commitSummaries
|
|
175
175
|
};
|
|
176
176
|
}
|
|
@@ -263,7 +263,7 @@ let PrGeneratorService = class PrGeneratorService {
|
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
formatPRForClipboard(title, description, baseBranch) {
|
|
266
|
-
return
|
|
266
|
+
return description;
|
|
267
267
|
}
|
|
268
268
|
getPRCreateUrl(platform, baseBranch) {
|
|
269
269
|
try {
|
|
@@ -294,16 +294,9 @@ let PrGeneratorService = class PrGeneratorService {
|
|
|
294
294
|
const scope = this.monorepoDetector.determineScope(commit.files, monorepoInfo);
|
|
295
295
|
return `Analyze this commit:\n\n**Commit:** ${commit.hash}\n**Message:** ${commit.message}\n**Author:** ${commit.author}\n**Date:** ${commit.date}\n**Scope:** ${scope || 'general'}\n\n**Files Changed:**\n${commit.files.join('\n')}\n\n**Diff Stats:**\n${commit.diff}\n\nProvide:\n1. A one-line summary (max 100 chars)\n2. Detailed explanation of changes, approach, and impact`;
|
|
296
296
|
}
|
|
297
|
-
buildPRDescriptionPrompt(branchName, commits, baseBranch) {
|
|
298
|
-
const commitsSummary = commits.map((c, i)=>`${i + 1}. **${c.hash}** - ${c.summary}\n ${c.details.slice(0, 200)}...`).join('\n\n');
|
|
299
|
-
return `Create a PR description for branch "${branchName}" to "${baseBranch}".\n\n**Commits:**\n${commitsSummary}\n\nGenerate:\n1. PR title (use branch name as inspiration)\n2. Description with: Overview, Changes, Technical Details, Testing`;
|
|
300
|
-
}
|
|
301
297
|
getCommitAnalysisSystemPrompt() {
|
|
302
298
|
return `Analyze a git commit. Provide:\n\n1. **Summary**: One sentence (max 100 chars)\n2. **Details**: Detailed paragraph about changes\n\nFormat:\nSUMMARY: <summary>\n\nDETAILS: <details>`;
|
|
303
299
|
}
|
|
304
|
-
getPRDescriptionSystemPrompt() {
|
|
305
|
-
return `Create a Pull Request description.\n\n**Title:** Concise, descriptive\n\n**Description Structure:**\n\`\`\`markdown\n## Overview\nBrief explanation\n\n## Changes\n- Change 1\n- Change 2\n\n## Technical Details\nImplementation details\n\n## Testing\nHow to test\n\`\`\``;
|
|
306
|
-
}
|
|
307
300
|
parseCommitAnalysis(content) {
|
|
308
301
|
const summaryMatch = content.match(/SUMMARY:\s*(.+?)(?=\n\n|DETAILS:|$)/is);
|
|
309
302
|
const detailsMatch = content.match(/DETAILS:\s*(.+?)$/is);
|
|
@@ -327,71 +320,37 @@ let PrGeneratorService = class PrGeneratorService {
|
|
|
327
320
|
return `Branch: ${branchName}\nBase: ${baseBranch}\nCommits: ${commits.length}\n\n${commitsInfo}`;
|
|
328
321
|
}
|
|
329
322
|
getSingleAgentSystemPrompt() {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
**OUTPUT FORMAT:**
|
|
333
|
-
TITLE: <PR title based on branch name and overall changes>
|
|
334
|
-
|
|
335
|
-
OVERVIEW: <2-3 sentences explaining what this PR accomplishes>
|
|
336
|
-
|
|
337
|
-
CHANGES:
|
|
338
|
-
- <key change 1>
|
|
339
|
-
- <key change 2>
|
|
340
|
-
- ...
|
|
341
|
-
|
|
342
|
-
TECHNICAL_DETAILS: <important implementation details, architecture decisions>
|
|
323
|
+
const template = this.getPRTemplate();
|
|
324
|
+
return `You are a senior developer creating a Pull Request description. Analyze ALL commits and fill in the PR template below.
|
|
343
325
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
326
|
+
Rules:
|
|
327
|
+
- Output ONLY the filled template.
|
|
328
|
+
- Keep headings, checklists, and ordering exactly as shown.
|
|
329
|
+
- Fill ONLY these sections with content: "O que essa PR faz?", "O que foi mexido?", "O que eu fiz?".
|
|
330
|
+
- Leave the other sections blank for the user to fill in.
|
|
331
|
+
- Do not add any extra sections beyond the template.
|
|
332
|
+
- Keep the final reminder line.
|
|
347
333
|
|
|
348
|
-
|
|
334
|
+
PR TEMPLATE:
|
|
335
|
+
${template}`;
|
|
349
336
|
}
|
|
350
337
|
parseSingleResponse(content, commits) {
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (overviewMatch) {
|
|
360
|
-
parts.push('## Overview\n' + overviewMatch[1].trim());
|
|
338
|
+
const title = '';
|
|
339
|
+
let description = content.trim();
|
|
340
|
+
const requiredHeading = '## 🚀 O que essa PR faz?';
|
|
341
|
+
const hasRequiredHeading = description.includes(requiredHeading);
|
|
342
|
+
if (!hasRequiredHeading) {
|
|
343
|
+
description = this.getPRTemplate();
|
|
344
|
+
} else if (!description.includes('Não se esqueça de revisar essa descrição')) {
|
|
345
|
+
description = `${description.trim()}\n\nNão se esqueça de revisar essa descrição`;
|
|
361
346
|
}
|
|
362
|
-
if (changesMatch) {
|
|
363
|
-
parts.push('## Changes\n' + changesMatch[1].trim());
|
|
364
|
-
}
|
|
365
|
-
if (technicalMatch) {
|
|
366
|
-
parts.push('## Technical Details\n' + technicalMatch[1].trim());
|
|
367
|
-
}
|
|
368
|
-
if (testingMatch) {
|
|
369
|
-
parts.push('## Testing\n' + testingMatch[1].trim());
|
|
370
|
-
}
|
|
371
|
-
const description = parts.join('\n\n');
|
|
372
347
|
const commitSummaries = [];
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
commitSummaries.push({
|
|
380
|
-
hash: match[1].slice(0, 7),
|
|
381
|
-
summary: match[2].trim(),
|
|
382
|
-
details: ''
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (commitSummaries.length === 0) {
|
|
388
|
-
for (const commit of commits){
|
|
389
|
-
commitSummaries.push({
|
|
390
|
-
hash: commit.hash,
|
|
391
|
-
summary: commit.message.slice(0, 60),
|
|
392
|
-
details: ''
|
|
393
|
-
});
|
|
394
|
-
}
|
|
348
|
+
for (const commit of commits){
|
|
349
|
+
commitSummaries.push({
|
|
350
|
+
hash: commit.hash,
|
|
351
|
+
summary: commit.message.slice(0, 60),
|
|
352
|
+
details: ''
|
|
353
|
+
});
|
|
395
354
|
}
|
|
396
355
|
return {
|
|
397
356
|
title,
|
|
@@ -400,7 +359,7 @@ TESTING: <how to test these changes>`;
|
|
|
400
359
|
};
|
|
401
360
|
}
|
|
402
361
|
generateDefaultTitle(branchName) {
|
|
403
|
-
return branchName
|
|
362
|
+
return branchName;
|
|
404
363
|
}
|
|
405
364
|
extractContent(content) {
|
|
406
365
|
if (typeof content === 'string') return content;
|
|
@@ -412,9 +371,51 @@ TESTING: <how to test these changes>`;
|
|
|
412
371
|
}
|
|
413
372
|
return String(content);
|
|
414
373
|
}
|
|
374
|
+
getPRTemplate() {
|
|
375
|
+
if (this.prTemplateCache) {
|
|
376
|
+
return this.prTemplateCache;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const fs = require('fs');
|
|
380
|
+
const raw = fs.readFileSync(this.prTemplatePath, 'utf-8').trimEnd();
|
|
381
|
+
this.prTemplateCache = this.normalizeTemplate(raw);
|
|
382
|
+
return this.prTemplateCache;
|
|
383
|
+
} catch {
|
|
384
|
+
const fallback = this.buildDefaultTemplate();
|
|
385
|
+
this.prTemplateCache = fallback;
|
|
386
|
+
return this.prTemplateCache;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
normalizeTemplate(raw) {
|
|
390
|
+
const trimmed = raw.trimEnd();
|
|
391
|
+
if (trimmed.includes('Não se esqueça de revisar essa descrição')) {
|
|
392
|
+
return trimmed;
|
|
393
|
+
}
|
|
394
|
+
return `${trimmed}\n\nNão se esqueça de revisar essa descrição`;
|
|
395
|
+
}
|
|
396
|
+
buildDefaultTemplate() {
|
|
397
|
+
return [
|
|
398
|
+
'## 🚀 O que essa PR faz?',
|
|
399
|
+
'',
|
|
400
|
+
'## 🛠️ O que foi mexido?',
|
|
401
|
+
'',
|
|
402
|
+
'## 💡 O que eu fiz? (Resumo técnico)',
|
|
403
|
+
'',
|
|
404
|
+
'## 🧪 Como testar?',
|
|
405
|
+
'',
|
|
406
|
+
'## ✅ Checklist do Dev',
|
|
407
|
+
'',
|
|
408
|
+
'## 📸 Prints / GIFs (Se for UI)',
|
|
409
|
+
'',
|
|
410
|
+
'## 🔗 Link da Task',
|
|
411
|
+
'',
|
|
412
|
+
'Não se esqueça de revisar essa descrição'
|
|
413
|
+
].join('\n');
|
|
414
|
+
}
|
|
415
415
|
constructor(multiLlmService, monorepoDetector){
|
|
416
416
|
this.multiLlmService = multiLlmService;
|
|
417
417
|
this.monorepoDetector = monorepoDetector;
|
|
418
|
+
this.prTemplatePath = '/home/pedro-castanheira/Downloads/pull-request.template.md';
|
|
418
419
|
}
|
|
419
420
|
};
|
|
420
421
|
PrGeneratorService = _ts_decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/git/services/pr-generator.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { execSync } from 'child_process';\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { MultiLlmService } from '../../../common/services/multi-llm.service';\nimport { MonorepoDetectorService } from './monorepo-detector.service';\n\nexport interface CommitInfo {\n hash: string;\n message: string;\n author: string;\n date: string;\n files: string[];\n diff: string;\n}\n\nexport interface PRDescription {\n title: string;\n description: string;\n commits: { hash: string; summary: string; details: string }[];\n}\n\nexport interface PRCreationResult {\n success: boolean;\n url?: string;\n error?: string;\n description?: string;\n platform: 'github' | 'azure' | 'gitlab' | 'bitbucket' | 'unknown';\n}\n\n@Injectable()\nexport class PrGeneratorService {\n constructor(\n private readonly multiLlmService: MultiLlmService,\n private readonly monorepoDetector: MonorepoDetectorService,\n ) {}\n\n getCurrentBranch(): string {\n try {\n return execSync('git branch --show-current', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n } catch {\n return 'unknown';\n }\n }\n\n detectDefaultBaseBranch(): string {\n try {\n const cwd = process.cwd();\n const candidates = ['main', 'master', 'develop'];\n \n for (const branch of candidates) {\n try {\n execSync(`git rev-parse --verify ${branch} 2>/dev/null || git rev-parse --verify origin/${branch} 2>/dev/null`, { \n cwd, \n stdio: 'ignore' \n });\n return branch;\n } catch {\n }\n }\n \n return 'main';\n } catch {\n return 'main';\n }\n }\n\n detectPlatform(): { platform: 'github' | 'azure' | 'gitlab' | 'bitbucket' | 'unknown'; url: string } {\n try {\n const remoteUrl = execSync('git remote get-url origin', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n\n if (remoteUrl.includes('github.com')) {\n return { platform: 'github', url: remoteUrl };\n }\n if (remoteUrl.includes('dev.azure.com') || remoteUrl.includes('visualstudio.com')) {\n return { platform: 'azure', url: remoteUrl };\n }\n if (remoteUrl.includes('gitlab.com') || remoteUrl.includes('gitlab')) {\n return { platform: 'gitlab', url: remoteUrl };\n }\n if (remoteUrl.includes('bitbucket.org')) {\n return { platform: 'bitbucket', url: remoteUrl };\n }\n\n return { platform: 'unknown', url: remoteUrl };\n } catch {\n return { platform: 'unknown', url: '' };\n }\n }\n\n getCommitsNotInBase(baseBranch: string = 'develop'): CommitInfo[] {\n try {\n const cwd = process.cwd();\n \n try {\n execSync(`git rev-parse --verify ${baseBranch}`, { cwd, stdio: 'ignore' });\n } catch {\n try {\n execSync(`git rev-parse --verify origin/${baseBranch}`, { cwd, stdio: 'ignore' });\n } catch {\n return [];\n }\n }\n\n const logOutput = execSync(\n `git log ${baseBranch}..HEAD --pretty=format:\"%H|%s|%an|%ad\" --date=short`,\n { cwd, encoding: 'utf-8' }\n );\n\n if (!logOutput.trim()) {\n return [];\n }\n\n const commits: CommitInfo[] = [];\n const lines = logOutput.trim().split('\\n');\n\n for (const line of lines) {\n const [hash, message, author, date] = line.split('|');\n if (!hash) continue;\n\n const filesOutput = execSync(\n `git diff-tree --no-commit-id --name-only -r ${hash}`,\n { cwd, encoding: 'utf-8' }\n );\n const files = filesOutput.trim().split('\\n').filter(f => f);\n\n const diffOutput = execSync(\n `git show ${hash} --stat`,\n { cwd, encoding: 'utf-8' }\n );\n\n commits.push({\n hash: hash.slice(0, 7),\n message,\n author,\n date,\n files,\n diff: diffOutput,\n });\n }\n\n return commits.reverse();\n } catch (error) {\n return [];\n }\n }\n\n async analyzeCommit(commit: CommitInfo): Promise<{ summary: string; details: string }> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const prompt = this.buildCommitAnalysisPrompt(commit);\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitAnalysisSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const content = this.extractContent(response.content);\n return this.parseCommitAnalysis(content);\n }\n\n async generatePRDescription(\n branchName: string, \n commits: CommitInfo[],\n baseBranch: string = 'develop',\n ): Promise<PRDescription> {\n const llm = this.multiLlmService.createModel('cheap');\n const prompt = this.buildSinglePrompt(branchName, commits, baseBranch);\n\n const response = await llm.invoke([\n new SystemMessage(this.getSingleAgentSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const content = this.extractContent(response.content);\n const { title, description, commitSummaries } = this.parseSingleResponse(content, commits);\n\n return {\n title: title || this.generateDefaultTitle(branchName),\n description,\n commits: commitSummaries,\n };\n }\n\n async createPR(\n title: string, \n description: string, \n baseBranch: string = 'develop'\n ): Promise<PRCreationResult> {\n const { platform } = this.detectPlatform();\n const branch = this.getCurrentBranch();\n\n if (platform !== 'github') {\n return {\n success: false,\n error: `Automatic PR creation not supported for ${platform}. Description generated and copied to clipboard.`,\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n\n try {\n execSync('which gh', { cwd: process.cwd() });\n } catch {\n return {\n success: false,\n error: 'GitHub CLI (gh) not found. Install from https://cli.github.com/',\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n\n try {\n const cwd = process.cwd();\n\n const tempFile = `/tmp/pr-body-${Date.now()}.md`;\n require('fs').writeFileSync(tempFile, description);\n\n try {\n const result = execSync(\n `gh pr create --title \"${title.replace(/\"/g, '\\\\\"')}\" --body-file \"${tempFile}\" --base \"${baseBranch}\"`,\n { cwd, encoding: 'utf-8' }\n );\n \n const urlMatch = result.match(/https:\\/\\/github\\.com\\/[^\\s]+/);\n \n return { \n success: true, \n url: urlMatch ? urlMatch[0] : undefined,\n platform,\n };\n } finally {\n try {\n require('fs').unlinkSync(tempFile);\n } catch {}\n }\n } catch (error: any) {\n const message = error.message || 'Failed to create PR';\n if (message.includes('already exists')) {\n return { \n success: false, \n error: 'A PR already exists for this branch',\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n return { \n success: false, \n error: message,\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n }\n\n copyToClipboard(text: string): boolean {\n try {\n const platform = process.platform;\n \n if (platform === 'darwin') {\n execSync(`echo ${JSON.stringify(text)} | pbcopy`);\n return true;\n } else if (platform === 'linux') {\n try {\n execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`);\n return true;\n } catch {\n try {\n execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`);\n return true;\n } catch {\n return false;\n }\n }\n } else if (platform === 'win32') {\n execSync(`echo ${JSON.stringify(text)} | clip`);\n return true;\n }\n \n return false;\n } catch {\n return false;\n }\n }\n\n formatPRForClipboard(title: string, description: string, baseBranch: string): string {\n return `# ${title}\\n\\n${description}\\n\\n---\\n**Base Branch:** ${baseBranch}\\n**Generated by:** Cast Code`;\n }\n\n getPRCreateUrl(platform: string, baseBranch: string): string | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n\n const branch = this.getCurrentBranch();\n\n let httpsUrl = remoteUrl\n .replace(/^git@github\\.com:/, 'https://github.com/')\n .replace(/^git@gitlab\\.com:/, 'https://gitlab.com/')\n .replace(/^git@bitbucket\\.org:/, 'https://bitbucket.org/')\n .replace(/\\.git$/, '');\n\n switch (platform) {\n case 'github':\n return `${httpsUrl}/compare/${baseBranch}...${branch}?expand=1`;\n case 'gitlab':\n return `${httpsUrl}/merge_requests/new?merge_request[source_branch]=${branch}&merge_request[target_branch]=${baseBranch}`;\n case 'bitbucket':\n return `${httpsUrl}/pull-requests/new?source=${branch}&dest=${baseBranch}`;\n case 'azure':\n return null;\n default:\n return null;\n }\n } catch {\n return null;\n }\n }\n\n private buildCommitAnalysisPrompt(commit: CommitInfo): string {\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const scope = this.monorepoDetector.determineScope(commit.files, monorepoInfo);\n\n return `Analyze this commit:\\n\\n**Commit:** ${commit.hash}\\n**Message:** ${commit.message}\\n**Author:** ${commit.author}\\n**Date:** ${commit.date}\\n**Scope:** ${scope || 'general'}\\n\\n**Files Changed:**\\n${commit.files.join('\\n')}\\n\\n**Diff Stats:**\\n${commit.diff}\\n\\nProvide:\\n1. A one-line summary (max 100 chars)\\n2. Detailed explanation of changes, approach, and impact`;\n }\n\n private buildPRDescriptionPrompt(\n branchName: string, \n commits: { hash: string; message: string; summary: string; details: string }[],\n baseBranch: string,\n ): string {\n const commitsSummary = commits.map((c, i) => \n `${i + 1}. **${c.hash}** - ${c.summary}\\n ${c.details.slice(0, 200)}...`\n ).join('\\n\\n');\n\n return `Create a PR description for branch \"${branchName}\" to \"${baseBranch}\".\\n\\n**Commits:**\\n${commitsSummary}\\n\\nGenerate:\\n1. PR title (use branch name as inspiration)\\n2. Description with: Overview, Changes, Technical Details, Testing`;\n }\n\n private getCommitAnalysisSystemPrompt(): string {\n return `Analyze a git commit. Provide:\\n\\n1. **Summary**: One sentence (max 100 chars)\\n2. **Details**: Detailed paragraph about changes\\n\\nFormat:\\nSUMMARY: <summary>\\n\\nDETAILS: <details>`;\n }\n\n private getPRDescriptionSystemPrompt(): string {\n return `Create a Pull Request description.\\n\\n**Title:** Concise, descriptive\\n\\n**Description Structure:**\\n\\`\\`\\`markdown\\n## Overview\\nBrief explanation\\n\\n## Changes\\n- Change 1\\n- Change 2\\n\\n## Technical Details\\nImplementation details\\n\\n## Testing\\nHow to test\\n\\`\\`\\``;\n }\n\n private parseCommitAnalysis(content: string): { summary: string; details: string } {\n const summaryMatch = content.match(/SUMMARY:\\s*(.+?)(?=\\n\\n|DETAILS:|$)/is);\n const detailsMatch = content.match(/DETAILS:\\s*(.+?)$/is);\n\n return {\n summary: summaryMatch ? summaryMatch[1].trim() : 'No summary available',\n details: detailsMatch ? detailsMatch[1].trim() : content,\n };\n }\n\n private parsePRDescription(content: string): { title: string; description: string } {\n const titleMatch = content.match(/^#?\\s*Title:?\\s*(.+?)(?=\\n\\n|\\n##|$)/i);\n const lines = content.split('\\n');\n const title = titleMatch ? titleMatch[1].trim() : lines[0].replace(/^#+\\s*/, '').trim();\n const description = titleMatch \n ? content.slice(content.indexOf(titleMatch[0]) + titleMatch[0].length).trim()\n : lines.slice(1).join('\\n').trim();\n\n return { title, description };\n }\n\n private buildSinglePrompt(branchName: string, commits: CommitInfo[], baseBranch: string): string {\n const commitsInfo = commits.map((c, i) => \n `${i + 1}. **${c.hash}** - ${c.message}\\n Files: ${c.files.slice(0, 5).join(', ')}${c.files.length > 5 ? '...' : ''}\\n Stats: ${c.diff.split('\\n').slice(-3, -1).join(' ')}`\n ).join('\\n\\n');\n\n return `Branch: ${branchName}\\nBase: ${baseBranch}\\nCommits: ${commits.length}\\n\\n${commitsInfo}`;\n }\n\n private getSingleAgentSystemPrompt(): string {\n return `You are a senior developer creating a Pull Request description. Analyze ALL commits and generate a comprehensive PR description.\n\n**OUTPUT FORMAT:**\nTITLE: <PR title based on branch name and overall changes>\n\nOVERVIEW: <2-3 sentences explaining what this PR accomplishes>\n\nCHANGES:\n- <key change 1>\n- <key change 2>\n- ...\n\nTECHNICAL_DETAILS: <important implementation details, architecture decisions>\n\nCOMMITS:\n<hash>: <one-line summary of what this commit does>\n(repeat for each commit)\n\nTESTING: <how to test these changes>`;\n }\n\n private parseSingleResponse(content: string, commits: CommitInfo[]): { \n title: string; \n description: string; \n commitSummaries: { hash: string; summary: string; details: string }[] \n } {\n const titleMatch = content.match(/TITLE:\\s*(.+?)(?=\\n\\n|\\n[A-Z]|$)/i);\n const overviewMatch = content.match(/OVERVIEW:\\s*([\\s\\S]+?)(?=\\n\\nCHANGES:|CHANGES:)/i);\n const changesMatch = content.match(/CHANGES:\\s*([\\s\\S]+?)(?=\\n\\nTECHNICAL_DETAILS:|TECHNICAL_DETAILS:)/i);\n const technicalMatch = content.match(/TECHNICAL_DETAILS:\\s*([\\s\\S]+?)(?=\\n\\nCOMMITS:|COMMITS:)/i);\n const commitsMatch = content.match(/COMMITS:\\s*([\\s\\S]+?)(?=\\n\\nTESTING:|TESTING:|$)/i);\n const testingMatch = content.match(/TESTING:\\s*([\\s\\S]+)$/i);\n\n const title = titleMatch ? titleMatch[1].trim() : '';\n \n const parts: string[] = [];\n \n if (overviewMatch) {\n parts.push('## Overview\\n' + overviewMatch[1].trim());\n }\n \n if (changesMatch) {\n parts.push('## Changes\\n' + changesMatch[1].trim());\n }\n \n if (technicalMatch) {\n parts.push('## Technical Details\\n' + technicalMatch[1].trim());\n }\n \n if (testingMatch) {\n parts.push('## Testing\\n' + testingMatch[1].trim());\n }\n\n const description = parts.join('\\n\\n');\n\n const commitSummaries: { hash: string; summary: string; details: string }[] = [];\n \n if (commitsMatch) {\n const commitsText = commitsMatch[1].trim();\n const lines = commitsText.split('\\n');\n \n for (const line of lines) {\n const match = line.match(/^([a-f0-9]+):\\s*(.+)$/i);\n if (match) {\n commitSummaries.push({\n hash: match[1].slice(0, 7),\n summary: match[2].trim(),\n details: '',\n });\n }\n }\n }\n\n if (commitSummaries.length === 0) {\n for (const commit of commits) {\n commitSummaries.push({\n hash: commit.hash,\n summary: commit.message.slice(0, 60),\n details: '',\n });\n }\n }\n\n return { title, description, commitSummaries };\n }\n\n private generateDefaultTitle(branchName: string): string {\n return branchName\n .replace(/^(feature|fix|hotfix|release)\\//, '')\n .split(/[-_]/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n }\n\n private extractContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (typeof first === 'object' && first !== null && 'text' in first) {\n return String(first.text);\n }\n }\n return String(content);\n }\n}\n"],"names":["PrGeneratorService","getCurrentBranch","execSync","cwd","process","encoding","trim","detectDefaultBaseBranch","candidates","branch","stdio","detectPlatform","remoteUrl","includes","platform","url","getCommitsNotInBase","baseBranch","logOutput","commits","lines","split","line","hash","message","author","date","filesOutput","files","filter","f","diffOutput","push","slice","diff","reverse","error","analyzeCommit","commit","llm","multiLlmService","createModel","prompt","buildCommitAnalysisPrompt","response","invoke","SystemMessage","getCommitAnalysisSystemPrompt","HumanMessage","content","extractContent","parseCommitAnalysis","generatePRDescription","branchName","buildSinglePrompt","getSingleAgentSystemPrompt","title","description","commitSummaries","parseSingleResponse","generateDefaultTitle","createPR","success","formatPRForClipboard","tempFile","Date","now","require","writeFileSync","result","replace","urlMatch","match","undefined","unlinkSync","copyToClipboard","text","JSON","stringify","getPRCreateUrl","httpsUrl","monorepoInfo","monorepoDetector","detectMonorepo","scope","determineScope","join","buildPRDescriptionPrompt","commitsSummary","map","c","i","summary","details","getPRDescriptionSystemPrompt","summaryMatch","detailsMatch","parsePRDescription","titleMatch","indexOf","length","commitsInfo","overviewMatch","changesMatch","technicalMatch","commitsMatch","testingMatch","parts","commitsText","word","charAt","toUpperCase","Array","isArray","first","String"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/git/services/pr-generator.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { execSync } from 'child_process';\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\nimport { MultiLlmService } from '../../../common/services/multi-llm.service';\nimport { MonorepoDetectorService } from './monorepo-detector.service';\n\nexport interface CommitInfo {\n hash: string;\n message: string;\n author: string;\n date: string;\n files: string[];\n diff: string;\n}\n\nexport interface PRDescription {\n title: string;\n description: string;\n commits: { hash: string; summary: string; details: string }[];\n}\n\nexport interface PRCreationResult {\n success: boolean;\n url?: string;\n error?: string;\n description?: string;\n platform: 'github' | 'azure' | 'gitlab' | 'bitbucket' | 'unknown';\n}\n\n@Injectable()\nexport class PrGeneratorService {\n private prTemplateCache?: string;\n private readonly prTemplatePath = '/home/pedro-castanheira/Downloads/pull-request.template.md';\n\n constructor(\n private readonly multiLlmService: MultiLlmService,\n private readonly monorepoDetector: MonorepoDetectorService,\n ) {}\n\n getCurrentBranch(): string {\n try {\n return execSync('git branch --show-current', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n } catch {\n return 'unknown';\n }\n }\n\n detectDefaultBaseBranch(): string {\n try {\n const cwd = process.cwd();\n const candidates = ['main', 'master', 'develop'];\n \n for (const branch of candidates) {\n try {\n execSync(`git rev-parse --verify ${branch} 2>/dev/null || git rev-parse --verify origin/${branch} 2>/dev/null`, { \n cwd, \n stdio: 'ignore' \n });\n return branch;\n } catch {\n }\n }\n \n return 'main';\n } catch {\n return 'main';\n }\n }\n\n detectPlatform(): { platform: 'github' | 'azure' | 'gitlab' | 'bitbucket' | 'unknown'; url: string } {\n try {\n const remoteUrl = execSync('git remote get-url origin', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n\n if (remoteUrl.includes('github.com')) {\n return { platform: 'github', url: remoteUrl };\n }\n if (remoteUrl.includes('dev.azure.com') || remoteUrl.includes('visualstudio.com')) {\n return { platform: 'azure', url: remoteUrl };\n }\n if (remoteUrl.includes('gitlab.com') || remoteUrl.includes('gitlab')) {\n return { platform: 'gitlab', url: remoteUrl };\n }\n if (remoteUrl.includes('bitbucket.org')) {\n return { platform: 'bitbucket', url: remoteUrl };\n }\n\n return { platform: 'unknown', url: remoteUrl };\n } catch {\n return { platform: 'unknown', url: '' };\n }\n }\n\n getCommitsNotInBase(baseBranch: string = 'develop'): CommitInfo[] {\n try {\n const cwd = process.cwd();\n \n try {\n execSync(`git rev-parse --verify ${baseBranch}`, { cwd, stdio: 'ignore' });\n } catch {\n try {\n execSync(`git rev-parse --verify origin/${baseBranch}`, { cwd, stdio: 'ignore' });\n } catch {\n return [];\n }\n }\n\n const logOutput = execSync(\n `git log ${baseBranch}..HEAD --pretty=format:\"%H|%s|%an|%ad\" --date=short`,\n { cwd, encoding: 'utf-8' }\n );\n\n if (!logOutput.trim()) {\n return [];\n }\n\n const commits: CommitInfo[] = [];\n const lines = logOutput.trim().split('\\n');\n\n for (const line of lines) {\n const [hash, message, author, date] = line.split('|');\n if (!hash) continue;\n\n const filesOutput = execSync(\n `git diff-tree --no-commit-id --name-only -r ${hash}`,\n { cwd, encoding: 'utf-8' }\n );\n const files = filesOutput.trim().split('\\n').filter(f => f);\n\n const diffOutput = execSync(\n `git show ${hash} --stat`,\n { cwd, encoding: 'utf-8' }\n );\n\n commits.push({\n hash: hash.slice(0, 7),\n message,\n author,\n date,\n files,\n diff: diffOutput,\n });\n }\n\n return commits.reverse();\n } catch (error) {\n return [];\n }\n }\n\n async analyzeCommit(commit: CommitInfo): Promise<{ summary: string; details: string }> {\n const llm = this.multiLlmService.createModel('cheap');\n\n const prompt = this.buildCommitAnalysisPrompt(commit);\n\n const response = await llm.invoke([\n new SystemMessage(this.getCommitAnalysisSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const content = this.extractContent(response.content);\n return this.parseCommitAnalysis(content);\n }\n\n async generatePRDescription(\n branchName: string, \n commits: CommitInfo[],\n baseBranch: string = 'develop',\n ): Promise<PRDescription> {\n const llm = this.multiLlmService.createModel('cheap');\n const prompt = this.buildSinglePrompt(branchName, commits, baseBranch);\n\n const response = await llm.invoke([\n new SystemMessage(this.getSingleAgentSystemPrompt()),\n new HumanMessage(prompt),\n ]);\n\n const content = this.extractContent(response.content);\n const { title, description, commitSummaries } = this.parseSingleResponse(content, commits);\n\n return {\n title: title || this.generateDefaultTitle(branchName),\n description: description && description.trim().length > 0 ? description : this.getPRTemplate(),\n commits: commitSummaries,\n };\n }\n\n async createPR(\n title: string, \n description: string, \n baseBranch: string = 'develop'\n ): Promise<PRCreationResult> {\n const { platform } = this.detectPlatform();\n const branch = this.getCurrentBranch();\n\n if (platform !== 'github') {\n return {\n success: false,\n error: `Automatic PR creation not supported for ${platform}. Description generated and copied to clipboard.`,\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n\n try {\n execSync('which gh', { cwd: process.cwd() });\n } catch {\n return {\n success: false,\n error: 'GitHub CLI (gh) not found. Install from https://cli.github.com/',\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n\n try {\n const cwd = process.cwd();\n\n const tempFile = `/tmp/pr-body-${Date.now()}.md`;\n require('fs').writeFileSync(tempFile, description);\n\n try {\n const result = execSync(\n `gh pr create --title \"${title.replace(/\"/g, '\\\\\"')}\" --body-file \"${tempFile}\" --base \"${baseBranch}\"`,\n { cwd, encoding: 'utf-8' }\n );\n \n const urlMatch = result.match(/https:\\/\\/github\\.com\\/[^\\s]+/);\n \n return { \n success: true, \n url: urlMatch ? urlMatch[0] : undefined,\n platform,\n };\n } finally {\n try {\n require('fs').unlinkSync(tempFile);\n } catch {}\n }\n } catch (error: any) {\n const message = error.message || 'Failed to create PR';\n if (message.includes('already exists')) {\n return { \n success: false, \n error: 'A PR already exists for this branch',\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n return { \n success: false, \n error: message,\n description: this.formatPRForClipboard(title, description, baseBranch),\n platform,\n };\n }\n }\n\n copyToClipboard(text: string): boolean {\n try {\n const platform = process.platform;\n \n if (platform === 'darwin') {\n execSync(`echo ${JSON.stringify(text)} | pbcopy`);\n return true;\n } else if (platform === 'linux') {\n try {\n execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`);\n return true;\n } catch {\n try {\n execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`);\n return true;\n } catch {\n return false;\n }\n }\n } else if (platform === 'win32') {\n execSync(`echo ${JSON.stringify(text)} | clip`);\n return true;\n }\n \n return false;\n } catch {\n return false;\n }\n }\n\n formatPRForClipboard(title: string, description: string, baseBranch: string): string {\n return description;\n }\n\n getPRCreateUrl(platform: string, baseBranch: string): string | null {\n try {\n const remoteUrl = execSync('git remote get-url origin', { \n cwd: process.cwd(), \n encoding: 'utf-8' \n }).trim();\n\n const branch = this.getCurrentBranch();\n\n let httpsUrl = remoteUrl\n .replace(/^git@github\\.com:/, 'https://github.com/')\n .replace(/^git@gitlab\\.com:/, 'https://gitlab.com/')\n .replace(/^git@bitbucket\\.org:/, 'https://bitbucket.org/')\n .replace(/\\.git$/, '');\n\n switch (platform) {\n case 'github':\n return `${httpsUrl}/compare/${baseBranch}...${branch}?expand=1`;\n case 'gitlab':\n return `${httpsUrl}/merge_requests/new?merge_request[source_branch]=${branch}&merge_request[target_branch]=${baseBranch}`;\n case 'bitbucket':\n return `${httpsUrl}/pull-requests/new?source=${branch}&dest=${baseBranch}`;\n case 'azure':\n return null;\n default:\n return null;\n }\n } catch {\n return null;\n }\n }\n\n private buildCommitAnalysisPrompt(commit: CommitInfo): string {\n const monorepoInfo = this.monorepoDetector.detectMonorepo(process.cwd());\n const scope = this.monorepoDetector.determineScope(commit.files, monorepoInfo);\n\n return `Analyze this commit:\\n\\n**Commit:** ${commit.hash}\\n**Message:** ${commit.message}\\n**Author:** ${commit.author}\\n**Date:** ${commit.date}\\n**Scope:** ${scope || 'general'}\\n\\n**Files Changed:**\\n${commit.files.join('\\n')}\\n\\n**Diff Stats:**\\n${commit.diff}\\n\\nProvide:\\n1. A one-line summary (max 100 chars)\\n2. Detailed explanation of changes, approach, and impact`;\n }\n\n private getCommitAnalysisSystemPrompt(): string {\n return `Analyze a git commit. Provide:\\n\\n1. **Summary**: One sentence (max 100 chars)\\n2. **Details**: Detailed paragraph about changes\\n\\nFormat:\\nSUMMARY: <summary>\\n\\nDETAILS: <details>`;\n }\n\n private parseCommitAnalysis(content: string): { summary: string; details: string } {\n const summaryMatch = content.match(/SUMMARY:\\s*(.+?)(?=\\n\\n|DETAILS:|$)/is);\n const detailsMatch = content.match(/DETAILS:\\s*(.+?)$/is);\n\n return {\n summary: summaryMatch ? summaryMatch[1].trim() : 'No summary available',\n details: detailsMatch ? detailsMatch[1].trim() : content,\n };\n }\n\n private parsePRDescription(content: string): { title: string; description: string } {\n const titleMatch = content.match(/^#?\\s*Title:?\\s*(.+?)(?=\\n\\n|\\n##|$)/i);\n const lines = content.split('\\n');\n const title = titleMatch ? titleMatch[1].trim() : lines[0].replace(/^#+\\s*/, '').trim();\n const description = titleMatch \n ? content.slice(content.indexOf(titleMatch[0]) + titleMatch[0].length).trim()\n : lines.slice(1).join('\\n').trim();\n\n return { title, description };\n }\n\n private buildSinglePrompt(branchName: string, commits: CommitInfo[], baseBranch: string): string {\n const commitsInfo = commits.map((c, i) => \n `${i + 1}. **${c.hash}** - ${c.message}\\n Files: ${c.files.slice(0, 5).join(', ')}${c.files.length > 5 ? '...' : ''}\\n Stats: ${c.diff.split('\\n').slice(-3, -1).join(' ')}`\n ).join('\\n\\n');\n\n return `Branch: ${branchName}\\nBase: ${baseBranch}\\nCommits: ${commits.length}\\n\\n${commitsInfo}`;\n }\n\n private getSingleAgentSystemPrompt(): string {\n const template = this.getPRTemplate();\n return `You are a senior developer creating a Pull Request description. Analyze ALL commits and fill in the PR template below.\n\nRules:\n- Output ONLY the filled template.\n- Keep headings, checklists, and ordering exactly as shown.\n- Fill ONLY these sections with content: \"O que essa PR faz?\", \"O que foi mexido?\", \"O que eu fiz?\".\n- Leave the other sections blank for the user to fill in.\n- Do not add any extra sections beyond the template.\n- Keep the final reminder line.\n\nPR TEMPLATE:\n${template}`;\n }\n\n private parseSingleResponse(content: string, commits: CommitInfo[]): { \n title: string; \n description: string; \n commitSummaries: { hash: string; summary: string; details: string }[] \n } {\n const title = '';\n let description = content.trim();\n const requiredHeading = '## 🚀 O que essa PR faz?';\n const hasRequiredHeading = description.includes(requiredHeading);\n\n if (!hasRequiredHeading) {\n description = this.getPRTemplate();\n } else if (!description.includes('Não se esqueça de revisar essa descrição')) {\n description = `${description.trim()}\\n\\nNão se esqueça de revisar essa descrição`;\n }\n\n const commitSummaries: { hash: string; summary: string; details: string }[] = [];\n for (const commit of commits) {\n commitSummaries.push({\n hash: commit.hash,\n summary: commit.message.slice(0, 60),\n details: '',\n });\n }\n\n return { title, description, commitSummaries };\n }\n\n private generateDefaultTitle(branchName: string): string {\n return branchName;\n }\n\n private extractContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (typeof first === 'object' && first !== null && 'text' in first) {\n return String(first.text);\n }\n }\n return String(content);\n }\n\n private getPRTemplate(): string {\n if (this.prTemplateCache) {\n return this.prTemplateCache;\n }\n\n try {\n const fs = require('fs');\n const raw = fs.readFileSync(this.prTemplatePath, 'utf-8').trimEnd();\n this.prTemplateCache = this.normalizeTemplate(raw);\n return this.prTemplateCache;\n } catch {\n const fallback = this.buildDefaultTemplate();\n this.prTemplateCache = fallback;\n return this.prTemplateCache;\n }\n }\n\n private normalizeTemplate(raw: string): string {\n const trimmed = raw.trimEnd();\n if (trimmed.includes('Não se esqueça de revisar essa descrição')) {\n return trimmed;\n }\n return `${trimmed}\\n\\nNão se esqueça de revisar essa descrição`;\n }\n\n private buildDefaultTemplate(): string {\n return [\n '## 🚀 O que essa PR faz?',\n '',\n '## 🛠️ O que foi mexido?',\n '',\n '## 💡 O que eu fiz? (Resumo técnico)',\n '',\n '## 🧪 Como testar?',\n '',\n '## ✅ Checklist do Dev',\n '',\n '## 📸 Prints / GIFs (Se for UI)',\n '',\n '## 🔗 Link da Task',\n '',\n 'Não se esqueça de revisar essa descrição',\n ].join('\\n');\n }\n}\n"],"names":["PrGeneratorService","getCurrentBranch","execSync","cwd","process","encoding","trim","detectDefaultBaseBranch","candidates","branch","stdio","detectPlatform","remoteUrl","includes","platform","url","getCommitsNotInBase","baseBranch","logOutput","commits","lines","split","line","hash","message","author","date","filesOutput","files","filter","f","diffOutput","push","slice","diff","reverse","error","analyzeCommit","commit","llm","multiLlmService","createModel","prompt","buildCommitAnalysisPrompt","response","invoke","SystemMessage","getCommitAnalysisSystemPrompt","HumanMessage","content","extractContent","parseCommitAnalysis","generatePRDescription","branchName","buildSinglePrompt","getSingleAgentSystemPrompt","title","description","commitSummaries","parseSingleResponse","generateDefaultTitle","length","getPRTemplate","createPR","success","formatPRForClipboard","tempFile","Date","now","require","writeFileSync","result","replace","urlMatch","match","undefined","unlinkSync","copyToClipboard","text","JSON","stringify","getPRCreateUrl","httpsUrl","monorepoInfo","monorepoDetector","detectMonorepo","scope","determineScope","join","summaryMatch","detailsMatch","summary","details","parsePRDescription","titleMatch","indexOf","commitsInfo","map","c","i","template","requiredHeading","hasRequiredHeading","Array","isArray","first","String","prTemplateCache","fs","raw","readFileSync","prTemplatePath","trimEnd","normalizeTemplate","fallback","buildDefaultTemplate","trimmed"],"mappings":"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"}
|