threadlines 0.2.20 → 0.2.22

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.
@@ -246,7 +246,7 @@ async function checkCommand(options) {
246
246
  console.log('');
247
247
  process.exit(0);
248
248
  }
249
- console.log(chalk_1.default.green(`✓ Found ${gitDiff.changedFiles.length} changed file(s)\n`));
249
+ console.log(chalk_1.default.green(`✓ Found ${gitDiff.changedFiles.length} changed file(s) (context: ${reviewContext})\n`));
250
250
  // Log the files being sent
251
251
  for (const file of gitDiff.changedFiles) {
252
252
  logger_1.logger.info(` → ${file}`);
package/dist/git/diff.js CHANGED
@@ -190,13 +190,20 @@ async function getPRDiff(repoRoot, targetBranch, logger) {
190
190
  /**
191
191
  * Get diff for a specific commit (or HEAD if no SHA provided).
192
192
  *
193
- * Fetches the parent commit on-demand to ensure git show can generate a proper diff.
193
+ * Uses plumbing commands consistently to work reliably in shallow clones.
194
194
  * This works regardless of CI checkout depth settings (depth=1 or depth=2).
195
195
  *
196
196
  * Strategy:
197
- * 1. Get parent SHA from commit metadata (git show --format=%P)
198
- * 2. Fetch parent commit if available (git fetch origin <parentSHA> --depth=1)
199
- * 3. Use git show to get diff (now parent is available for comparison)
197
+ * 1. Get parent SHA using plumbing command (git cat-file -p) to read raw commit object
198
+ * - Plumbing commands ignore .git/shallow boundaries and show actual parent SHA
199
+ * - Porcelain commands (git show) respect shallow boundaries and hide parents in shallow clones
200
+ * - This is critical for CI environments that use shallow clones (depth=1)
201
+ * 2. Parse first parent line (handles standard commits and merge commits)
202
+ * 3. Fetch parent commit (git fetch origin <parentSHA> --depth=1)
203
+ * 4. Use git diff <PARENT_SHA> HEAD to get diff (plumbing command, ignores shallow boundaries)
204
+ * - git show HEAD still respects .git/shallow even after fetching parent
205
+ * - git diff <PARENT_SHA> HEAD compares tree objects directly, ignoring shallow boundaries
206
+ * - This is the key fix: must use plumbing commands consistently, not mix with porcelain
200
207
  *
201
208
  * Used by:
202
209
  * - All CI environments for push/commit context (GitHub, GitLab, Bitbucket, Vercel)
@@ -210,13 +217,36 @@ async function getCommitDiff(repoRoot, sha = 'HEAD') {
210
217
  // Fetch parent commit on-demand to ensure git show can generate a proper diff
211
218
  // This works regardless of CI checkout depth settings (depth=1 or depth=2)
212
219
  // If parent is already available, fetch is fast/no-op; if not, we fetch it
213
- // Get parent SHA from commit metadata (works even if parent isn't fetched locally)
220
+ // Get parent SHA using plumbing command (git cat-file) instead of porcelain (git show)
221
+ // Plumbing commands ignore .git/shallow boundaries and show the actual parent SHA
222
+ // Porcelain commands (git show) respect shallow boundaries and hide parents in shallow clones
223
+ // This is critical for CI environments that use shallow clones (depth=1)
214
224
  let parentSha;
215
225
  try {
216
- parentSha = (0, child_process_1.execSync)(`git show ${sha} --format=%P --no-patch`, {
226
+ // Use git cat-file -p to read raw commit object (plumbing command)
227
+ // This ignores shallow boundaries and shows the actual parent SHA
228
+ const commitObject = (0, child_process_1.execSync)(`git cat-file -p ${sha}`, {
217
229
  encoding: 'utf-8',
218
230
  cwd: repoRoot
219
- }).trim();
231
+ });
232
+ // Parse commit object to find first parent line
233
+ // Standard commits have one parent; merge commits have multiple parents
234
+ // We use the first parent (standard for diffing against previous state of branch)
235
+ const lines = commitObject.split('\n');
236
+ const parentLine = lines.find(line => line.startsWith('parent '));
237
+ if (!parentLine) {
238
+ throw new Error(`Commit ${sha} has no parent (it might be the root commit of the repository)`);
239
+ }
240
+ // Extract SHA from "parent <sha>" line
241
+ // Format: "parent <40-char-sha>"
242
+ const parts = parentLine.split(' ');
243
+ if (parts.length < 2 || !parts[1]) {
244
+ throw new Error(`Malformed parent line in commit object: "${parentLine}"`);
245
+ }
246
+ parentSha = parts[1].trim();
247
+ if (!parentSha || parentSha.length !== 40) {
248
+ throw new Error(`Invalid parent SHA format: "${parentSha}" (expected 40 characters)`);
249
+ }
220
250
  }
221
251
  catch (error) {
222
252
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -224,33 +254,31 @@ async function getCommitDiff(repoRoot, sha = 'HEAD') {
224
254
  `This is required to generate a proper diff. ` +
225
255
  `Error: ${errorMessage}`);
226
256
  }
227
- // Fetch parent commit if we have a parent (root commits have no parent)
228
- if (parentSha && parentSha.length === 40) {
229
- try {
230
- // Fetch just this one commit (depth=1 is fine, we only need the parent)
231
- await git.fetch(['origin', parentSha, '--depth=1']);
232
- }
233
- catch (error) {
234
- const errorMessage = error instanceof Error ? error.message : String(error);
235
- throw new Error(`Failed to fetch parent commit ${parentSha} from origin. ` +
236
- `This is required to generate a proper diff in shallow clones. ` +
237
- `Ensure 'origin' remote is configured and accessible. ` +
238
- `Error: ${errorMessage}`);
239
- }
257
+ // Fetch parent commit (we've already validated parentSha is valid above)
258
+ // If we get here, parentSha is guaranteed to be a valid 40-character SHA
259
+ try {
260
+ // Fetch just this one commit (depth=1 is fine, we only need the parent)
261
+ await git.fetch(['origin', parentSha, '--depth=1']);
262
+ }
263
+ catch (error) {
264
+ const errorMessage = error instanceof Error ? error.message : String(error);
265
+ throw new Error(`Failed to fetch parent commit ${parentSha} from origin. ` +
266
+ `This is required to generate a proper diff in shallow clones. ` +
267
+ `Ensure 'origin' remote is configured and accessible. ` +
268
+ `Error: ${errorMessage}`);
240
269
  }
241
- // If no parent (root commit), git show will show the full commit content
242
- // This is expected behavior for root commits
243
- // Get diff using git show (now parent should be available)
270
+ // Get diff using plumbing command (git diff) instead of porcelain (git show)
271
+ // git show respects .git/shallow boundaries and will still treat HEAD as root commit
272
+ // git diff <PARENT_SHA> HEAD ignores shallow boundaries and compares tree objects directly
273
+ // This is critical for shallow clones - we must use plumbing commands consistently
244
274
  let diff;
245
275
  let changedFiles;
246
276
  try {
247
- diff = await git.show([sha, '--format=', '--no-color', '-U200']);
248
- // Get changed files using git show --name-only
249
- const commitFiles = await git.show([sha, '--name-only', '--format=', '--pretty=format:']);
250
- changedFiles = commitFiles
251
- .split('\n')
252
- .filter(line => line.trim().length > 0)
253
- .map(line => line.trim());
277
+ // Use git diff to compare parent against HEAD (plumbing command, ignores shallow boundaries)
278
+ diff = await git.diff([`${parentSha}..${sha}`, '-U200']);
279
+ // Get changed files using git diff --name-only
280
+ const diffSummary = await git.diffSummary([`${parentSha}..${sha}`]);
281
+ changedFiles = diffSummary.files.map(f => f.file);
254
282
  }
255
283
  catch (error) {
256
284
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadlines",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Threadlines CLI - AI-powered linter based on your natural language documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {