@icode-js/icode 3.0.3 → 3.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icode-js/icode",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "description": "Modern git workflow CLI with robust parent-repo and hook handling",
5
5
  "type": "module",
6
6
  "author": "C",
@@ -162,20 +162,52 @@ export class GitService {
162
162
  }
163
163
 
164
164
  async statusPorcelain() {
165
+ // 返回标准 porcelain 状态,供上层统一判断“是否存在未提交改动”。
165
166
  const result = await this.exec(['status', '--porcelain'])
166
167
  return result.stdout
167
168
  }
168
169
 
169
170
  async hasChanges() {
171
+ // 只要 porcelain 有输出,就说明暂存区、工作区或未跟踪文件存在改动。
170
172
  const output = await this.statusPorcelain()
171
173
  return cleanOutput(output).length > 0
172
174
  }
173
175
 
174
- async diffWorkingTree() {
175
- const result = await this.exec(['diff'])
176
+ // 列出未被忽略的未跟踪文件,供 AI 工作流补全新文件 diff。
177
+ async listUntrackedFiles() {
178
+ const result = await this.exec(['ls-files', '--others', '--exclude-standard'])
179
+ return result.stdout
180
+ .split('\n')
181
+ .map((item) => item.trim())
182
+ .filter(Boolean)
183
+ }
184
+
185
+ // 通过 no-index diff 生成“/dev/null -> 新文件”的补丁,覆盖未跟踪文件内容。
186
+ async buildUntrackedFileDiff(filePath) {
187
+ const result = await this.exec(['diff', '--no-index', '--binary', '--', '/dev/null', filePath], {
188
+ allowFailure: true
189
+ })
176
190
  return result.stdout
177
191
  }
178
192
 
193
+ async diffWorkingTree() {
194
+ // git diff 默认不包含 untracked 文件,这里显式补齐,避免新仓库被误判成“无改动”。
195
+ const trackedResult = await this.exec(['diff'])
196
+ const untrackedFiles = await this.listUntrackedFiles()
197
+ const untrackedDiffs = []
198
+
199
+ for (const filePath of untrackedFiles) {
200
+ const diff = await this.buildUntrackedFileDiff(filePath)
201
+ if (cleanOutput(diff)) {
202
+ untrackedDiffs.push(diff.trimEnd())
203
+ }
204
+ }
205
+
206
+ return [trackedResult.stdout.trimEnd(), ...untrackedDiffs]
207
+ .filter(Boolean)
208
+ .join('\n\n')
209
+ }
210
+
179
211
  async diffStaged() {
180
212
  const result = await this.exec(['diff', '--staged'])
181
213
  return result.stdout
@@ -33,6 +33,15 @@ function buildCommitMessage(parsed) {
33
33
  return `${header}\n\n${body}`
34
34
  }
35
35
 
36
+ // 将多个 diff 片段稳定拼接,避免空块污染 prompt。
37
+ function joinDiffSections(sections) {
38
+ return sections
39
+ .map((item) => (item || '').trim())
40
+ .filter(Boolean)
41
+ .join('\n\n')
42
+ }
43
+
44
+ // 基于“全部未提交改动”生成 AI commit,确保未暂存新文件也能被识别并提交。
36
45
  export async function runAiCommitWorkflow(options) {
37
46
  const context = await resolveGitContext({
38
47
  cwd: options.cwd,
@@ -44,13 +53,19 @@ export async function runAiCommitWorkflow(options) {
44
53
  logger.info(`仓库根目录: ${context.topLevelPath}`)
45
54
  }
46
55
 
47
- let diff = await git.diffStaged()
48
- let diffSource = 'staged'
49
-
50
- if (!diff.trim()) {
51
- diff = await git.diffWorkingTree()
52
- diffSource = 'working-tree'
53
- }
56
+ const stagedDiff = await git.diffStaged()
57
+ const workingDiff = await git.diffWorkingTree()
58
+ const hasStagedDiff = Boolean(stagedDiff.trim())
59
+ const hasWorkingDiff = Boolean(workingDiff.trim())
60
+ const diff = joinDiffSections([
61
+ hasStagedDiff ? `--- STAGED DIFF ---\n${stagedDiff}` : '',
62
+ hasWorkingDiff ? `--- WORKING TREE DIFF ---\n${workingDiff}` : ''
63
+ ])
64
+ const diffSource = hasStagedDiff && hasWorkingDiff
65
+ ? 'staged+working-tree'
66
+ : hasStagedDiff
67
+ ? 'staged'
68
+ : 'working-tree'
54
69
 
55
70
  if (!diff.trim()) {
56
71
  throw new IcodeError('没有可用于生成提交信息的代码改动。', {
@@ -105,8 +120,8 @@ export async function runAiCommitWorkflow(options) {
105
120
  }
106
121
  }
107
122
 
108
- if (diffSource === 'working-tree') {
109
- // 从 working-tree 生成信息时,提交前统一暂存,避免 commit 为空。
123
+ if (hasWorkingDiff) {
124
+ // 统一暂存工作区改动,确保未暂存修改和新文件与 AI 看到的 diff 保持一致。
110
125
  await git.stageAll()
111
126
  }
112
127