git-repo-comitter 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/dist/index.cjs +37 -20
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,12 +1,10 @@
1
1
  #!/usr/bin/env node
2
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("commander"),l=require("fs"),u=require("path"),d=require("os"),f=require("child_process"),p=require("openai");p=s(p,1);let m=require("readline/promises");var h={llm:{model:`deepseek-v4-flash`,temperature:.7,maxInputTokens:262144,maxOutputTokens:16384},apiKey:``,endpoint:`https://api.openai.com/v1`},g=(0,u.resolve)((0,d.homedir)(),`.grc`,`config.json`);function _(){if(!(0,l.existsSync)(g))return{};try{let e=(0,l.readFileSync)(g,`utf-8`),t=JSON.parse(e);return{...t,llm:t.llm?{...t.llm}:void 0}}catch(e){return console.warn(`警告:解析 ${g} 失败,已忽略用户配置。原因:${e instanceof Error?e.message:String(e)}`),{}}}function v(e){let t=(0,u.resolve)((0,d.homedir)(),`.grc`);(0,l.existsSync)(t)||(0,l.mkdirSync)(t,{recursive:!0}),(0,l.writeFileSync)(g,JSON.stringify(e,null,2),`utf-8`)}function y(){let e=_();return{...h,...e,llm:{...h.llm,...e.llm||{}},apiKey:e.apiKey||``,endpoint:e.endpoint||h.endpoint}}var b=class extends Error{constructor(e,t=1){super(e),this.exitCode=t,this.name=`CliError`}};function x(e,t={}){try{return(0,f.execFileSync)(`git`,e,{cwd:process.cwd(),encoding:`utf-8`})}catch(e){if(t.tolerateError)return``;throw e}}function S(){return x([`rev-parse`,`--is-inside-work-tree`],{tolerateError:!0}).trim()===`true`}function C(){x([`add`,`.`])}function w(e){x([`commit`,`-m`,e])}function T(){let e=x([`diff`,`--cached`],{tolerateError:!0}),t=x([`diff`],{tolerateError:!0}),n=[];return e.trim()&&n.push(`=== Staged Changes ===
3
- `+e),t.trim()&&n.push(`=== Unstaged Changes ===
4
- `+t),n.join(`
2
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("commander"),l=require("fs"),u=require("path"),d=require("os"),f=require("child_process"),p=require("openai");p=s(p,1);let m=require("readline/promises");var h={llm:{model:`deepseek-v4-flash`,temperature:.7,maxInputTokens:262144,maxOutputTokens:16384},apiKey:``,endpoint:`https://api.openai.com/v1`},g=(0,u.resolve)((0,d.homedir)(),`.grc`,`config.json`);function _(){if(!(0,l.existsSync)(g))return{};try{let e=(0,l.readFileSync)(g,`utf-8`),t=JSON.parse(e);return{...t,llm:t.llm?{...t.llm}:void 0}}catch(e){return console.warn(`警告:解析 ${g} 失败,已忽略用户配置。原因:${e instanceof Error?e.message:String(e)}`),{}}}function v(e){let t=(0,u.resolve)((0,d.homedir)(),`.grc`);(0,l.existsSync)(t)||(0,l.mkdirSync)(t,{recursive:!0}),(0,l.writeFileSync)(g,JSON.stringify(e,null,2),`utf-8`)}function y(){let e=_();return{...h,...e,llm:{...h.llm,...e.llm||{}},apiKey:e.apiKey||``,endpoint:e.endpoint||h.endpoint}}var b=class extends Error{constructor(e,t=1){super(e),this.exitCode=t,this.name=`CliError`}};function x(e,t={}){try{return(0,f.execFileSync)(`git`,e,{cwd:process.cwd(),encoding:`utf-8`})}catch(e){if(t.tolerateError)return``;throw e}}function ee(){return x([`rev-parse`,`--is-inside-work-tree`],{tolerateError:!0}).trim()===`true`}function S(){x([`add`,`.`])}function C(e){x([`commit`,`-m`,e])}function w(e=!1){let t=T();return(e?t:[...t,...E().filter(e=>!t.includes(e))]).map(e=>({path:e,content:x([`show`,`:${e}`],{tolerateError:!0})}))}function T(){let e=x([`diff`,`--cached`,`--name-status`,`--diff-filter=A`],{tolerateError:!0});return e.trim()?e.split(`
3
+ `).filter(e=>e.startsWith(`A `)).map(e=>e.slice(2).trim()).filter(Boolean):[]}function E(){let e=x([`diff`,`--name-status`,`--diff-filter=A`],{tolerateError:!0});return e.trim()?e.split(`
4
+ `).filter(e=>e.startsWith(`A `)).map(e=>e.slice(2).trim()).filter(Boolean):[]}function D(){return x([`diff`,`--cached`,`--name-only`],{tolerateError:!0}).trim().length>0}var O=[`feat`,`fix`,`docs`,`style`,`refactor`,`perf`,`test`,`build`,`ci`,`chore`,`revert`];function te(e){let t=e.indexOf(`
5
5
 
6
- `)}function E(){return x([`diff`,`--cached`,`--name-only`],{tolerateError:!0}).trim().length>0}var D=[`feat`,`fix`,`docs`,`style`,`refactor`,`perf`,`test`,`build`,`ci`,`chore`,`revert`];function O(e){let t=e.indexOf(`
7
-
8
- `),n=t===-1?e.trim():e.slice(0,t).trim(),r=t===-1?``:e.slice(t+2).trim(),i=n.match(/^(?<type>[a-zA-Z]+)(?:\((?<scope>[^)]*)\))?(?<breaking>!)?:\s*(?<description>.+)$/);if(!i)return{valid:!1,reason:`标题不符合 Conventional Commits 格式:type(optional scope): description`};let{type:a}=i.groups;if(!D.includes(a))return{valid:!1,reason:`type 字段的值 "${a}" 不在允许的列表中 (${D.join(`, `)})`};if(n.length>72)return{valid:!1,reason:`标题行超过 72 字符限制 (当前 ${n.length} 字符)`};if(r){if(t===-1)return{valid:!1,reason:`正文前需空一行`};let e=r.split(`
9
- `).filter(e=>e.length>72);if(e.length>0)return{valid:!1,reason:`正文行超出 72 字符限制: ${e.join(`, `)}`}}return{valid:!0}}function k(e){let t=(typeof e==`string`?JSON.parse(e):e).choices?.[0]?.message?.content;return typeof t==`string`?t.trim():Array.isArray(t)?t.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim():``}var A=`你是一位擅长编写 Git 提交信息的专家。根据提供的 Git diff,生成简洁且描述准确的提交信息。
6
+ `),n=t===-1?e.trim():e.slice(0,t).trim(),r=t===-1?``:e.slice(t+2).trim(),i=n.match(/^(?<type>[a-zA-Z]+)(?:\((?<scope>[^)]*)\))?(?<breaking>!)?:\s*(?<description>.+)$/);if(!i)return{valid:!1,reason:`标题不符合 Conventional Commits 格式:type(optional scope): description`};let{type:a}=i.groups;if(!O.includes(a))return{valid:!1,reason:`type 字段的值 "${a}" 不在允许的列表中 (${O.join(`, `)})`};if(n.length>72)return{valid:!1,reason:`标题行超过 72 字符限制 (当前 ${n.length} 字符)`};if(r){if(t===-1)return{valid:!1,reason:`正文前需空一行`};let e=r.split(`
7
+ `).filter(e=>e.length>72);if(e.length>0)return{valid:!1,reason:`正文行超出 72 字符限制: ${e.join(`, `)}`}}return{valid:!0}}var k=`你是一位擅长编写 Git 提交信息的专家。根据提供的 Git diff,生成简洁且描述准确的提交信息。
10
8
 
11
9
  格式规范:
12
10
  - 遵循 Conventional Commits:<type>(<scope>): <description>
@@ -36,11 +34,7 @@ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=
36
34
  - 迁移 session 和 permission 模块至新接口
37
35
  - 移除已废弃的 getCacheKey 方法
38
36
 
39
- 只回复提交信息本身,不要添加任何解释。`;function j(e){if(!e.apiKey)throw Error("apiKey 未设置,请运行 `grc init` 进行配置。");return new p.default({apiKey:e.apiKey,baseURL:e.endpoint})}async function M(e,t){let n=j(t),r=[{role:`system`,content:A},{role:`user`,content:`以下是 Git diff 内容:\n\n${e}`}];for(let e=1;e<=3;e++){let i=await n.chat.completions.create({model:t.llm.model,temperature:t.llm.temperature,max_tokens:t.llm.maxOutputTokens,messages:r}),a=k(i);if(!a)throw Error(`LLM 返回了空的提交信息。响应内容:${JSON.stringify(i)}`);let o=await O(a);if(o.valid)return a;if(e<3)console.log(` 提交信息格式校验未通过(第 ${e} 次): ${o.reason}`),console.log(` 正在重新生成...
40
- `),r.push({role:`assistant`,content:a},{role:`user`,content:`生成的提交信息格式不符合规范:${o.reason}。请严格按照 Conventional Commits 格式重新生成。`});else throw Error(`LLM 提交信息格式校验失败(已重试 3 次): ${o.reason}\n最后一次生成的提交信息:\n${a}`)}throw Error(`意外的错误:重试循环结束后仍未能生成有效提交信息`)}var N=4;function P(e){return Math.ceil(e.length/N)}function F(e){let t=e.split(`
41
- `),n=[],r=``,i=[],a=``,o=()=>{if(i.length===0)return;let e=i.join(`
42
- `);n.push({filePath:a,content:e,estimatedTokens:P(e)}),i=[]};for(let e of t){let t=e.match(/^diff --git a\/(.+?) b\//),n=e.match(/^=== .+ ===$/);t?(o(),a=t[1],r&&=(i.push(r),``),i.push(e)):n?r=e:(r&&i.length===0&&(i.push(r),r=``),i.push(e))}return o(),n}function I(e,t){if(e.length===0)return[];let n=[],r=[],i=0,a=()=>{if(r.length===0)return;let e=r.map(e=>e.content).join(`
43
- `);n.push({blocks:r,content:e,estimatedTokens:i}),r=[],i=0};for(let n of e)i+n.estimatedTokens<=t?(r.push(n),i+=n.estimatedTokens):(a(),r.push(n),i=n.estimatedTokens);return a(),n}var L=200,R=`你是一位擅长编写 Git 提交信息的专家。你将收到一个大型 Git diff 的**一部分**(包含部分文件的变更)。
37
+ 只回复提交信息本身,不要添加任何解释。`,A=`你是一位擅长编写 Git 提交信息的专家。你将收到一个大型 Git diff 的**一部分**(包含部分文件的变更)。
44
38
 
45
39
  请仅根据这部分 diff 生成一个**局部的**提交信息草稿,格式规范同 Conventional Commits,但允许 scope 或 type 不够精确——后续会合并所有部分的结果。
46
40
 
@@ -50,7 +44,7 @@ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=
50
44
  - 标题行控制在 72 字符以内
51
45
  - 正文使用无序列表分条陈述,每条以 "- " 开头
52
46
  - 描述语言使用中文,避免使用英文
53
- - 只回复提交信息本身,不要添加任何解释`,z=`你是一位擅长编写 Git 提交信息的专家。你将收到多个**局部的**提交信息草稿,每个草稿描述了部分文件的变更。
47
+ - 只回复提交信息本身,不要添加任何解释`,j=`你是一位擅长编写 Git 提交信息的专家。你将收到多个**局部的**提交信息草稿,每个草稿描述了部分文件的变更。
54
48
 
55
49
  请将它们合并为一个**完整的、连贯的**提交信息,遵循 Conventional Commits 格式:
56
50
  - 选择最能概括所有变更的 type 和 scope
@@ -58,12 +52,35 @@ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=
58
52
  - 正文合并所有草稿的要点,去重,保持逻辑分组,使用无序列表
59
53
  - 如变更较简单,可省略正文
60
54
  - 描述语言使用中文,避免使用英文
61
- - 只回复最终的提交信息本身,不要添加任何解释`;function B(e,t){return e.llm.maxInputTokens-P(t)-L-e.llm.maxOutputTokens}async function V(e,t){let n=k(await j(t).chat.completions.create({model:t.llm.model,temperature:t.llm.temperature,max_tokens:t.llm.maxOutputTokens,messages:[{role:`system`,content:R},{role:`user`,content:`以下是 Git diff 的一部分:\n\n${e}`}]}));if(!n)throw Error(`LLM 在处理分批 diff 时返回了空内容。`);return n}async function H(e,t){let n=j(t),r=B(t,z),i=[],a=0,o=0;for(let t=0;t<e.length;t++){let n=`--- 部分 ${t+1} ---\n${e[t]}`,s=P(n);if(a+s<=r)i.push(n),a+=s;else{o=e.length-t;break}}let s=i.join(`
55
+ - 只回复最终的提交信息本身,不要添加任何解释`,M=`你是一个 Git 提交审查助手。你的任务是检查 Git 新增的文件,判断这些文件是否应该被提交。
56
+
57
+ 请检查以下新增的文件列表和内容,判断是否存在不当提交的情况:
58
+ 1. 编译产物或生成文件(如 node_modules/, dist/, build/ 等)
59
+ 2. 依赖锁定文件(如 package-lock.json, yarn.lock 等)
60
+ 3. 临时文件、日志文件、系统文件
61
+ 4. 涉及敏感信息(密码、密钥、Token、内网地址等)的文件
62
+ 5. 其他不应提交到 Git 仓库的文件
63
+
64
+ 请只回复 JSON 格式,不要添加任何其他解释:
65
+ {
66
+ "shouldCommit": boolean, // 是否应该提交这些文件
67
+ "suspiciousFiles": [ // 可疑的文件列表(路径)
68
+ "path/to/file"
69
+ ],
70
+ "reason": "string" // 判断理由
71
+ }`;function N(e){let t=(typeof e==`string`?JSON.parse(e):e).choices?.[0]?.message?.content;return typeof t==`string`?t.trim():Array.isArray(t)?t.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim():``}function P(e){if(!e.apiKey)throw Error("apiKey 未设置,请运行 `grc init` 进行配置。");return new p.default({apiKey:e.apiKey,baseURL:e.endpoint})}async function F(e,t,n){let r=n?.label??`提交信息`,i=n?.initialMessage??null;for(let n=1;n<=3;n++){if(i===null&&(i=N(await P(e).chat.completions.create({model:e.llm.model,temperature:e.llm.temperature,max_tokens:e.llm.maxOutputTokens,messages:t})),!i))throw Error(`LLM 在生成${r}时返回了空内容。`);t.push({role:`assistant`,content:i});let a=await te(i);if(a.valid)return i;if(n<3)console.log(` ${r}格式校验未通过(第 ${n} 次): ${a.reason}`),console.log(` 正在重新生成...
72
+ `),t.push({role:`user`,content:`生成的提交信息格式不符合规范:${a.reason}。请严格按照 Conventional Commits 格式重新生成。`}),i=null;else throw Error(`${r}格式校验失败(已重试 3 次): ${a.reason}\n最后一次生成的提交信息:\n${i}`)}throw Error(`意外的错误:重试循环结束后仍未能生成有效提交信息`)}async function I(e,t){return F(t,[{role:`system`,content:k},{role:`user`,content:`以下是 Git diff 内容:\n\n${e}`}])}var L=4;function R(e){return Math.ceil(e.length/L)}function z(e){let t=e.split(`
73
+ `),n=[],r=``,i=[],a=``,o=()=>{if(i.length===0)return;let e=i.join(`
74
+ `);n.push({filePath:a,content:e,estimatedTokens:R(e)}),i=[]};for(let e of t){let t=e.match(/^diff --git a\/(.+?) b\//),n=e.match(/^=== .+ ===$/);t?(o(),a=t[1],r&&=(i.push(r),``),i.push(e)):n?r=e:(r&&i.length===0&&(i.push(r),r=``),i.push(e))}return o(),n}function B(e,t){if(e.length===0)return[];let n=[],r=[],i=0,a=()=>{if(r.length===0)return;let e=r.map(e=>e.content).join(`
75
+ `);n.push({blocks:r,content:e,estimatedTokens:i}),r=[],i=0};for(let n of e)i+n.estimatedTokens<=t?(r.push(n),i+=n.estimatedTokens):(a(),r.push(n),i=n.estimatedTokens);return a(),n}var V=200;function H(e,t){return e.llm.maxInputTokens-R(t)-V-e.llm.maxOutputTokens}async function U(e,t){let n=N(await P(t).chat.completions.create({model:t.llm.model,temperature:t.llm.temperature,max_tokens:t.llm.maxOutputTokens,messages:[{role:`system`,content:A},{role:`user`,content:`以下是 Git diff 的一部分:\n\n${e}`}]}));if(!n)throw Error(`LLM 在处理分批 diff 时返回了空内容。`);return n}async function W(e,t){let n=P(t),r=H(t,j),i=[],a=0,o=0;for(let t=0;t<e.length;t++){let n=`--- 部分 ${t+1} ---\n${e[t]}`,s=R(n);if(a+s<=r)i.push(n),a+=s;else{o=e.length-t;break}}let s=i.join(`
76
+
77
+ `)+(o>0?`\n\n[... 前面 ${o} 个批次已省略 ...]`:``),c=N(await n.chat.completions.create({model:t.llm.model,temperature:t.llm.temperature,max_tokens:t.llm.maxOutputTokens,messages:[{role:`system`,content:j},{role:`user`,content:s}]}));if(!c)throw Error(`LLM 在合并提交信息时返回了空内容。`);return c}async function G(e,t){let n=H(t,k),r=R(e);if(r<=n)return{message:await I(e,t),batchCount:1};let i=B(z(e),n);console.log(` 变更内容较大(约 ${r} tokens),将分为 ${i.length} 批次处理...`);let a=[];for(let e=0;e<i.length;e++){console.log(` 正在处理第 ${e+1}/${i.length} 批次...`);let n=await U(i[e].content,t);a.push(n)}console.log(` 正在合并 ${i.length} 个批次的提交信息...`);let o=await W(a,t);return{message:await F(t,[{role:`system`,content:j},{role:`user`,content:a.map((e,t)=>`--- 部分 ${t+1} ---\n${e}`).join(`
78
+
79
+ `)}],{initialMessage:o,label:`合并信息`}),batchCount:i.length}}async function K(e,t){let n=P(t),r=e.map(e=>`路径: ${e.path}\n内容:\n${e.content}`).join(`
62
80
 
63
- `)+(o>0?`\n\n[... 前面 ${o} 个批次已省略 ...]`:``),c=k(await n.chat.completions.create({model:t.llm.model,temperature:t.llm.temperature,max_tokens:t.llm.maxOutputTokens,messages:[{role:`system`,content:z},{role:`user`,content:s}]}));if(!c)throw Error(`LLM 在合并提交信息时返回了空内容。`);return c}async function U(e,t,n){let r=[{role:`system`,content:z},{role:`user`,content:t.map((e,t)=>`--- 部分 ${t+1} ---\n${e}`).join(`
81
+ ---
64
82
 
65
- `)},{role:`assistant`,content:e}],i=e;for(let e=1;e<=3;e++){let t=await O(i);if(t.valid)return i;if(e<3){if(console.log(` 合并信息格式校验未通过(第 ${e} 次): ${t.reason}`),console.log(` 正在重新合并...
66
- `),r.push({role:`user`,content:`生成的提交信息格式不符合规范:${t.reason}。请严格按照 Conventional Commits 格式重新生成。`}),i=k(await j(n).chat.completions.create({model:n.llm.model,temperature:n.llm.temperature,max_tokens:n.llm.maxOutputTokens,messages:r})),!i)throw Error(`LLM 在重试合并时返回了空内容。`);r.push({role:`assistant`,content:i})}else throw Error(`合并信息格式校验失败(已重试 3 次): ${t.reason}\n最后一次生成的提交信息:\n${i}`)}throw Error(`意外的错误:重试循环结束后仍未能生成有效提交信息`)}async function W(e,t){let n=B(t,A),r=P(e);if(r<=n)return{message:await M(e,t),batchCount:1};let i=I(F(e),n);console.log(` 变更内容较大(约 ${r} tokens),将分为 ${i.length} 批次处理...`);let a=[];for(let e=0;e<i.length;e++){console.log(` 正在处理第 ${e+1}/${i.length} 批次...`);let n=await V(i[e].content,t);a.push(n)}return console.log(` 正在合并 ${i.length} 个批次的提交信息...`),{message:await U(await H(a,t),a,t),batchCount:i.length}}function G(){let e=y();if(!e.apiKey)throw new b("API Key 未设置,请运行 `grc init` 进行配置。");return e}function K(e){if(e){if(!E())throw new b(`没有已暂存的变更,请先使用 git add 暂存文件。`);console.log(`仅提交暂存变更...`);return}E()?console.log(`检出已有暂存变更,直接提交...`):(console.log(`暂存所有变更...`),C())}function q(){let e=T();return e.trim()?e:null}async function J(e={}){let t=G();if(!S())throw new b(`当前目录不是 git 仓库,请确保在 git 仓库中执行 grc 命令`);K(!!e.stagedOnly);let n=q();if(!n){console.log(`没有可提交的变更。`);return}console.log(`正在生成提交信息...
67
- `);let{message:r,batchCount:i}=await W(n,t);i>1&&console.log(` (已将 diff 分为 ${i} 批次处理并合并)\n`),w(r),console.log(`提交信息:\n ${r}\n`),console.log(`提交成功!`)}function Y(e){let t=(0,m.createInterface)({input:process.stdin,output:process.stdout});return t.question(e).finally(()=>t.close())}async function X(){console.log(`LLM 配置初始化
68
- `);let e=_(),t=(await Y(`大模型链接(API 地址)[${e.endpoint||`https://api.openai.com/v1`}]: `)).trim()||e.endpoint||`https://api.openai.com/v1`,n=(await Y(`模型名称 [${e.llm?.model||`deepseek-v4-flash`}]: `)).trim()||e.llm?.model||`deepseek-v4-flash`,r=(await Y(`API Key [${e.apiKey?`***`:`(必填)`}]: `)).trim()||e.apiKey;if(!r)throw new b(`API Key 不能为空。`);v({apiKey:r,endpoint:t,llm:{model:n,temperature:.7,maxInputTokens:262144,maxOutputTokens:16384}}),console.log(`
69
- 配置已保存到 ~/.grc/config.json`)}function Z(){let e=(0,u.resolve)(__dirname,`..`,`package.json`);return JSON.parse((0,l.readFileSync)(e,`utf-8`)).version}function Q(e){e instanceof b&&(console.error(`错误:`,e.message),process.exit(e.exitCode)),console.error(`意外错误:`,e instanceof Error?e.message:String(e)),process.exit(1)}var $=new c.Command;$.name(`grc`).version(Z(),`-v, --version`,`显示当前版本`).description(`一款使用 LLM 生成 Git 提交信息并执行提交的命令行工具`).helpOption(`-h, --help`,`显示帮助信息`).option(`-s, --staged-only`,`仅提交已暂存的变更`),$.command(`init`).description(`交互式 LLM 配置初始化`).action(()=>{X().catch(Q)}),$.action(e=>{J({stagedOnly:e.stagedOnly??!1}).catch(Q)});function ee(){$.parse(process.argv)}ee();
83
+ `),i=N(await n.chat.completions.create({model:t.llm.model,temperature:0,max_tokens:t.llm.maxOutputTokens,messages:[{role:`system`,content:M},{role:`user`,content:`请审查以下新增文件:\n\n${r}`}]}));if(!i)throw Error(`LLM 在审查新增文件时返回了空内容。`);try{return JSON.parse(i)}catch{throw Error(`LLM 返回的审查结果不是有效的 JSON:\n${i}`)}}function q(e){let t=(0,m.createInterface)({input:process.stdin,output:process.stdout});return t.question(e).finally(()=>t.close())}function J(){let e=y();if(!e.apiKey)throw new b("API Key 未设置,请运行 `grc init` 进行配置。");return e}function Y(e){if(e){if(!D())throw new b(`没有已暂存的变更,请先使用 git add 暂存文件。`);console.log(`仅提交暂存变更...`);return}console.log(`暂存所有变更...`),S()}function X(){return x([`diff`,`--cached`],{tolerateError:!0}).trim()||null}async function Z(e,t){let n=w(t);if(n.length===0)return;let r=await K(n,e);if(!r.shouldCommit){console.log(`LLM 审查发现以下文件疑似不需要提交:`);for(let e of r.suspiciousFiles)console.log(` - ${e}`);console.log(`原因:${r.reason}`);let e=await q(`是否排除这些文件并继续提交?(y/N): `);if(e.toLowerCase()===`y`||e.toLowerCase()===`yes`){let e=T(),t=r.suspiciousFiles.filter(t=>e.includes(t));if(t.length>0&&x([`reset`,`--`,...t]),!D())throw new b(`排除所有可疑文件后没有可提交的变更。`);console.log(`已排除 ${r.suspiciousFiles.length} 个文件,继续提交...`)}else throw new b(`用户取消提交。`)}}async function ne(e={}){let t=J();if(!ee())throw new b(`当前目录不是 git 仓库,请确保在 git 仓库中执行 grc 命令`);Y(!!e.stagedOnly),await Z(t,!!e.stagedOnly);let n=X();if(!n){console.log(`没有可提交的变更。`);return}console.log(`正在生成提交信息...
84
+ `);let{message:r,batchCount:i}=await G(n,t);i>1&&console.log(` (已将 diff 分为 ${i} 批次处理并合并)\n`),C(r),console.log(`提交信息:\n ${r}\n`),console.log(`提交成功!`)}async function re(){console.log(`LLM 配置初始化
85
+ `);let e=_(),t=(await q(`大模型链接(API 地址)[${e.endpoint||`https://api.openai.com/v1`}]: `)).trim()||e.endpoint||`https://api.openai.com/v1`,n=(await q(`模型名称 [${e.llm?.model||`deepseek-v4-flash`}]: `)).trim()||e.llm?.model||`deepseek-v4-flash`,r=(await q(`API Key [${e.apiKey?`***`:`(必填)`}]: `)).trim()||e.apiKey;if(!r)throw new b(`API Key 不能为空。`);v({apiKey:r,endpoint:t,llm:{model:n,temperature:.7,maxInputTokens:262144,maxOutputTokens:16384}}),console.log(`
86
+ 配置已保存到 ~/.grc/config.json`)}function ie(){let e=(0,u.resolve)(__dirname,`..`,`package.json`);return JSON.parse((0,l.readFileSync)(e,`utf-8`)).version}function Q(e){e instanceof b&&(console.error(`错误:`,e.message),process.exit(e.exitCode)),console.error(`意外错误:`,e instanceof Error?e.message:String(e)),process.exit(1)}var $=new c.Command;$.name(`grc`).version(ie(),`-v, --version`,`显示当前版本`).description(`一款使用 LLM 生成 Git 提交信息并执行提交的命令行工具`).helpOption(`-h, --help`,`显示帮助信息`).option(`-s, --staged-only`,`仅提交已暂存的变更`),$.command(`init`).description(`交互式 LLM 配置初始化`).action(()=>{re().catch(Q)}),$.action(e=>{ne({stagedOnly:e.stagedOnly??!1}).catch(Q)});function ae(){$.parse(process.argv)}ae();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-repo-comitter",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A CLI tool that uses LLM to generate Git commit messages and execute commits",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",