git-coco 0.12.0 → 0.12.1

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
@@ -4,8 +4,8 @@ import { OllamaInput } from '@langchain/community/llms/ollama';
4
4
  import { BaseLLMParams } from '@langchain/core/language_models/llms';
5
5
  import * as _langchain_openai from '@langchain/openai';
6
6
  import { TiktokenModel, OpenAIInput, ChatOpenAI } from '@langchain/openai';
7
- import { ChatOllama } from '@langchain/ollama';
8
7
  import { SimpleGit } from 'simple-git';
8
+ import { ChatOllama } from '@langchain/ollama';
9
9
  import { Color } from 'chalk';
10
10
  import { TiktokenModel as TiktokenModel$1 } from 'tiktoken';
11
11
 
@@ -285,7 +285,7 @@ interface BaseParserInput {
285
285
  }
286
286
  interface FileChangeParserInput extends BaseParserInput {
287
287
  changes: FileChange[];
288
- commit: '--staged' | string;
288
+ commit: '--staged' | '--unstaged' | '--untracked' | string;
289
289
  }
290
290
  interface CommitLogParserInput extends BaseParserInput {
291
291
  range: {
@@ -2608,100 +2608,6 @@ async function summarizeDiffs(rootDiffNode, { tokenizer, logger, maxTokens = 204
2608
2608
  return directoryDiffs.map(handleOutput).join('');
2609
2609
  }
2610
2610
 
2611
- class DiffTreeNode {
2612
- constructor(path) {
2613
- this.path = [];
2614
- this.files = [];
2615
- this.children = new Map();
2616
- if (path)
2617
- this.path = path;
2618
- }
2619
- addFile(file) {
2620
- this.files.push(file);
2621
- }
2622
- addChild(part, node) {
2623
- this.children.set(part, node);
2624
- }
2625
- getChild(part) {
2626
- return this.children.get(part);
2627
- }
2628
- getPath() {
2629
- return this.path.join('/');
2630
- }
2631
- print(indentation = 0) {
2632
- const indent = ' '.repeat(indentation);
2633
- let output = `${indent}- Path: ${this.getPath()}\n`;
2634
- if (this.files.length > 0) {
2635
- output += `${indent} Files:\n`;
2636
- for (const file of this.files) {
2637
- output += `${indent} - ${file.summary}\n`;
2638
- }
2639
- }
2640
- if (this.children.size > 0) {
2641
- output += `${indent} Children:\n`;
2642
- for (const [, child] of this.children) {
2643
- output += child.print(indentation + 4);
2644
- }
2645
- }
2646
- return output;
2647
- }
2648
- }
2649
- const createDiffTree = (changes) => {
2650
- const root = new DiffTreeNode();
2651
- for (const change of changes) {
2652
- let currentParent = root;
2653
- const parts = change.filePath.split('/');
2654
- parts.pop();
2655
- for (const part of parts) {
2656
- let childNode = currentParent.getChild(part);
2657
- if (!childNode) {
2658
- childNode = new DiffTreeNode([...currentParent.path, part]);
2659
- currentParent.addChild(part, childNode);
2660
- }
2661
- currentParent = childNode;
2662
- }
2663
- // Create a NodeFile object and add it to the parent
2664
- currentParent.addFile({
2665
- filePath: change.filePath,
2666
- oldFilePath: change.oldFilePath,
2667
- summary: change.summary,
2668
- status: change.status,
2669
- });
2670
- }
2671
- return root;
2672
- };
2673
-
2674
- /**
2675
- * Asynchronously collect diffs for a given node and its children.
2676
- */
2677
- async function collectDiffs(node, getFileDiff, tokenizer, logger) {
2678
- // Collect diffs for the files of the current node
2679
- const diffPromises = node.files.map(async (nodeFile) => {
2680
- const diff = await getFileDiff(nodeFile);
2681
- const tokenCount = tokenizer(diff);
2682
- logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
2683
- color: 'magenta',
2684
- });
2685
- return {
2686
- file: nodeFile.filePath,
2687
- summary: nodeFile.summary,
2688
- diff,
2689
- tokenCount,
2690
- };
2691
- });
2692
- // Collect diffs for the children of the current node
2693
- const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer, logger));
2694
- const [diffs, children] = await Promise.all([
2695
- Promise.all(diffPromises),
2696
- Promise.all(childrenPromises),
2697
- ]);
2698
- return {
2699
- path: node.getPath(),
2700
- diffs,
2701
- children,
2702
- };
2703
- }
2704
-
2705
2611
  /**
2706
2612
  * Base interface that all chains must implement.
2707
2613
  */
