git-coco 0.22.7 → 0.22.9

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/index.d.ts CHANGED
@@ -337,6 +337,7 @@ interface BaseParserOptions {
337
337
  llm: ReturnType<typeof getLlm>;
338
338
  git: SimpleGit;
339
339
  logger: Logger;
340
+ maxTokens?: number;
340
341
  }
341
342
  interface BaseParserInput {
342
343
  options: BaseParserOptions;
@@ -47,7 +47,7 @@ import { pathToFileURL } from 'url';
47
47
  /**
48
48
  * Current build version from package.json
49
49
  */
50
- const BUILD_VERSION = "0.22.7";
50
+ const BUILD_VERSION = "0.22.9";
51
51
 
52
52
  const isInteractive = (config) => {
53
53
  return config?.mode === 'interactive' || !!config?.interactive;
@@ -7212,6 +7212,8 @@ const CONVENTIONAL_TEMPLATE = `Generate a commit message that strictly adheres t
7212
7212
  - Scope should be a noun in parentheses (e.g., (parser), (ui), (config))
7213
7213
  - Omit scope if the change is broad or affects multiple areas
7214
7214
 
7215
+ CRITICAL: You must respond with ONLY valid JSON. All string values must be properly quoted.
7216
+
7215
7217
  Based on the following diff summary, generate a conventional commit message that follows these rules exactly:
7216
7218
 
7217
7219
  """"""
@@ -7955,6 +7957,55 @@ async function executeChainWithSchema(schema, llm, prompt, variables, options =
7955
7957
  }
7956
7958
  }
7957
7959
 
7960
+ /**
7961
+ * Utility to repair common JSON formatting issues that LLMs make
7962
+ * Specifically handles cases where string values are not properly quoted
7963
+ */
7964
+ function repairJson(jsonString) {
7965
+ // Remove any markdown code block wrapping
7966
+ let cleaned = jsonString.replace(/```(?:json)?\s*([\s\S]*?)\s*```/g, '$1').trim();
7967
+ // Remove inline code block wrapping
7968
+ cleaned = cleaned.replace(/^`(.*)`$/, '$1').trim();
7969
+ // If it doesn't look like JSON, return as-is
7970
+ if (!cleaned.startsWith('{') || !cleaned.endsWith('}')) {
7971
+ return jsonString;
7972
+ }
7973
+ try {
7974
+ // First try parsing as-is
7975
+ JSON.parse(cleaned);
7976
+ return cleaned;
7977
+ }
7978
+ catch {
7979
+ // Try to repair common issues
7980
+ let repaired = cleaned;
7981
+ // Fix unquoted string values in title and body fields
7982
+ // Pattern: "title": unquoted_value, -> "title": "unquoted_value",
7983
+ repaired = repaired.replace(/"(title|body)":\s*([^",\{\}\[\]]+?)(?=\s*[,\}])/g, (match, field, value) => {
7984
+ // Clean up the value (remove leading/trailing whitespace)
7985
+ const cleanValue = value.trim();
7986
+ // If it's already quoted or looks like a number/boolean, leave it
7987
+ if (cleanValue.startsWith('"') || /^(true|false|\d+)$/.test(cleanValue)) {
7988
+ return match;
7989
+ }
7990
+ // Quote the value
7991
+ return `"${field}": "${cleanValue}"`;
7992
+ });
7993
+ // Fix missing quotes around field names (though this should be rare)
7994
+ repaired = repaired.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":');
7995
+ // Remove trailing commas before closing braces
7996
+ repaired = repaired.replace(/,(\s*[}\]])/g, '$1');
7997
+ try {
7998
+ // Test if the repair worked
7999
+ JSON.parse(repaired);
8000
+ return repaired;
8001
+ }
8002
+ catch {
8003
+ // If repair failed, return original
8004
+ return jsonString;
8005
+ }
8006
+ }
8007
+ }
8008
+
7958
8009
  /**
7959
8010
  * Utility function to ensure commit messages are properly formatted as strings
7960
8011
  * rather than JSON objects, whether they come as parsed objects or stringified JSON
@@ -8004,7 +8055,23 @@ function formatCommitMessage(result, options = {}) {
8004
8055
  }
8005
8056
  }
8006
8057
  catch {
8007
- // Not valid JSON, continue to fallback
8058
+ // Try to repair the JSON and parse again
8059
+ try {
8060
+ const repairedJson = repairJson(jsonString);
8061
+ const parsed = JSON.parse(repairedJson);
8062
+ if (parsed &&
8063
+ typeof parsed === 'object' &&
8064
+ typeof parsed.title === 'string' &&
8065
+ typeof parsed.body === 'string' &&
8066
+ parsed.title.length > 0 &&
8067
+ parsed.body.length > 0) {
8068
+ // Successfully repaired and parsed JSON
8069
+ return constructMessage(parsed.title, parsed.body);
8070
+ }
8071
+ }
8072
+ catch {
8073
+ // Repair failed, continue to fallback
8074
+ }
8008
8075
  }
8009
8076
  }
8010
8077
  // If no JSON found and it's already formatted, return as-is
@@ -11140,10 +11207,7 @@ for (var i = 0; i < 256; i++) {
11140
11207
  simpleEscapeMap[i] = simpleEscapeSequence(i);
11141
11208
  }
11142
11209
 
11143
- // Max tokens for GPT-3 is 4096
11144
- // const MAX_TOKENS_PER_SUMMARY = 4096
11145
- const MAX_TOKENS_PER_SUMMARY = 12288;
11146
- async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
11210
+ async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger, maxTokens }, }) {
11147
11211
  const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 10000, chunkOverlap: 250 });
11148
11212
  const summarizationChain = loadSummarizationChain(model, {
11149
11213
  type: 'map_reduce',
@@ -11161,7 +11225,7 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
11161
11225
  logger.startTimer();
11162
11226
  const summary = await summarizeDiffs(diffs, {
11163
11227
  tokenizer,
11164
- maxTokens: MAX_TOKENS_PER_SUMMARY,
11228
+ maxTokens: maxTokens || 4096,
11165
11229
  textSplitter,
11166
11230
  chain: summarizationChain,
11167
11231
  logger,
@@ -11464,7 +11528,7 @@ const handler$3 = async (argv, logger) => {
11464
11528
  return await fileChangeParser({
11465
11529
  changes,
11466
11530
  commit: '--staged',
11467
- options: { tokenizer, git, llm, logger },
11531
+ options: { tokenizer, git, llm, logger, maxTokens: config.service.tokenLimit },
11468
11532
  });
11469
11533
  }
11470
11534
  const commitMsg = await generateAndReviewLoop({
@@ -11498,12 +11562,23 @@ const handler$3 = async (argv, logger) => {
11498
11562
  const schema = USE_CONVENTIONAL_COMMITS
11499
11563
  ? ConventionalCommitMessageResponseSchema
11500
11564
  : CommitMessageResponseSchema;
11501
- const formatInstructions = `You must always return a valid JSON object. Do not return any additional text. The JSON object you return should match the following schema:
11565
+ const formatInstructions = `CRITICAL: You must return ONLY a valid JSON object with no additional text, explanations, or markdown formatting.
11566
+
11567
+ REQUIRED JSON FORMAT:
11502
11568
  ${schema.description}
11569
+
11570
+ EXAMPLE (follow this exact structure):
11503
11571
  {
11504
- "title": "The commit title",
11505
- "body": "The commit body"
11506
- }`;
11572
+ "title": "feat(auth): add user authentication system",
11573
+ "body": "Implement JWT-based authentication with login and logout functionality. Includes password hashing and session management."
11574
+ }
11575
+
11576
+ IMPORTANT RULES:
11577
+ - ALL string values MUST be enclosed in double quotes
11578
+ - NO trailing commas
11579
+ - NO comments or additional text outside the JSON
11580
+ - The "title" and "body" values must be properly quoted strings
11581
+ - Return ONLY the JSON object, nothing else`;
11507
11582
  // Use conventional commit prompt if enabled
11508
11583
  const promptTemplate = USE_CONVENTIONAL_COMMITS ? CONVENTIONAL_COMMIT_PROMPT : COMMIT_PROMPT;
11509
11584
  const prompt = getPrompt({
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
69
69
  /**
70
70
  * Current build version from package.json
71
71
  */
72
- const BUILD_VERSION = "0.22.7";
72
+ const BUILD_VERSION = "0.22.9";
73
73
 
74
74
  const isInteractive = (config) => {
75
75
  return config?.mode === 'interactive' || !!config?.interactive;
@@ -7234,6 +7234,8 @@ const CONVENTIONAL_TEMPLATE = `Generate a commit message that strictly adheres t
7234
7234
  - Scope should be a noun in parentheses (e.g., (parser), (ui), (config))
7235
7235
  - Omit scope if the change is broad or affects multiple areas
7236
7236
 
7237
+ CRITICAL: You must respond with ONLY valid JSON. All string values must be properly quoted.
7238
+
7237
7239
  Based on the following diff summary, generate a conventional commit message that follows these rules exactly:
7238
7240
 
7239
7241
  """"""
@@ -7977,6 +7979,55 @@ async function executeChainWithSchema(schema, llm, prompt, variables, options =
7977
7979
  }
7978
7980
  }