@@ -5950,6 +5856,100 @@ function getTextSplitter(options = {}) {
5950
5856
  return new RecursiveCharacterTextSplitter(options);
5951
5857
  }
5952
5858
 
5859
+ /**
5860
+ * Asynchronously collect diffs for a given node and its children.
5861
+ */
5862
+ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
5863
+ // Collect diffs for the files of the current node
5864
+ const diffPromises = node.files.map(async (nodeFile) => {
5865
+ const diff = await getFileDiff(nodeFile);
5866
+ const tokenCount = tokenizer(diff);
5867
+ logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
5868
+ color: 'magenta',
5869
+ });
5870
+ return {
5871
+ file: nodeFile.filePath,
5872
+ summary: nodeFile.summary,
5873
+ diff,
5874
+ tokenCount,
5875
+ };
5876
+ });
5877
+ // Collect diffs for the children of the current node
5878
+ const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer, logger));
5879
+ const [diffs, children] = await Promise.all([
5880
+ Promise.all(diffPromises),
5881
+ Promise.all(childrenPromises),
5882
+ ]);
5883
+ return {
5884
+ path: node.getPath(),
5885
+ diffs,
5886
+ children,
5887
+ };
5888
+ }
5889
+
5890
+ class DiffTreeNode {
5891
+ constructor(path) {
5892
+ this.path = [];
5893
+ this.files = [];
5894
+ this.children = new Map();
5895
+ if (path)
5896
+ this.path = path;
5897
+ }
5898
+ addFile(file) {
5899
+ this.files.push(file);
5900
+ }
5901
+ addChild(part, node) {
5902
+ this.children.set(part, node);
5903
+ }
5904
+ getChild(part) {
5905
+ return this.children.get(part);
5906
+ }
5907
+ getPath() {
5908
+ return this.path.join('/');
5909
+ }
5910
+ print(indentation = 0) {
5911
+ const indent = ' '.repeat(indentation);
5912
+ let output = `${indent}- Path: ${this.getPath()}\n`;
5913
+ if (this.files.length > 0) {
5914
+ output += `${indent} Files:\n`;
5915
+ for (const file of this.files) {
5916
+ output += `${indent} - ${file.summary}\n`;
5917
+ }
5918
+ }
5919
+ if (this.children.size > 0) {
5920
+ output += `${indent} Children:\n`;
5921
+ for (const [, child] of this.children) {
5922
+ output += child.print(indentation + 4);
5923
+ }
5924
+ }
5925
+ return output;
5926
+ }
5927
+ }
5928
+ const createDiffTree = (changes) => {
5929
+ const root = new DiffTreeNode();
5930
+ for (const change of changes) {
5931
+ let currentParent = root;
5932
+ const parts = change.filePath.split('/');
5933
+ parts.pop();
5934
+ for (const part of parts) {
5935
+ let childNode = currentParent.getChild(part);
5936
+ if (!childNode) {
5937
+ childNode = new DiffTreeNode([...currentParent.path, part]);
5938
+ currentParent.addChild(part, childNode);
5939
+ }
5940
+ currentParent = childNode;
5941
+ }
5942
+ // Create a NodeFile object and add it to the parent
5943
+ currentParent.addFile({
5944
+ filePath: change.filePath,
5945
+ oldFilePath: change.oldFilePath,
5946
+ summary: change.summary,
5947
+ status: change.status,
5948
+ });
5949
+ }
5950
+ return root;
5951
+ };
5952
+
5953
5953
  /**
5954
5954
  * Parses the default file diff for a given nodeFile.
5955
5955
  *
@@ -5959,8 +5959,15 @@ function getTextSplitter(options = {}) {
5959
5959
  * @returns A Promise that resolves to the file diff as a string.
5960
5960
  */
5961
5961
  async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
5962
- if (commit !== '--staged') {
5963
- return await git.diff([`${commit}~1..${commit}`, '--', nodeFile.filePath]);
5962
+ if (commit === '--staged') {
5963
+ return await git.diff(['--staged', nodeFile.filePath]);
5964
+ }
5965
+ else if (commit === '--unstaged') {
5966
+ return await git.diff([nodeFile.filePath]);
5967
+ }
5968
+ else if (commit === '--untracked') {
5969
+ // For untracked files, return the entire file content
5970
+ return await git.show([`:${nodeFile.filePath}`]);
5964
5971
  }
5965
5972
  return await git.diff([commit, nodeFile.filePath]);
5966
5973
  }
@@ -6041,10 +6048,6 @@ async function getDiff(nodeFile, commit, { git, logger, }) {
6041
6048
  const MAX_TOKENS_PER_SUMMARY = 12288;
6042
6049
  async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
6043
6050
  const textSplitter = getTextSplitter({ chunkSize: 10000, chunkOverlap: 250 });
6044
- // const textSplitter = new TokenTextSplitter({
6045
- // chunkSize: 10000,
6046
- // chunkOverlap: 250,
6047
- // });
6048
6051
  const summarizationChain = getSummarizationChain(model, {
6049
6052
  type: 'map_reduce',
6050
6053
  combineMapPrompt: SUMMARIZE_PROMPT,
@@ -6887,13 +6890,25 @@ const handler = async (argv, logger) => {
6887
6890
  case 'current':
6888
6891
  const { staged, unstaged, untracked } = await getChanges({ git });
6889
6892
  logger.log(`Staged: ${staged.length}, Unstaged: ${unstaged?.length || 0}, Untracked: ${untracked?.length || 0}`);
6890
- const result = await fileChangeParser({
6891
- changes: [...staged, ...(unstaged ? unstaged : []), ...(untracked ? untracked : [])],
6892
- commit: 'HEAD',
6893
+ const unstagedChanges = await fileChangeParser({
6894
+ changes: unstaged || [],
6895
+ commit: '--unstaged',
6896
+ options: { tokenizer, git, llm, logger },
6897
+ });
6898
+ const unstagedResponse = `Unstaged changes:\n${unstagedChanges}`;
6899
+ const untrackedChanges = await fileChangeParser({
6900
+ changes: untracked || [],
6901
+ commit: '--untracked',
6902
+ options: { tokenizer, git, llm, logger },
6903
+ });
6904
+ const untrackedResponse = `Untracked changes:\n${untrackedChanges}`;
6905
+ const stagedChanges = await fileChangeParser({
6906
+ changes: staged,
6907
+ commit: '--staged',
6893
6908
  options: { tokenizer, git, llm, logger },
6894
6909
  });
6895
- logger.log(`Parsed Result: ${result}`);
6896
- return [result];
6910
+ const stagedResponse = `Staged changes:\n${stagedChanges}`;
6911
+ return [unstagedResponse, untrackedResponse, stagedResponse];
6897
6912
  case 'yesterday':
6898
6913
  const yesterday = new Date();
6899
6914
  yesterday.setDate(yesterday.getDate() - 1);
@@ -6915,7 +6930,6 @@ const handler = async (argv, logger) => {
6915
6930
  }
6916
6931
  }
6917
6932
  async function parser(changes) {
6918
- console.log({ changes });
6919
6933
  return changes.join('\n');
6920
6934
  }
6921
6935
  const recap = await generateAndReviewLoop({
package/dist/index.js CHANGED
@@ -2629,100 +2629,6 @@ async function summarizeDiffs(rootDiffNode, { tokenizer, logger, maxTokens = 204
2629
2629
  return directoryDiffs.map(handleOutput).join('');
2630
2630
  }
2631
2631
 
2632
- class DiffTreeNode {
2633
- constructor(path) {
2634
- this.path = [];
2635
- this.files = [];
2636
- this.children = new Map();
2637
- if (path)
2638
- this.path = path;
2639
- }
2640
- addFile(file) {
2641
- this.files.push(file);
2642
- }
2643
- addChild(part, node) {
2644
- this.children.set(part, node);
2645
- }
2646
- getChild(part) {
2647
- return this.children.get(part);
2648
- }
2649
- getPath() {
2650
- return this.path.join('/');
2651
- }
2652
- print(indentation = 0) {
2653
- const indent = ' '.repeat(indentation);
2654
- let output = `${indent}- Path: ${this.getPath()}\n`;
2655
- if (this.files.length > 0) {
2656
- output += `${indent} Files:\n`;
2657
- for (const file of this.files) {
2658
- output += `${indent} - ${file.summary}\n`;
2659
- }
2660
- }
2661
- if (this.children.size > 0) {
2662
- output += `${indent} Children:\n`;
2663
- for (const [, child] of this.children) {
2664
- output += child.print(indentation + 4);
2665
- }
2666
- }
2667
- return output;
2668
- }
2669
- }
2670
- const createDiffTree = (changes) => {
2671
- const root = new DiffTreeNode();
2672
- for (const change of changes) {
2673
- let currentParent = root;
2674
- const parts = change.filePath.split('/');
2675
- parts.pop();
2676
- for (const part of parts) {
2677
- let childNode = currentParent.getChild(part);
2678
- if (!childNode) {
2679
- childNode = new DiffTreeNode([...currentParent.path, part]);
2680
- currentParent.addChild(part, childNode);
2681
- }
2682
- currentParent = childNode;
2683
- }
2684
- // Create a NodeFile object and add it to the parent
2685
- currentParent.addFile({
2686
- filePath: change.filePath,
2687
- oldFilePath: change.oldFilePath,
2688
- summary: change.summary,
2689
- status: change.status,
2690
- });
2691
- }
2692
- return root;
2693
- };
2694
-
2695
- /**
2696
- * Asynchronously collect diffs for a given node and its children.
2697
- */
2698
- async function collectDiffs(node, getFileDiff, tokenizer, logger) {
2699
- // Collect diffs for the files of the current node
2700
- const diffPromises = node.files.map(async (nodeFile) => {
2701
- const diff = await getFileDiff(nodeFile);
2702
- const tokenCount = tokenizer(diff);
2703
- logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
2704
- color: 'magenta',
2705
- });
2706
- return {
2707
- file: nodeFile.filePath,
2708
- summary: nodeFile.summary,
2709
- diff,
2710
- tokenCount,
2711
- };
2712
- });
2713
- // Collect diffs for the children of the current node
2714
- const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer, logger));
2715
- const [diffs, children] = await Promise.all([
2716
- Promise.all(diffPromises),
2717
- Promise.all(childrenPromises),
2718
- ]);
2719
- return {
2720
- path: node.getPath(),
2721
- diffs,
2722
- children,
2723
- };
2724
- }
2725
-
2726
2632
  /**
2727
2633
  * Base interface that all chains must implement.
2728
2634
  */
@@ -5971,6 +5877,100 @@ function getTextSplitter(options = {}) {
5971
5877
  return new RecursiveCharacterTextSplitter(options);
5972
5878
  }
5973
5879
 
5880
+ /**
5881
+ * Asynchronously collect diffs for a given node and its children.
5882
+ */
5883
+ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
5884
+ // Collect diffs for the files of the current node
5885
+ const diffPromises = node.files.map(async (nodeFile) => {
5886
+ const diff = await getFileDiff(nodeFile);
5887
+ const tokenCount = tokenizer(diff);
5888
+ logger.verbose(`Collected diff for ${nodeFile.filePath} (${tokenCount} tokens)`, {
5889
+ color: 'magenta',
5890
+ });
5891
+ return {
5892
+ file: nodeFile.filePath,
5893
+ summary: nodeFile.summary,
5894
+ diff,
5895
+ tokenCount,
5896
+ };
5897
+ });
5898
+ // Collect diffs for the children of the current node
5899
+ const childrenPromises = Array.from(node.children.values()).map(async (child) => collectDiffs(child, getFileDiff, tokenizer, logger));
5900
+ const [diffs, children] = await Promise.all([
5901
+ Promise.all(diffPromises),
5902
+ Promise.all(childrenPromises),
5903
+ ]);
5904
+ return {
5905
+ path: node.getPath(),
5906
+ diffs,
5907
+ children,
5908
+ };
5909
+ }
5910
+
5911
+ class DiffTreeNode {
5912
+ constructor(path) {
5913
+ this.path = [];
5914
+ this.files = [];
5915
+ this.children = new Map();
5916
+ if (path)
5917
+ this.path = path;
5918
+ }
5919
+ addFile(file) {
5920
+ this.files.push(file);
5921
+ }
5922
+ addChild(part, node) {
5923
+ this.children.set(part, node);
5924
+ }
5925
+ getChild(part) {
5926
+ return this.children.get(part);
5927
+ }
5928
+ getPath() {
5929
+ return this.path.join('/');
5930
+ }
5931
+ print(indentation = 0) {
5932
+ const indent = ' '.repeat(indentation);
5933
+ let output = `${indent}- Path: ${this.getPath()}\n`;
5934
+ if (this.files.length > 0) {
5935
+ output += `${indent} Files:\n`;
5936
+ for (const file of this.files) {
5937
+ output += `${indent} - ${file.summary}\n`;
5938
+ }
5939
+ }
5940
+ if (this.children.size > 0) {
5941
+ output += `${indent} Children:\n`;
5942
+ for (const [, child] of this.children) {
5943
+ output += child.print(indentation + 4);
5944
+ }
5945
+ }
5946
+ return output;
5947
+ }
5948
+ }
5949
+ const createDiffTree = (changes) => {
5950
+ const root = new DiffTreeNode();
5951
+ for (const change of changes) {
5952
+ let currentParent = root;
5953
+ const parts = change.filePath.split('/');
5954
+ parts.pop();
5955
+ for (const part of parts) {
5956
+ let childNode = currentParent.getChild(part);
5957
+ if (!childNode) {
5958
+ childNode = new DiffTreeNode([...currentParent.path, part]);
5959
+ currentParent.addChild(part, childNode);
5960
+ }
5961
+ currentParent = childNode;
5962
+ }
5963
+ // Create a NodeFile object and add it to the parent
5964
+ currentParent.addFile({
5965
+ filePath: change.filePath,
5966
+ oldFilePath: change.oldFilePath,
5967
+ summary: change.summary,
5968
+ status: change.status,
5969
+ });
5970
+ }
5971
+ return root;
5972
+ };
5973
+
5974
5974
  /**
5975
5975
  * Parses the default file diff for a given nodeFile.
5976
5976
  *
@@ -5980,8 +5980,15 @@ function getTextSplitter(options = {}) {
5980
5980
  * @returns A Promise that resolves to the file diff as a string.
5981
5981
  */
5982
5982
  async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
5983
- if (commit !== '--staged') {
5984
- return await git.diff([`${commit}~1..${commit}`, '--', nodeFile.filePath]);
5983
+ if (commit === '--staged') {
5984
+ return await git.diff(['--staged', nodeFile.filePath]);
5985
+ }
5986
+ else if (commit === '--unstaged') {
5987
+ return await git.diff([nodeFile.filePath]);
5988
+ }
5989
+ else if (commit === '--untracked') {
5990
+ // For untracked files, return the entire file content
5991
+ return await git.show([`:${nodeFile.filePath}`]);
5985
5992
  }
5986
5993
  return await git.diff([commit, nodeFile.filePath]);
5987
5994
  }
@@ -6062,10 +6069,6 @@ async function getDiff(nodeFile, commit, { git, logger, }) {
6062
6069
  const MAX_TOKENS_PER_SUMMARY = 12288;
6063
6070
  async function fileChangeParser({ changes, commit, options: { tokenizer, git, llm: model, logger }, }) {
6064
6071
  const textSplitter = getTextSplitter({ chunkSize: 10000, chunkOverlap: 250 });
6065
- // const textSplitter = new TokenTextSplitter({
6066
- // chunkSize: 10000,
6067
- // chunkOverlap: 250,
6068
- // });
6069
6072
  const summarizationChain = getSummarizationChain(model, {
6070
6073
  type: 'map_reduce',
6071
6074
  combineMapPrompt: SUMMARIZE_PROMPT,
@@ -6908,13 +6911,25 @@ const handler = async (argv, logger) => {
6908
6911
  case 'current':
6909
6912
  const { staged, unstaged, untracked } = await getChanges({ git });
6910
6913
  logger.log(`Staged: ${staged.length}, Unstaged: ${unstaged?.length || 0}, Untracked: ${untracked?.length || 0}`);
6911
- const result = await fileChangeParser({
6912
- changes: [...staged, ...(unstaged ? unstaged : []), ...(untracked ? untracked : [])],
6913
- commit: 'HEAD',
6914
+ const unstagedChanges = await fileChangeParser({
6915
+ changes: unstaged || [],
6916
+ commit: '--unstaged',
6917
+ options: { tokenizer, git, llm, logger },
6918
+ });
6919
+ const unstagedResponse = `Unstaged changes:\n${unstagedChanges}`;
6920
+ const untrackedChanges = await fileChangeParser({
6921
+ changes: untracked || [],
6922
+ commit: '--untracked',
6923
+ options: { tokenizer, git, llm, logger },
6924
+ });
6925
+ const untrackedResponse = `Untracked changes:\n${untrackedChanges}`;
6926
+ const stagedChanges = await fileChangeParser({
6927
+ changes: staged,
6928
+ commit: '--staged',
6914
6929
  options: { tokenizer, git, llm, logger },
6915
6930
  });
6916
- logger.log(`Parsed Result: ${result}`);
6917
- return [result];
6931
+ const stagedResponse = `Staged changes:\n${stagedChanges}`;
6932
+ return [unstagedResponse, untrackedResponse, stagedResponse];
6918
6933
  case 'yesterday':
6919
6934
  const yesterday = new Date();
6920
6935
  yesterday.setDate(yesterday.getDate() - 1);
@@ -6936,7 +6951,6 @@ const handler = async (argv, logger) => {
6936
6951
  }
6937
6952
  }
6938
6953
  async function parser(changes) {
6939
- console.log({ changes });
6940
6954
  return changes.join('\n');
6941
6955
  }
6942
6956
  const recap = await generateAndReviewLoop({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-coco",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "zero-effort git commits with coco.",
5
5
  "author": "gfargo <ghfargo@gmail.com>",
6
6
  "license": "MIT",