7979
7981
 
7982
+ /**
7983
+ * Utility to repair common JSON formatting issues that LLMs make
7984
+ * Specifically handles cases where string values are not properly quoted
7985
+ */
7986
+ function repairJson(jsonString) {
7987
+ // Remove any markdown code block wrapping
7988
+ let cleaned = jsonString.replace(/```(?:json)?\s*([\s\S]*?)\s*```/g, '$1').trim();
7989
+ // Remove inline code block wrapping
7990
+ cleaned = cleaned.replace(/^`(.*)`$/, '$1').trim();
7991
+ // If it doesn't look like JSON, return as-is
7992
+ if (!cleaned.startsWith('{') || !cleaned.endsWith('}')) {
7993
+ return jsonString;
7994
+ }
7995
+ try {
7996
+ // First try parsing as-is
7997
+ JSON.parse(cleaned);
7998
+ return cleaned;
7999
+ }
8000
+ catch {
8001
+ // Try to repair common issues
8002
+ let repaired = cleaned;
8003
+ // Fix unquoted string values in title and body fields
8004
+ // Pattern: "title": unquoted_value, -> "title": "unquoted_value",
8005
+ repaired = repaired.replace(/"(title|body)":\s*([^",\{\}\[\]]+?)(?=\s*[,\}])/g, (match, field, value) => {
8006
+ // Clean up the value (remove leading/trailing whitespace)
8007
+ const cleanValue = value.trim();
8008
+ // If it's already quoted or looks like a number/boolean, leave it
8009
+ if (cleanValue.startsWith('"') || /^(true|false|\d+)$/.test(cleanValue)) {
8010
+ return match;
8011
+ }
8012
+ // Quote the value
8013
+ return `"${field}": "${cleanValue}"`;
8014
+ });
8015
+ // Fix missing quotes around field names (though this should be rare)
8016
+ repaired = repaired.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":');
8017
+ // Remove trailing commas before closing braces
8018
+ repaired = repaired.replace(/,(\s*[}\]])/g, '$1');
8019
+ try {
8020
+ // Test if the repair worked
8021
+ JSON.parse(repaired);
8022
+ return repaired;
8023
+ }
8024
+ catch {
8025
+ // If repair failed, return original
8026
+ return jsonString;
8027
+ }
8028
+ }
8029
+ }
8030
+
7980
8031
  /**
7981
8032
  * Utility function to ensure commit messages are properly formatted as strings
7982
8033
  * rather than JSON objects, whether they come as parsed objects or stringified JSON
@@ -8026,7 +8077,23 @@ function formatCommitMessage(result, options = {}) {
8026
8077
  }
8027
8078
  }
8028
8079
  catch {
8029
- // Not valid JSON, continue to fallback
8080
+ // Try to repair the JSON and parse again
8081
+ try {
8082
+ const repairedJson = repairJson(jsonString);
8083
+ const parsed = JSON.parse(repairedJson);
8084
+ if (parsed &&
8085
+ typeof parsed === 'object' &&
8086
+ typeof parsed.title === 'string' &&
8087
+ typeof parsed.body === 'string' &&
8088
+ parsed.title.length > 0 &&
8089
+ parsed.body.length > 0) {
8090
+ // Successfully repaired and parsed JSON
8091
+ return constructMessage(parsed.title, parsed.body);
8092
+ }
8093
+ }
8094
+ catch {
8095
+ // Repair failed, continue to fallback
8096
+ }
8030
8097
  }
8031
8098
  }
8032
8099
  // If no JSON found and it's already formatted, return as-is
@@ -11162,10 +11229,7 @@ for (var i = 0; i < 256; i++) {
11162
11229
  simpleEscapeMap[i] = simpleEscapeSequence(i);
11163
11230
  }
11164
11231
 
11165
- // Max tokens for GPT-3 is 4096
11166
- // const MAX_TOKENS_PER_SUMMARY = 4096
11167
- const MAX_TOKENS_PER_SUMMARY = 12288;
11168
- async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
11232
+ async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger, maxTokens }, }) {
11169
11233
  const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 10000, chunkOverlap: 250 });
11170
11234
  const summarizationChain = loadSummarizationChain(model, {
11171
11235
  type: 'map_reduce',
@@ -11183,7 +11247,7 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
11183
11247
  logger.startTimer();
11184
11248
  const summary = await summarizeDiffs(diffs, {
11185
11249
  tokenizer,
11186
- maxTokens: MAX_TOKENS_PER_SUMMARY,
11250
+ maxTokens: maxTokens || 4096,
11187
11251
  textSplitter,
11188
11252
  chain: summarizationChain,
11189
11253
  logger,
@@ -11486,7 +11550,7 @@ const handler$3 = async (argv, logger) => {
11486
11550
  return await fileChangeParser({
11487
11551
  changes,
11488
11552
  commit: '--staged',
11489
- options: { tokenizer, git, llm, logger },
11553
+ options: { tokenizer, git, llm, logger, maxTokens: config.service.tokenLimit },
11490
11554
  });
11491
11555
  }
11492
11556
  const commitMsg = await generateAndReviewLoop({
@@ -11520,12 +11584,23 @@ const handler$3 = async (argv, logger) => {
11520
11584
  const schema = USE_CONVENTIONAL_COMMITS
11521
11585
  ? ConventionalCommitMessageResponseSchema
11522
11586
  : CommitMessageResponseSchema;
11523
- const formatInstructions = `You must always return a valid JSON object. Do not return any additional text. The JSON object you return should match the following schema:
11587
+ const formatInstructions = `CRITICAL: You must return ONLY a valid JSON object with no additional text, explanations, or markdown formatting.
11588
+
11589
+ REQUIRED JSON FORMAT:
11524
11590
  ${schema.description}
11591
+
11592
+ EXAMPLE (follow this exact structure):
11525
11593
  {
11526
- "title": "The commit title",
11527
- "body": "The commit body"
11528
- }`;
11594
+ "title": "feat(auth): add user authentication system",
11595
+ "body": "Implement JWT-based authentication with login and logout functionality. Includes password hashing and session management."
11596
+ }
11597
+
11598
+ IMPORTANT RULES:
11599
+ - ALL string values MUST be enclosed in double quotes
11600
+ - NO trailing commas
11601
+ - NO comments or additional text outside the JSON
11602
+ - The "title" and "body" values must be properly quoted strings
11603
+ - Return ONLY the JSON object, nothing else`;
11529
11604
  // Use conventional commit prompt if enabled
11530
11605
  const promptTemplate = USE_CONVENTIONAL_COMMITS ? CONVENTIONAL_COMMIT_PROMPT : COMMIT_PROMPT;
11531
11606
  const prompt = getPrompt({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-coco",
3
- "version": "0.22.7",
3
+ "version": "0.22.9",
4
4
  "description": "zero-effort git commits with coco.",
5
5
  "author": "gfargo <ghfargo@gmail.com>",
6
6
  "license": "MIT",